Skip to content

Commit 2057992

Browse files
committed
Add moon phase
1 parent db40532 commit 2057992

File tree

9 files changed

+117
-20
lines changed

9 files changed

+117
-20
lines changed

download-moon.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
3+
base_url="https://cosmicdashboard.com/images"
4+
prefix="moon"
5+
start=1
6+
end=28
7+
8+
for i in $(seq $start $end)
9+
do
10+
url="${base_url}/${prefix}-${i}.png"
11+
filename="${prefix}-${i}.png"
12+
13+
echo "Downloading ${filename}..."
14+
curl -o "./moon/$filename" "$url"
15+
16+
if [ $? -eq 0 ]; then
17+
echo "Successfully downloaded ${filename}"
18+
else
19+
echo "Failed to download ${filename}"
20+
fi
21+
done
22+
23+
echo "Download complete!"

src/screens/Weather/Main/Weather.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Canvas, LinearGradient, Rect, vec } from '@shopify/react-native-skia'
44
import { useMutation } from '@tanstack/react-query'
55
import { WeatherColors } from '@utils/colors'
66
import { H, W } from '@utils/dimensions'
7+
import { F, Medium, Regular, SemiBold } from '@utils/fonts'
78
import type { NavProp, Theme } from '@utils/types'
89
import React, { useCallback, useEffect, useMemo } from 'react'
910
import { StatusBar, View } from 'react-native'
@@ -20,6 +21,7 @@ import FeelsLike, { getFeelsLikeStatusString } from './components/FeelsLike'
2021
import Header from './components/Header'
2122
import HourlyForecast from './components/HourlyForecast'
2223
import Humidity from './components/Humidity'
24+
import MoonPhase from './components/MoonPhase'
2325
import Precipitation from './components/Precipitation'
2426
import Pressure from './components/Pressure'
2527
import SunRiseSet from './components/SunRiseSet'
@@ -64,10 +66,6 @@ export default function WeatherScreen({ navigation }: NavProp) {
6466
})
6567
const w = data || currentWeather
6668

67-
useEffect(() => {
68-
console.log(currentCity)
69-
}, [currentCity])
70-
7169
useEffect(() => {
7270
if (currentCity) mutate()
7371
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -98,7 +96,7 @@ export default function WeatherScreen({ navigation }: NavProp) {
9896
<PaddingTop />
9997
<Header navigation={navigation} color={color} isPending={isPending} />
10098
</View>
101-
<ScrollView contentContainerStyle={{ paddingBottom: 100, gap: 12 }}>
99+
<ScrollView contentContainerStyle={{ paddingBottom: bottom + 20, gap: 12 }}>
102100
<WeatherTopInfo color={color} w={w} />
103101
<HourlyForecast color={color} w={w} hourly={data?.hourly} />
104102
<DailyForecast color={color} daily={data?.daily} theme={theme} />
@@ -164,7 +162,18 @@ function Boxes({ w, theme }: { w: Weather; theme: Theme }) {
164162
<AirQualityIndex aqi={currentAQI?.list?.[0]?.main?.aqi} theme={theme} aqiStatus={aqiStatus} />
165163
<Cloudiness theme={theme} clouds={w?.current.clouds || 0} />
166164
<Precipitation theme={theme} rain={w?.daily[0]?.rain} snow={w?.daily[0]?.snow} status={rainStatus} />
167-
<SunRiseSet w={w} theme={theme} now={w?.current?.dt} sunrise={w?.current?.sunrise} sunset={w?.current?.sunset} />
165+
<SunRiseSet theme={theme} now={w?.current?.dt} sunrise={w?.current?.sunrise} sunset={w?.current?.sunset} />
166+
<MoonPhase
167+
theme={theme}
168+
moonrise={w?.daily[0]?.moonrise}
169+
moonset={w?.daily[0]?.moonset}
170+
phase={w?.daily[0]?.moon_phase}
171+
/>
172+
<View className='w-full items-center justify-center'>
173+
<SemiBold className='mt-5 text-center opacity-50' style={F.F10}>
174+
Powered by OpenWeatherMap
175+
</SemiBold>
176+
</View>
168177
</View>
169178
)
170179
}

src/screens/Weather/Main/components/DailyForecast.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default function DailyForecast({ color, daily, theme }: WeatherForecastPr
4444
<View className='rounded-3xl bg-black/10 pb-2'>
4545
<WeatherLabel color={color} label='7-Day Forecast' Icon={Calendar03SolidIcon} />
4646
<Underline />
47-
{!data && <View style={{ height: 56 * 5.855 }}></View>}
47+
{!data && <View style={{ height: 56 * 6.855 }}></View>}
4848
<View>
4949
{data?.map((d, i) => {
5050
return (

src/screens/Weather/Main/components/HourlyForecast.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type HourlyWeather = {
2020
hourly: Current[] | undefined
2121
}
2222

23-
export default function HourlyForecast({ color, w, hourly }: HourlyWeather) {
23+
const HourlyForecast = React.memo<HourlyWeather>(({ color, w, hourly }) => {
2424
const currentUnit = weatherStore((state) => state.temperatureUnit)
2525
const timeFormat = weatherStore((state) => state.weatherTimeFormat)
2626
const Icon = Icons[w?.current.weather[0]!.icon || '02d']
@@ -69,7 +69,9 @@ export default function HourlyForecast({ color, w, hourly }: HourlyWeather) {
6969
</View>
7070
</Animated.View>
7171
)
72-
}
72+
})
73+
74+
export default HourlyForecast
7375

7476
type SmallWeatherProps = {
7577
color: {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { weatherStore } from '@/zustand/weatherStore'
2+
import { Moon02SolidIcon } from '@assets/icons/icons'
3+
import { getMoonImageLink } from '@screens/Weather/api'
4+
import { boxFullSize, mapMoonPhaseToImage } from '@screens/Weather/utils'
5+
import { F, Medium, Regular } from '@utils/fonts'
6+
import type { Theme } from '@utils/types'
7+
import { getHoursMinutes } from '@utils/utils'
8+
import React from 'react'
9+
import { Image, View } from 'react-native'
10+
import WeatherLabel from './WeatherLabel'
11+
12+
type MoonRiseSetProps = {
13+
theme: Theme
14+
moonrise?: number
15+
moonset?: number
16+
phase?: number
17+
}
18+
19+
export default function MoonPhase({ moonrise, moonset, phase, theme }: MoonRiseSetProps) {
20+
const timeFormat = weatherStore((state) => state.weatherTimeFormat)
21+
22+
return (
23+
<View className='rounded-3xl bg-black/10' style={boxFullSize}>
24+
<WeatherLabel Icon={Moon02SolidIcon} color={theme.color} label={'Moon Phase'} />
25+
<View className='flex-1 justify-between px-5 pb-4 pt-0'>
26+
<View className='flex-row justify-between'>
27+
<View className='flex-1'>
28+
<Medium style={[{ fontSize: 35 }, theme.color]}>{phase || '__'}</Medium>
29+
<Regular style={[theme.color, F.F12]}>{moonPhaseString(phase || 1)}</Regular>
30+
{/* <View> */}
31+
<Regular style={[theme.color, F.F12]}>Moonrise at {getHoursMinutes(moonrise || 0, timeFormat)}, </Regular>
32+
<Regular style={[theme.color, F.F12]}>Moonset at {getHoursMinutes(moonset || 0, timeFormat)}</Regular>
33+
{/* </View> */}
34+
</View>
35+
<Image
36+
source={{ uri: getMoonImageLink(mapMoonPhaseToImage(phase || 1)) }}
37+
style={{
38+
height: 31 * 4,
39+
width: 31 * 4,
40+
marginTop: -7,
41+
}}
42+
/>
43+
</View>
44+
</View>
45+
</View>
46+
)
47+
}
48+
49+
function moonPhaseString(phase: number) {
50+
if (phase === 0) return 'New Moon'
51+
if (phase > 0 && phase < 0.25) return 'Waxing Crescent'
52+
if (phase === 0.25) return 'First Quarter'
53+
if (phase > 0.25 && phase < 0.5) return 'Waxing Gibbous'
54+
if (phase === 0.5) return 'Full Moon'
55+
if (phase > 0.5 && phase < 0.75) return 'Waning Gibbous'
56+
if (phase === 0.75) return 'Last Quarter'
57+
if (phase > 0.75 && phase < 1) return 'Waning Crescent'
58+
return 'New Moon'
59+
}

src/screens/Weather/Main/components/Precipitation.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ export default function Precipitation({ theme, rain, snow, status }: Precipitati
1919
<WeatherLabel Icon={CloudAngledRainSolidIcon} color={theme.color} label='Precipitation' />
2020
<View className='flex-1 justify-between px-5 pb-4 pt-0'>
2121
<View className='flex-row items-end'>
22-
<Medium style={[{ fontSize: 33 }, theme.color]}>{rain || '__'}</Medium>
22+
<Medium style={[{ fontSize: 33 }, theme.color]}>{rain || '0'}</Medium>
2323
<Medium style={[{ fontSize: 20, paddingBottom: 8 }, theme.color]} className='pl-1'>
2424
mm{' '}
2525
</Medium>
2626
</View>
2727
{snow ? (
28-
<Regular style={[theme.color, F.F12]}>{snow || '__'} mm snowfall.</Regular>
28+
<Regular style={[theme.color, F.F12]}>{snow || '0'} mm snowfall.</Regular>
2929
) : (
3030
<Regular style={[theme.color, F.F12]}>{status}</Regular>
3131
)}

src/screens/Weather/Main/components/SunRiseSet.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { weatherStore } from '@/zustand/weatherStore'
22
import { Sun03SolidIcon } from '@assets/icons/icons'
3-
import type { Weather } from '@screens/Weather/types'
43
import { boxSize } from '@screens/Weather/utils'
54
import { F, Medium, Regular } from '@utils/fonts'
65
import type { Theme } from '@utils/types'
@@ -10,14 +9,13 @@ import { View } from 'react-native'
109
import WeatherLabel from './WeatherLabel'
1110

1211
type SunRiseSetProps = {
13-
w: Weather
1412
theme: Theme
1513
now?: number
1614
sunrise?: number
1715
sunset?: number
1816
}
1917

20-
export default function SunRiseSet({ w, theme, now, sunrise, sunset }: SunRiseSetProps) {
18+
export default function SunRiseSet({ theme, now, sunrise, sunset }: SunRiseSetProps) {
2119
const timeFormat = weatherStore((state) => state.weatherTimeFormat)
2220
const isDay = sunrise && sunset && now ? now > sunrise && now < sunset : null
2321

@@ -29,16 +27,15 @@ export default function SunRiseSet({ w, theme, now, sunrise, sunset }: SunRiseSe
2927
label={isDay === null ? 'Daylight' : isDay ? 'Sunset' : 'Sunrise'}
3028
/>
3129
<View className='flex-1 justify-between px-5 pb-4 pt-0'>
32-
<Medium style={[{ fontSize: 25 }, theme.color]}>
30+
<Medium style={[{ fontSize: 28 }, theme.color]}>
3331
{isDay === null
3432
? '__:__'
3533
: isDay
36-
? getHoursMinutes(w?.current.sunset || 0, timeFormat)
37-
: getHoursMinutes(w?.current.sunrise || 0, timeFormat)}
34+
? getHoursMinutes(sunset || 0, timeFormat)
35+
: getHoursMinutes(sunrise || 0, timeFormat)}
3836
</Medium>
3937
<Regular style={[theme.color, F.F12]}>
40-
Sunset at {getHoursMinutes(w?.current.sunset || 0, timeFormat)} and sunrise at{' '}
41-
{getHoursMinutes(w?.current.sunrise || 0, timeFormat)}.
38+
Sunrise at {getHoursMinutes(sunrise || 0, timeFormat)}, sunset at {getHoursMinutes(sunset || 0, timeFormat)}.
4239
</Regular>
4340
</View>
4441
</View>

src/screens/Weather/api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,7 @@ export async function getWeather(lat: number, lon: number) {
100100
export async function getAQI(lat: number, lon: number) {
101101
return await (await fetch(AQIUrl(lat, lon, OPENWEATHER_API_KEY))).json()
102102
}
103+
104+
export function getMoonImageLink(n: number) {
105+
return `https://codeabinash.github.io/moon/moon-${n}.png`
106+
}

src/screens/Weather/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,12 @@ export function getAQIStatus(aqi: number) {
7575
return 'Hazardous'
7676
}
7777
export function getRainStatus(rain: number | undefined) {
78-
if (!rain) return ''
78+
if (!rain) return 'No rain expected.'
7979
if (rain === 0) return 'No rain expected.'
8080
else if (rain < 2) return 'Light rain expected.'
8181
else if (rain < 5) return 'Moderate rain expected.'
8282
else return 'Heavy rain expected.'
8383
}
84+
export function mapMoonPhaseToImage(phase: number) {
85+
return Math.round(phase * 27) + 1
86+
}

0 commit comments

Comments
 (0)