diff --git a/README.md b/README.md
index 55fcfd6d..8ebecc9a 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,11 @@ If you are not familiar with the different technologies used in this project, pl
- [tRPC](https://trpc.io)
- [TypeScript](https://www.typescriptlang.org)
- [Turborepo](https://turbo.build/repo)
+#### APIs
- [OpenWeatherMap API](https://openweathermap.org/api)
- [Open Meteo API](https://open-meteo.com)
+- [QWeather API](https://dev.qweather.com/en/)
+- [API Ninjas API](https://api-ninjas.com/) (For the Reverse Geocoding)
## Learn More
diff --git a/apps/web/public/locales/de/home.json b/apps/web/public/locales/de/home.json
index 4d2f140e..36649131 100644
--- a/apps/web/public/locales/de/home.json
+++ b/apps/web/public/locales/de/home.json
@@ -69,5 +69,9 @@
"moon phase waning crescent": "Abnehmende Sichel",
"more information": "Mehr Informationen",
- "sun hours": "Sonnenstunden"
+ "less information": "Weniger Informationen",
+
+ "sun hours": "Sonnenstunden",
+
+ "from": "Von"
}
diff --git a/apps/web/public/locales/en/home.json b/apps/web/public/locales/en/home.json
index 722bff3c..40bd4494 100644
--- a/apps/web/public/locales/en/home.json
+++ b/apps/web/public/locales/en/home.json
@@ -69,5 +69,9 @@
"moon phase waning crescent": "Waning Crescent",
"more information": "More Information",
- "sun hours": "Sun Hours"
+ "less information": "Less Information",
+
+ "sun hours": "Sun Hours",
+
+ "from": "From"
}
diff --git a/apps/web/src/pages/home/index.tsx b/apps/web/src/pages/home/index.tsx
index afa296b0..c48900c8 100644
--- a/apps/web/src/pages/home/index.tsx
+++ b/apps/web/src/pages/home/index.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useState } from "react";
import dynamic from "next/dynamic";
import Link from "next/link";
import { useRouter } from "next/router";
@@ -109,10 +109,17 @@ function convertWindSpeed(
const InternalHome = observer(() => {
const [isMoreInfoCollapsibleOpen, setIsMoreInfoCollapsibleOpen] =
- React.useState(false);
+ useState(false);
+ const [isMoreWarningsCollapsibleOpen, setIsMoreWarningsCollapsibleOpen] =
+ useState(false);
+
const { locale } = useRouter();
const weatherData = api.weather.getWeather.useQuery(
- { coordinates: activeCity$.coord.get(), timezone: dayjs.tz.guess() },
+ {
+ coordinates: activeCity$.coord.get(),
+ timezone: dayjs.tz.guess(),
+ lang: locale,
+ },
// TODO: The cache (stale time) is not yet working if you refresh the page
{ refetchOnWindowFocus: false, staleTime: 1000 * 60 * 60 /* 1 hour */ },
);
@@ -342,6 +349,72 @@ const InternalHome = observer(() => {
) : null}
+ {weatherData.data?.warnings ? (
+
+ {weatherData.data.warnings.map((warning, index: number) => {
+ if (index > 0 || warning.status !== "active") return null;
+ const maxWarningTextLength = 150;
+ const textToLong = warning.text.length > maxWarningTextLength;
+ let warningText = warning.text;
+
+ if (
+ warning.text.length > maxWarningTextLength &&
+ !isMoreWarningsCollapsibleOpen
+ ) {
+ warningText = warning.text.slice(0, maxWarningTextLength) + "...";
+ }
+
+ return (
+
+
+
+ {ReactHtmlParser(warning.title)}
+
+
+ {ReactHtmlParser(warningText)}
+ {textToLong && (
+
+ )}
+
+
+ {translationHome("from")}: {ReactHtmlParser(warning.sender)}
+
+
+
+ );
+ })}
+
+ ) : null}
{weatherData.data?.hourlyForecast ? (
diff --git a/packages/api/src/routers/weather.ts b/packages/api/src/routers/weather.ts
index 7a45f2cc..2ca8f65c 100644
--- a/packages/api/src/routers/weather.ts
+++ b/packages/api/src/routers/weather.ts
@@ -129,6 +129,26 @@ const MoonPhaseSchema = z.object({
type MoonPhase = z.infer | undefined;
+const WarningSchema = z.object({
+ warning: z.array(
+ z.object({
+ sender: z.string(),
+ pubTime: z.string(),
+ title: z.string(),
+ startTime: z.string(),
+ endTime: z.string(),
+ status: z.string(),
+ level: z.string(),
+ severity: z.string(),
+ urgency: z.string(),
+ certainty: z.string(),
+ text: z.string(),
+ }),
+ ),
+});
+
+type Warning = z.infer | undefined;
+
/**
* Calculates the Air Quality Index (AQI) based on the given PM10 and PM2.5 values.
*
@@ -172,6 +192,7 @@ export const weatherRouter = createTRPCRouter({
lon: z.number().min(-180).max(180),
}),
timezone: z.string(),
+ lang: z.union([z.string(), z.undefined()] as const),
}),
)
.query(async ({ input, ctx }) => {
@@ -180,6 +201,7 @@ export const weatherRouter = createTRPCRouter({
timezone: input.timezone,
user: ctx.ip,
});
+ const lang = input.lang ? input.lang : "en";
// OpenWeatherMap API
const urlWeather = `https://api.openweathermap.org/data/2.5/weather?lat=${input.coordinates.lat}&lon=${input.coordinates.lon}&appid=${env.OPEN_WEATHER_API_KEY}`;
// Open Meteo
@@ -191,23 +213,31 @@ export const weatherRouter = createTRPCRouter({
)},${input.coordinates.lat.toFixed(2)}&key=${
env.QWEATHER_API_KEY
}&date=${dayjs().tz("Asia/Shanghai").format("YYYYMMDD")}`;
+ const urlWarning = `https://devapi.qweather.com/v7/warning/now?location=${input.coordinates.lon.toFixed(
+ 2,
+ )},${input.coordinates.lat.toFixed(2)}&key=${
+ env.QWEATHER_API_KEY
+ }&lang=${lang}`;
const [
hourlyResult,
presentWeatherResult,
presentAirQualityResult,
moonPhaseResult,
+ warningResult,
] = await Promise.allSettled([
axios.get(urlHourlyAndDailyForecast),
axios.get(urlWeather),
axios.get(urlAirQuality),
axios.get(urlMoonPhase),
+ axios.get(urlWarning),
]);
let hourlyAndDailyData: HourlyAndDailyWeather = undefined;
let presentWeather: PresentWeather = undefined;
let presentAirQuality: PresentAirQuality = undefined;
let moonPhase: MoonPhase = undefined;
+ let warning: Warning = undefined;
if (hourlyResult.status === "fulfilled") {
try {
@@ -330,6 +360,23 @@ export const weatherRouter = createTRPCRouter({
: "The reason is not a string",
});
}
+ if (warningResult.status === "fulfilled") {
+ try {
+ // console.debug(warningResult.value.data);
+ // console.debug(urlWarning);
+ warning = WarningSchema.parse(warningResult.value.data);
+ } catch (error) {
+ if (error instanceof z.ZodError) {
+ log.error("Zod Errors in the warning", error.issues);
+ console.debug("Warning data without the filter", {
+ data: warningResult.value.data,
+ });
+ console.debug("Warning data url", urlWarning);
+ } else {
+ log.error("Else Error in the warning", { error });
+ }
+ }
+ }
let presentAirQualityIndex: number | undefined = undefined;
@@ -653,6 +700,7 @@ export const weatherRouter = createTRPCRouter({
? (hourlyAndDailyData.daily.sunshine_duration[0] / 3600).toFixed(0)
: undefined,
maxUVIndex: hourlyAndDailyData?.daily.uv_index_max[0],
+ warnings: warning?.warning,
};
}),
});