- {loading ? (
-
- {prayerTimeBoxes.map((box, index) => (
-
- ))}
-
- ) : error ? (
-
-
-
+
+ {!user?.city?.id ? (
+
+
🕋
+
+ برای نمایش اوقات شرعی، لطفاً ابتدا در تنظیمات ویجت، شهر خود را
+ تنظیم کنید.
+
+
-
- مشکلی در دریافت اطلاعات وجود دارد
-
-
- ) : (
- <>
-
+ ) : loading ? (
+
{prayerTimeBoxes.map((box, index) => (
))}
-
{' '}
- {loading ? (
-
- ) : (
- dailyZikr && (
-
- )
- )}
- >
- )}
+
+ ) : error ? (
+
+
+
+
+
+ مشکلی در دریافت اطلاعات وجود دارد
+
+
+ ) : (
+ <>
+
+ {prayerTimeBoxes.map((box, index) => (
+
+ ))}
+
{' '}
+ {loading ? (
+
+ ) : (
+ dailyZikr && (
+
+ )
+ )}
+ >
+ )}
+
)
}
diff --git a/src/layouts/widgets/weather/current/current-box.weather.tsx b/src/layouts/widgets/weather/current/current-box.weather.tsx
index f68643b5..ca953984 100644
--- a/src/layouts/widgets/weather/current/current-box.weather.tsx
+++ b/src/layouts/widgets/weather/current/current-box.weather.tsx
@@ -5,28 +5,26 @@ import { TbWind } from 'react-icons/tb'
import { WiCloudy, WiHumidity } from 'react-icons/wi'
interface CurrentWeatherBoxProps {
- fetchedWeather: FetchedWeather['weather'] | null
+ fetchedWeather: FetchedWeather | null
enabledShowName: boolean
temperatureUnit: keyof typeof unitsFlag
- selectedCityName: string
}
export function CurrentWeatherBox({
fetchedWeather,
enabledShowName,
- selectedCityName,
temperatureUnit,
}: CurrentWeatherBoxProps) {
return (
<>
- {fetchedWeather?.statusBanner && (
+ {fetchedWeather?.weather?.statusBanner && (
)}
- {!fetchedWeather?.statusBanner && (
+ {!fetchedWeather?.weather?.statusBanner && (
)}
- {enabledShowName ? cleanCityName(selectedCityName) : '🏠'}
+ {enabledShowName
+ ? cleanCityName(fetchedWeather?.city?.fa || '')
+ : '🏠'}
- {Math.round(fetchedWeather?.temperature?.temp || 0)}
+ {Math.round(fetchedWeather?.weather?.temperature?.temp || 0)}
{unitsFlag[temperatureUnit || 'metric']}
- {fetchedWeather?.description?.text} •{' '}
- {fetchedWeather?.temperature?.temp_description}
+ {fetchedWeather?.weather?.description?.text} •{' '}
+ {fetchedWeather?.weather?.temperature?.temp_description}
-

+ {fetchedWeather?.weather?.icon?.url ? (
+

+ ) : (
+
+ )}
@@ -72,7 +76,9 @@ export function CurrentWeatherBox({
- {Math.round(fetchedWeather?.temperature?.wind_speed || 0)}{' '}
+ {Math.round(
+ fetchedWeather?.weather?.temperature?.wind_speed || 0
+ )}{' '}
m/s
@@ -82,7 +88,7 @@ export function CurrentWeatherBox({
- {fetchedWeather?.temperature?.humidity || 0}%
+ {fetchedWeather?.weather?.temperature?.humidity || 0}%
@@ -91,7 +97,7 @@ export function CurrentWeatherBox({
- {fetchedWeather?.temperature?.clouds || 0}%
+ {fetchedWeather?.weather?.temperature?.clouds || 0}%
diff --git a/src/layouts/widgets/weather/weather-setting.tsx b/src/layouts/widgets/weather/weather-setting.tsx
index a61b3dfb..43bb8851 100644
--- a/src/layouts/widgets/weather/weather-setting.tsx
+++ b/src/layouts/widgets/weather/weather-setting.tsx
@@ -1,3 +1,4 @@
+import { useRef, useState, useEffect } from 'react'
import { getFromStorage, setToStorage } from '@/common/storage'
import { callEvent } from '@/common/utils/call-event'
import { SectionPanel } from '@/components/section-panel'
diff --git a/src/layouts/widgets/weather/weather.layout.tsx b/src/layouts/widgets/weather/weather.layout.tsx
index 766f3a94..d866a6de 100644
--- a/src/layouts/widgets/weather/weather.layout.tsx
+++ b/src/layouts/widgets/weather/weather.layout.tsx
@@ -1,41 +1,48 @@
import { useEffect, useState } from 'react'
import { getFromStorage, setToStorage } from '@/common/storage'
-import { listenEvent } from '@/common/utils/call-event'
-import { useGeneralSetting } from '@/context/general-setting.context'
+import { callEvent, listenEvent } from '@/common/utils/call-event'
import type {
FetchedForecast,
FetchedWeather,
WeatherSettings,
} from '@/layouts/widgets/weather/weather.interface'
-import { useGetForecastWeatherByLatLon } from '@/services/hooks/weather/getForecastWeatherByLatLon'
-import { useGetWeatherByLatLon } from '@/services/hooks/weather/getWeatherByLatLon'
import { WidgetContainer } from '../widget-container'
import { Forecast } from './forecast/forecast'
import { CurrentWeatherBox } from './current/current-box.weather'
+import { useAuth } from '@/context/auth.context'
+import { RequireAuth } from '@/components/auth/require-auth'
+import { useGetWeatherByLatLon } from '@/services/hooks/weather/getWeatherByLatLon'
+import { useGetForecastWeatherByLatLon } from '@/services/hooks/weather/getForecastWeatherByLatLon'
+import { Button } from '@/components/button/button'
+import { WidgetTabKeys } from '@/layouts/widgets-settings/constant/tab-keys'
+import Analytics from '@/analytics'
export function WeatherLayout() {
- const { selectedCity } = useGeneralSetting()
+ const { isAuthenticated, user } = useAuth()
const [weatherSettings, setWeatherSettings] = useState
(null)
const [weatherState, setWeather] = useState(null)
const [forecastWeather, setForecastWeather] = useState(null)
- const { data, dataUpdatedAt } = useGetWeatherByLatLon(
- selectedCity.lat,
- selectedCity.lon,
- {
- refetchInterval: 0,
- units: weatherSettings?.temperatureUnit,
- useAI: weatherSettings?.useAI,
- enabled: !!weatherSettings,
- }
- )
+ const {
+ data,
+ dataUpdatedAt,
+ refetch: refetchWeather,
+ } = useGetWeatherByLatLon({
+ refetchInterval: 0,
+ units: weatherSettings?.temperatureUnit,
+ useAI: weatherSettings?.useAI,
+ enabled: isAuthenticated && user?.city?.id != null,
+ })
- const { data: forecastData, dataUpdatedAt: forecastDataUpdatedAt } =
- useGetForecastWeatherByLatLon(selectedCity.lat, selectedCity.lon, {
- count: 6,
- units: weatherSettings?.temperatureUnit,
- enabled: !!weatherSettings,
- refetchInterval: 0,
- })
+ const {
+ data: forecastData,
+ dataUpdatedAt: forecastDataUpdatedAt,
+ refetch: refetchForecast,
+ } = useGetForecastWeatherByLatLon({
+ count: 6,
+ units: weatherSettings?.temperatureUnit,
+ enabled: isAuthenticated && user?.city?.id != null,
+ refetchInterval: 0,
+ })
useEffect(() => {
async function load() {
@@ -93,25 +100,58 @@ export function WeatherLayout() {
}
}, [forecastDataUpdatedAt])
- if (selectedCity === null || !weatherSettings) return null
+ useEffect(() => {
+ if (isAuthenticated && user?.city?.id) {
+ refetchWeather()
+ refetchForecast()
+ }
+ }, [user?.city?.id, isAuthenticated, refetchWeather, refetchForecast])
+
+ const onClickSetCity = () => {
+ callEvent('openWidgetsSettings', {
+ tab: WidgetTabKeys.weather_settings,
+ })
+ Analytics.event('weather_set_city_clicked')
+ }
+
+ if (!weatherSettings) return null
return (
-
-
+
+ {!user?.city?.id ? (
+
+
🌤️
+
+ برای نمایش آب و هوا، لطفاً ابتدا در تنظیمات ویجت، شهر خود را
+ تنظیم کنید.
+
+
+
+ ) : (
+
+
+
+
+
+ )}
+
)
}
diff --git a/src/services/hooks/cities/getCitiesList.ts b/src/services/hooks/cities/getCitiesList.ts
new file mode 100644
index 00000000..a9807531
--- /dev/null
+++ b/src/services/hooks/cities/getCitiesList.ts
@@ -0,0 +1,23 @@
+import { useQuery } from '@tanstack/react-query'
+import { getMainClient } from '@/services/api'
+
+export interface CityResponse {
+ city: string
+ cityId: string
+}
+
+async function fetchCitiesList(): Promise {
+ const client = await getMainClient()
+ const response = await client.get('/cities/list')
+ return response.data
+}
+
+export function useGetCitiesList(enabled: boolean) {
+ return useQuery({
+ queryKey: ['getCitiesList'],
+ queryFn: fetchCitiesList,
+ enabled,
+ staleTime: 5 * 60 * 1000, // 5 minutes
+ gcTime: 10 * 60 * 1000, // 10 minutes
+ })
+}
diff --git a/src/services/hooks/date/getReligiousTime.hook.ts b/src/services/hooks/date/getReligiousTime.hook.ts
index fc191f59..9db48fef 100644
--- a/src/services/hooks/date/getReligiousTime.hook.ts
+++ b/src/services/hooks/date/getReligiousTime.hook.ts
@@ -1,5 +1,5 @@
import { getMainClient } from '@/services/api'
-import { useEffect, useState } from 'react'
+import { useQuery } from '@tanstack/react-query'
export interface FetchedReligiousTimeData {
azan_sobh: string
@@ -9,55 +9,31 @@ export interface FetchedReligiousTimeData {
azan_maghreb: string
nimeshab: string
}
-const cachedData: Map = new Map()
-export const useReligiousTime = (
- day: number,
- month: number,
- lat: number,
- long: number
-) => {
- const [data, setData] = useState(null)
- const [loading, setLoading] = useState(true)
- const [error, setError] = useState(null)
+export interface FetchedReligiousTimeData {
+ azan_sobh: string
+ tolu_aftab: string
+ azan_zohr: string
+ ghorub_aftab: string
+ azan_maghreb: string
+ nimeshab: string
+}
- useEffect(() => {
- const fetchReligiousTime = async () => {
- try {
- const cacheKey = `${day}-${month}-${lat}-${long}`
- if (cachedData.has(cacheKey)) {
- setData(cachedData.get(cacheKey) as FetchedReligiousTimeData)
- return
+export const useReligiousTime = (day: number, month: number, enabled: boolean) => {
+ return useQuery({
+ queryKey: ['religiousTime', day, month],
+ queryFn: async () => {
+ const client = await getMainClient()
+ const { data: result } = await client.get(
+ '/date/owghat',
+ {
+ params: {
+ day,
+ month,
+ },
}
-
- setLoading(true)
- const client = await getMainClient()
-
- const { data: result } = await client.get(
- '/date/owghat',
- {
- params: {
- day,
- month,
- lat,
- long,
- },
- }
- )
-
- setData(result)
-
- cachedData.set(cacheKey, result)
- } catch (err) {
- setError(
- err instanceof Error ? err : new Error('An unknown error occurred')
- )
- } finally {
- setLoading(false)
- }
- }
-
- fetchReligiousTime()
- }, [day, month, lat, long])
-
- return { data, loading, error }
+ )
+ return result
+ },
+ enabled,
+ })
}
diff --git a/src/services/hooks/user/userService.hook.ts b/src/services/hooks/user/userService.hook.ts
index a2db030e..fafa15d1 100644
--- a/src/services/hooks/user/userService.hook.ts
+++ b/src/services/hooks/user/userService.hook.ts
@@ -30,6 +30,9 @@ interface FetchedProfile {
font: FontFamily
timeZone: string
coins: number
+ city?: {
+ id: string
+ }
}
export interface UserProfile extends FetchedProfile {
@@ -115,3 +118,19 @@ export function useSendVerificationEmail() {
mutationKey: ['sendVerificationEmail'],
})
}
+
+async function setCityToServer(cityId: string): Promise {
+ const client = await getMainClient()
+ await client.put('/users/@me/city', { cityId })
+}
+
+export function useSetCity() {
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ mutationFn: (cityId: string) => setCityToServer(cityId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['userProfile'] })
+ },
+ })
+}
diff --git a/src/services/hooks/weather/getForecastWeatherByLatLon.ts b/src/services/hooks/weather/getForecastWeatherByLatLon.ts
index f57c9218..374ba96e 100644
--- a/src/services/hooks/weather/getForecastWeatherByLatLon.ts
+++ b/src/services/hooks/weather/getForecastWeatherByLatLon.ts
@@ -6,8 +6,6 @@ import type {
} from '../../../layouts/widgets/weather/weather.interface'
async function fetchForecastWeatherByLatLon(
- lat: number,
- lon: number,
count?: number,
units?: TemperatureUnit
): Promise {
@@ -15,8 +13,6 @@ async function fetchForecastWeatherByLatLon(
const response = await client.get('/weather/forecast', {
params: {
- lat,
- lon,
...(count !== null && { count }),
...(units !== null && { units }),
},
@@ -24,20 +20,15 @@ async function fetchForecastWeatherByLatLon(
return response.data
}
-export function useGetForecastWeatherByLatLon(
- lat: number,
- lon: number,
- options: {
- refetchInterval: number | null
- count?: number
- units?: TemperatureUnit
- enabled: boolean
- }
-) {
+export function useGetForecastWeatherByLatLon(options: {
+ refetchInterval: number | null
+ count?: number
+ units?: TemperatureUnit
+ enabled: boolean
+}) {
return useQuery({
- queryKey: ['ForecastGetWeatherByLatLon', lat, lon],
- queryFn: () =>
- fetchForecastWeatherByLatLon(lat, lon, options.count, options.units),
+ queryKey: ['ForecastGetWeatherByLatLon'],
+ queryFn: () => fetchForecastWeatherByLatLon(options.count, options.units),
refetchInterval: options.refetchInterval || false,
enabled: options.enabled,
})
diff --git a/src/services/hooks/weather/getWeatherByLatLon.ts b/src/services/hooks/weather/getWeatherByLatLon.ts
index 0ff10135..482724f6 100644
--- a/src/services/hooks/weather/getWeatherByLatLon.ts
+++ b/src/services/hooks/weather/getWeatherByLatLon.ts
@@ -5,21 +5,13 @@ import type { FetchedWeather } from '../../../layouts/widgets/weather/weather.in
type units = 'standard' | 'metric' | 'imperial'
async function fetchWeatherByLatLon(
- lat: number,
- lon: number,
units?: units,
useAI?: boolean
): Promise {
- if (lat === 0 && lon === 0) {
- throw new Error('Invalid coordinates')
- }
-
const client = await getMainClient()
const response = await client.get('/weather/current', {
params: {
- lat,
- lon,
units,
useAI,
},
@@ -33,14 +25,10 @@ type GetWeatherByLatLon = {
refetchInterval: number | null
enabled: boolean
}
-export function useGetWeatherByLatLon(
- lat: number,
- lon: number,
- options: GetWeatherByLatLon
-) {
+export function useGetWeatherByLatLon(options: GetWeatherByLatLon) {
return useQuery({
- queryKey: ['getWeatherByLatLon', lat, lon],
- queryFn: () => fetchWeatherByLatLon(lat, lon, options.units, options.useAI),
+ queryKey: ['getWeatherByLatLon'],
+ queryFn: () => fetchWeatherByLatLon(options.units, options.useAI),
refetchInterval: options?.refetchInterval || false,
enabled: options.enabled,
staleTime: ms('5m'),
diff --git a/wxt.config.ts b/wxt.config.ts
index f893e06f..09bfb39e 100644
--- a/wxt.config.ts
+++ b/wxt.config.ts
@@ -48,7 +48,7 @@ export default defineConfig({
'@wxt-dev/module-react',
],
manifest: {
- version: '1.0.57',
+ version: '1.0.58',
name: 'Widgetify',
description:
'Transform your new tab into a smart dashboard with Widgetify! Get currency rates, crypto prices, weather & more.',