Personal site staging.colinozanne.co.uk
portfolio astro

feat: add location+weather text

finxol.io ffd96077 10047834

verified
+140 -9
+13 -3
src/assets/styles/index.css
··· 80 80 & > div.container { 81 81 --container-color: var(--secondary); 82 82 } 83 + 84 + div.weather { 85 + font-size: var(--size-0); 86 + } 83 87 } 84 88 85 89 section.container { ··· 285 289 grid-template-columns: auto auto 1fr; 286 290 grid-template-areas: 287 291 "aside h3 intro" 288 - ". body body"; 292 + ". body body" 293 + ". others others"; 289 294 align-items: center; 290 295 justify-content: start; 291 296 padding: calc(var(--spacing) * 2) var(--spacing) ··· 296 301 grid-template-areas: 297 302 "aside h3" 298 303 ". intro" 299 - ". body"; 304 + ". body" 305 + ". others"; 300 306 } 301 307 302 308 & > aside { ··· 325 331 font-size: var(--size-1); 326 332 } 327 333 328 - & > div { 334 + & > div:not([class]) { 329 335 grid-area: body; 330 336 display: flex; 331 337 flex-direction: column; 332 338 gap: var(--spacing); 339 + } 340 + 341 + & > div[class] { 342 + grid-area: others; 333 343 } 334 344 } 335 345 }
+6 -3
src/components/pages/index.astro
··· 6 6 import { config } from "@/config"; 7 7 import type { Locale } from "@/hooks/useLocale.astro"; 8 8 import Customise from "../customise.astro"; 9 + import Weather from "../weather.astro"; 9 10 10 11 interface Props { 11 12 content: { 12 13 intro: { 13 14 title: string; 14 15 description: string; 15 - ps: string[]; 16 + ilivein: string; 16 17 }; 17 18 languages: string; 18 19 blogSection: { ··· 47 48 <aside>👋</aside> 48 49 <h3>{intro.title}</h3> 49 50 <p>{intro.description}</p> 50 - <div> 51 - {intro.ps.map((p) => <p>{p}</p>)} 51 + <div class="weather"> 52 + {intro.ilivein} 53 + {config.city.name}, {config.city.country}. 54 + <Weather locale={locale} server:defer /> 52 55 </div> 53 56 </div> 54 57 <Customise />
+111
src/components/weather.astro
··· 1 + --- 2 + import { config } from "@/config"; 3 + import type { Locale } from "@/hooks/useLocale.astro"; 4 + 5 + interface WeatherData { 6 + update_time: string; 7 + type: string; 8 + geometry: Geometry; 9 + properties: Properties; 10 + } 11 + 12 + interface Geometry { 13 + type: string; 14 + coordinates: number[]; 15 + } 16 + 17 + interface Properties { 18 + altitude: number; 19 + name: string; 20 + country: string; 21 + french_department: string; 22 + rain_product_available: number; 23 + timezone: string; 24 + insee: string; 25 + bulletin_cote: number; 26 + daily_forecast: DailyForecast[]; 27 + forecast: Forecast[]; 28 + probability_forecast: ProbabilityForecast[]; 29 + } 30 + 31 + interface DailyForecast { 32 + time: string; 33 + T_min: number; 34 + T_max: number; 35 + T_sea: any; 36 + relative_humidity_min: number; 37 + relative_humidity_max: number; 38 + total_precipitation_24h: number; 39 + uv_index: number; 40 + daily_weather_icon: string; 41 + daily_weather_description: string; 42 + sunrise_time: string; 43 + sunset_time: string; 44 + } 45 + 46 + interface Forecast { 47 + time: string; 48 + T: number; 49 + T_windchill: number; 50 + relative_humidity: number; 51 + P_sea: number; 52 + wind_speed: number; 53 + wind_speed_gust: number; 54 + wind_direction: number; 55 + wind_icon: string; 56 + rain_1h?: number; 57 + rain_3h?: number; 58 + rain_6h: any; 59 + rain_12h: any; 60 + rain_24h: any; 61 + snow_1h?: number; 62 + snow_3h?: number; 63 + snow_6h: any; 64 + snow_12h: any; 65 + snow_24h: any; 66 + iso0: number; 67 + rain_snow_limit: any; 68 + total_cloud_cover: number; 69 + weather_icon: string; 70 + weather_description: string; 71 + } 72 + 73 + interface ProbabilityForecast { 74 + time: string; 75 + rain_hazard_3h?: number; 76 + rain_hazard_6h?: number; 77 + snow_hazard_3h?: number; 78 + snow_hazard_6h?: number; 79 + freezing_hazard: number; 80 + storm_hazard: number; 81 + } 82 + 83 + const weatherUrl = `https://rpcache-aa.meteofrance.com/internet2018client/2.0/forecast?lat=${config.city.lat}&lon=${config.city.lon}&instants=&day=2&token=__Wj7dVSTjV9YGu1guveLyDq0g7S7TfTjaHBTPTpO0kj8__`; 84 + 85 + console.log(weatherUrl); 86 + 87 + const weather = async () => { 88 + const response = await fetch(weatherUrl); 89 + const data = await response.json(); 90 + return data as WeatherData; 91 + }; 92 + 93 + const current_forecast = (await weather()).properties.daily_forecast[0]; 94 + 95 + interface Props { 96 + locale: Locale; 97 + } 98 + 99 + const text = (temp: number) => { 100 + if (Astro.props.locale === "en") { 101 + return `It's ${temp}°C today.`; 102 + } 103 + return `Il fait ${temp}°C aujourd'hui.`; 104 + }; 105 + --- 106 + 107 + <span> 108 + {text(Math.round((current_forecast.T_max + current_forecast.T_min) / 2))} 109 + </span> 110 + 111 + <style scoped></style>
+6
src/config.ts
··· 12 12 fr: "https://next.colinozanne.fr", 13 13 en: "https://next.colinozanne.co.uk", 14 14 }, 15 + city: { 16 + name: "Rennes", 17 + country: "France", 18 + lat: "48.1113387", 19 + lon: "-1.6800198", 20 + }, 15 21 } as const;
+2 -1
src/middleware.ts
··· 29 29 // For other paths without locale prefix, also rewrite 30 30 if ( 31 31 domainLocale && !url.pathname.startsWith(`/${domainLocale}/`) && 32 - !url.pathname.startsWith(`/${domainLocale}`) 32 + !url.pathname.startsWith(`/${domainLocale}`) && 33 + !url.pathname.startsWith("/_") 33 34 ) { 34 35 // Check if the path starts with any other locale 35 36 const startsWithOtherLocale = config.locales.some(
+1 -1
src/pages/en/index.astro
··· 5 5 intro: { 6 6 title: "Hiya!", 7 7 description: "I'm a 🇫🇷 French computer science student.", 8 - ps: ["I love building robust and performant websites."], 8 + ilivein: "I'm currently living in", 9 9 }, 10 10 languages: `<p>I'm a native 🇬🇧 <strong>English</strong> and 🇫🇷 <strong>French</strong> speaker.</p>`, 11 11 blogSection: {
+1 -1
src/pages/fr/index.astro
··· 5 5 intro: { 6 6 title: "Bonjour!", 7 7 description: "Je suis un étudiant en informatique 🇫🇷 Français.", 8 - ps: ["J'adore construire des sites web robustes et performants."], 8 + ilivein: "J'habite actuellement à", 9 9 }, 10 10 languages: `<p>Je suis natif en 🇬🇧 <strong>Anglais</strong> et 🇫🇷 <strong>Français</strong>.</p>`, 11 11 blogSection: {