@uniai-fe/uds-templates 0.5.12 → 0.5.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.5.12",
3
+ "version": "0.5.14",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -12,6 +12,13 @@ export const CCTV_MESSAGE = {
12
12
  offline: "CCTV 연결 오류",
13
13
  } as const;
14
14
 
15
+ const RTC_PREPARING_STATES = new Set<RTCPeerConnectionState>([
16
+ "new",
17
+ "connecting",
18
+ "disconnected",
19
+ "closed",
20
+ ]);
21
+
15
22
  /**
16
23
  * CCTV; 스트리밍 상태에 따른 메시지 추출
17
24
  * @param {CctvVideoOverlayMessageParams} params 상태 파라미터
@@ -27,6 +34,7 @@ export const CCTV_MESSAGE = {
27
34
  */
28
35
  export function getOverlayMessage({
29
36
  cam,
37
+ connectionState,
30
38
  hasCamProp = false,
31
39
  isFetching = false,
32
40
  isTokenLoading,
@@ -44,6 +52,8 @@ export function getOverlayMessage({
44
52
  if (isTokenError) return CCTV_MESSAGE.tokenError;
45
53
  if (streamError) return streamError;
46
54
  if (isTokenLoading || isStreaming) return CCTV_MESSAGE.preparing;
55
+ if (connectionState === "failed") return CCTV_MESSAGE.offline;
56
+ if (RTC_PREPARING_STATES.has(connectionState)) return CCTV_MESSAGE.preparing;
47
57
  return null;
48
58
  }
49
59
 
@@ -82,6 +82,8 @@ export const routeWeatherKoreaNow = async ({
82
82
  const NOW_DATA: API_Res_WeatherKoreaToday = {
83
83
  ...API_RES_BASE,
84
84
  temperature: null,
85
+ max_temperature: null,
86
+ min_temperature: null,
85
87
  humidity: null,
86
88
  };
87
89
  const resDefault: API_Res_WeatherKoreaNow = {
@@ -146,7 +148,13 @@ export const routeWeatherKoreaForecast = async ({
146
148
  };
147
149
  const resDefault: API_Res_WeatherKoreaForecast = {
148
150
  raw: API_RES_RAW as KMA_Res_WeatherForecast,
149
- today: { ...API_RES_BASE, temperature: null, humidity: null },
151
+ today: {
152
+ ...API_RES_BASE,
153
+ temperature: null,
154
+ max_temperature: null,
155
+ min_temperature: null,
156
+ humidity: null,
157
+ },
150
158
  day_1: { ...FORECAST_DATA },
151
159
  day_2: { ...FORECAST_DATA },
152
160
  };
@@ -10,6 +10,7 @@ import type { WeatherPageHeaderTodayProps } from "../../types";
10
10
  import { useOpenWeatherMap, useWeatherKorea } from "../../hooks";
11
11
  import {
12
12
  getOpenWeatherMapConditionCode,
13
+ getOpenWeatherMapTodayForecast,
13
14
  isKoreaWeatherLocale,
14
15
  resolveWeatherPageHeaderTexts,
15
16
  } from "../../utils";
@@ -27,17 +28,27 @@ export default function WeatherPageHeaderToday({
27
28
  const openWeatherMapWeatherFallback = useOpenWeatherMap({
28
29
  locale,
29
30
  enabledNow: !openWeatherMapWeather && !isKoreaProvider,
30
- enabledForecast: false,
31
+ enabledForecast: !openWeatherMapWeather && !isKoreaProvider,
31
32
  });
32
33
  const korea = koreaWeather ?? koreaWeatherFallback;
33
34
  const openWeatherMap = openWeatherMapWeather ?? openWeatherMapWeatherFallback;
34
35
  const texts = resolveWeatherPageHeaderTexts(textOverrides);
36
+ const openWeatherMapToday = useMemo(
37
+ () => getOpenWeatherMapTodayForecast(openWeatherMap.forecast),
38
+ [openWeatherMap.forecast],
39
+ );
35
40
 
36
41
  const isFetchingAlternate = useMemo(() => {
37
- if (!isKoreaProvider) return openWeatherMap.isFetchingNow;
42
+ if (!isKoreaProvider)
43
+ return openWeatherMap.isFetchingNow || openWeatherMap.isFetchingForecast;
38
44
  if (korea.isFetchingNow) return true;
39
45
  return false;
40
- }, [isKoreaProvider, korea.isFetchingNow, openWeatherMap.isFetchingNow]);
46
+ }, [
47
+ isKoreaProvider,
48
+ korea.isFetchingNow,
49
+ openWeatherMap.isFetchingForecast,
50
+ openWeatherMap.isFetchingNow,
51
+ ]);
41
52
 
42
53
  const iconCode = useMemo(() => {
43
54
  if (isKoreaProvider) {
@@ -119,8 +130,57 @@ export default function WeatherPageHeaderToday({
119
130
  openWeatherMap.now?.main?.humidity,
120
131
  ]);
121
132
 
122
- const maxTemperature = null;
123
- const minTemperature = null;
133
+ const maxTemperature = useMemo(() => {
134
+ if (isKoreaProvider) {
135
+ const kma_forecast = korea.forecast?.today?.max_temperature;
136
+ if (
137
+ kma_forecast !== null &&
138
+ kma_forecast !== undefined &&
139
+ !Number.isNaN(Number(kma_forecast))
140
+ )
141
+ return Number(kma_forecast);
142
+ }
143
+
144
+ const alt_forecast = openWeatherMapToday?.max_temperature;
145
+ if (
146
+ alt_forecast !== null &&
147
+ alt_forecast !== undefined &&
148
+ !Number.isNaN(Number(alt_forecast))
149
+ )
150
+ return Number(alt_forecast);
151
+
152
+ return null;
153
+ }, [
154
+ isKoreaProvider,
155
+ korea.forecast?.today?.max_temperature,
156
+ openWeatherMapToday?.max_temperature,
157
+ ]);
158
+
159
+ const minTemperature = useMemo(() => {
160
+ if (isKoreaProvider) {
161
+ const kma_forecast = korea.forecast?.today?.min_temperature;
162
+ if (
163
+ kma_forecast !== null &&
164
+ kma_forecast !== undefined &&
165
+ !Number.isNaN(Number(kma_forecast))
166
+ )
167
+ return Number(kma_forecast);
168
+ }
169
+
170
+ const alt_forecast = openWeatherMapToday?.min_temperature;
171
+ if (
172
+ alt_forecast !== null &&
173
+ alt_forecast !== undefined &&
174
+ !Number.isNaN(Number(alt_forecast))
175
+ )
176
+ return Number(alt_forecast);
177
+
178
+ return null;
179
+ }, [
180
+ isKoreaProvider,
181
+ korea.forecast?.today?.min_temperature,
182
+ openWeatherMapToday?.min_temperature,
183
+ ]);
124
184
  const alert = (
125
185
  <WeatherPageHeaderAlert locale={locale} texts={textOverrides} />
126
186
  );
@@ -158,11 +158,17 @@ export type API_Res_WeatherKoreaBase = {
158
158
  /**
159
159
  * 기상청 API; 오늘 날씨
160
160
  * @property {number|string|null} temperature 기온(℃)
161
+ * @property {number|string|null} max_temperature 최고기온(℃)
162
+ * @property {number|string|null} min_temperature 최저기온(℃)
161
163
  * @property {number|string|null} humidity 습도(%)
162
164
  */
163
165
  export type API_Res_WeatherKoreaToday = API_Res_WeatherKoreaBase & {
164
166
  /** 기온(℃) */
165
167
  temperature: number | string | null;
168
+ /** 최고 기온(℃) */
169
+ max_temperature: number | string | null;
170
+ /** 최저 기온(℃) */
171
+ min_temperature: number | string | null;
166
172
  /** 습도(%) */
167
173
  humidity: number | string | null;
168
174
  };
@@ -267,7 +267,7 @@ const getLocalDateKey = (date: Date): string => {
267
267
  const getForecastItemDateKey = (item: OWM_Res_Weather_Forecast_Item): string =>
268
268
  item.dt_txt?.slice(0, 10) || getLocalDateKey(new Date(item.dt * 1000));
269
269
 
270
- const toOpenWeatherMapNextDay = (
270
+ const toOpenWeatherMapDayForecast = (
271
271
  items: OWM_Res_Weather_Forecast_Item[],
272
272
  ): API_Res_WeatherKoreaNextDays | undefined => {
273
273
  if (items.length === 0) return undefined;
@@ -275,11 +275,11 @@ const toOpenWeatherMapNextDay = (
275
275
  const conditionItem =
276
276
  items.find(item => item.dt_txt?.includes("12:00")) ||
277
277
  items[Math.floor(items.length / 2)];
278
- const temperatures = items.flatMap(item => [
279
- item.main.temp_min,
280
- item.main.temp_max,
281
- item.main.temp,
282
- ]);
278
+ const temperatures = items
279
+ .flatMap(item => [item.main.temp_min, item.main.temp_max, item.main.temp])
280
+ .filter((temperature): temperature is number =>
281
+ Number.isFinite(temperature),
282
+ );
283
283
 
284
284
  return {
285
285
  sky: null,
@@ -324,11 +324,29 @@ export const getOpenWeatherMapNextDays = (
324
324
  const [day1Key, day2Key] = Object.keys(grouped).sort();
325
325
 
326
326
  return {
327
- day_1: day1Key ? toOpenWeatherMapNextDay(grouped[day1Key]) : undefined,
328
- day_2: day2Key ? toOpenWeatherMapNextDay(grouped[day2Key]) : undefined,
327
+ day_1: day1Key ? toOpenWeatherMapDayForecast(grouped[day1Key]) : undefined,
328
+ day_2: day2Key ? toOpenWeatherMapDayForecast(grouped[day2Key]) : undefined,
329
329
  };
330
330
  };
331
331
 
332
+ /**
333
+ * 날씨 도구; OpenWeatherMap 예보 응답을 오늘 최고/최저 형태로 변환
334
+ * @param {OWM_Res_Weather_Forecast} [forecast] OpenWeatherMap 예보 응답
335
+ * @return {API_Res_WeatherKoreaNextDays | undefined} 오늘 최고/최저 예보
336
+ */
337
+ export const getOpenWeatherMapTodayForecast = (
338
+ forecast?: OWM_Res_Weather_Forecast,
339
+ ): API_Res_WeatherKoreaNextDays | undefined => {
340
+ if (!Array.isArray(forecast?.list)) return undefined;
341
+
342
+ const todayKey = getLocalDateKey(new Date());
343
+ const todayItems = forecast.list.filter(
344
+ item => getForecastItemDateKey(item) === todayKey,
345
+ );
346
+
347
+ return toOpenWeatherMapDayForecast(todayItems);
348
+ };
349
+
332
350
  export function now(
333
351
  outputResponse: API_Res_WeatherKoreaNow,
334
352
  apiResponse: KMA_Res_WeatherNow,
@@ -396,6 +414,8 @@ export function forecast(
396
414
  };
397
415
 
398
416
  res.today.temperature = getValueForecast(items, today, "TMP");
417
+ res.today.max_temperature = getValueForecast(items, today, "TMX");
418
+ res.today.min_temperature = getValueForecast(items, today, "TMN");
399
419
  res.today.humidity = getValueForecast(items, today, "REH");
400
420
  Object.assign(res.today, getDailyState(today));
401
421