From b9bdadb201a93b4fa24baf64e611bdbe05b30198 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Sat, 24 Feb 2024 21:49:21 +0100 Subject: [PATCH 1/7] added events for eventBusForHost for getting data for statistic from endpoint --- src/events/statistic.ts | 45 ++++++++++++++++++++++++++++ src/features/Statistic.tsx | 26 +++++++--------- src/hooks/useEventBusForHost.ts | 21 +++++++++++++ src/hooks/useEventBusForStatistic.ts | 45 ++++++++++++++++++++++++++-- src/services/refact.ts | 34 ++++++++++++++++++++- 5 files changed, 152 insertions(+), 19 deletions(-) diff --git a/src/events/statistic.ts b/src/events/statistic.ts index 30c7f967..78f52ea5 100644 --- a/src/events/statistic.ts +++ b/src/events/statistic.ts @@ -1,3 +1,48 @@ export enum EVENT_NAMES_FROM_STATISTIC { BACK_FROM_STATISTIC = "back_from_statistic", } + +export enum EVENT_NAMES_TO_STATISTIC { + REQUEST_STATISTIC_DATA = "request_statistic_data", + RECEIVE_STATISTIC_DATA = "receive_statistic_data", + RECEIVE_STATISTIC_DATA_ERROR = "receive_statistic_data_error", +} + +interface BaseAction { + type: EVENT_NAMES_FROM_STATISTIC | EVENT_NAMES_TO_STATISTIC; + payload?: { data: string }; +} + +export interface ActionToStatistic extends BaseAction { + type: EVENT_NAMES_TO_STATISTIC; +} +export interface RequestDataForStatistic extends ActionToStatistic { + type: EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA; +} + +export function isActionToStatistic( + action: unknown, +): action is ActionToStatistic { + if (!action) return false; + if (typeof action !== "object") return false; + if (!("type" in action)) return false; + if (typeof action.type !== "string") return false; + const ALL_EVENT_NAMES: Record = { + ...EVENT_NAMES_TO_STATISTIC, + }; + return Object.values(ALL_EVENT_NAMES).includes(action.type); +} + +export function isRequestDataForStatistic( + action: unknown, +): action is RequestDataForStatistic { + if (!isActionToStatistic(action)) return false; + return action.type === EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA; +} + +export function isReceiveDataForStatistic( + action: unknown, +): action is RequestDataForStatistic { + if (!isActionToStatistic(action)) return false; + return action.type === EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA; +} diff --git a/src/features/Statistic.tsx b/src/features/Statistic.tsx index fbf41c9c..ac8019b3 100644 --- a/src/features/Statistic.tsx +++ b/src/features/Statistic.tsx @@ -1,22 +1,19 @@ import React, { useEffect, useState } from "react"; import { Box, Flex, Button, Heading, Responsive } from "@radix-ui/themes"; -import { RefactTableData } from "../services/refact"; import { Table } from "../components/Table/Table"; import { Chart } from "../components/Chart/Chart"; import { Spinner } from "../components/Spinner"; import { ArrowLeftIcon } from "@radix-ui/react-icons"; import { useConfig } from "../contexts/config-context"; import { ScrollArea } from "../components/ScrollArea"; -import { TABLE } from "../__fixtures__"; import { useEventBusForStatistic } from "../hooks"; export const Statistic: React.FC<{ onCloseStatistic?: () => void; }> = ({ onCloseStatistic }) => { - const [isLoaded, setIsLoaded] = useState(false); - const [refactTable, setRefactTable] = useState(null); + const [isLoading, setIsLoading] = useState(true); const { host, tabbed } = useConfig(); - const { backFromStatistic } = useEventBusForStatistic(); + const { backFromStatistic, statisticData } = useEventBusForStatistic(); const LeftRightPadding: Responsive< "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" > = @@ -38,11 +35,10 @@ export const Statistic: React.FC<{ }; useEffect(() => { - if (TABLE.data) { - setRefactTable(JSON.parse(TABLE.data) as RefactTableData); - setIsLoaded(true); + if (statisticData) { + setIsLoading(false); } - }, []); + }, [statisticData]); return ( - {isLoaded ? ( + {isLoading ? ( + + ) : ( Statistics - {refactTable !== null && ( + {statisticData !== null && ( )} - ) : ( - )} diff --git a/src/hooks/useEventBusForHost.ts b/src/hooks/useEventBusForHost.ts index 308cd3f7..0ca09c49 100644 --- a/src/hooks/useEventBusForHost.ts +++ b/src/hooks/useEventBusForHost.ts @@ -3,14 +3,17 @@ import { sendChat, getCaps, ChatContextFile } from "../services/refact"; import { useChatHistory } from "./useChatHistory"; import { EVENT_NAMES_TO_CHAT, + EVENT_NAMES_TO_STATISTIC, ChatThread, isQuestionFromChat, isSaveChatFromChat, isRequestCapsFromChat, isStopStreamingFromChat, isRequestForFileFromChat, + isRequestDataForStatistic, } from "../events"; import { useConfig } from "../contexts/config-context"; +import { getStatisticData } from "../services/refact"; export function useEventBusForHost() { const { lspUrl } = useConfig(); @@ -119,6 +122,24 @@ export function useEventBusForHost() { ); }); } + + if (isRequestDataForStatistic(event.data)) { + getStatisticData(lspUrl) + .then((data) => { + window.postMessage({ + type: EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA, + payload: data, + }); + }) + .catch((error: Error) => { + window.postMessage({ + type: EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA_ERROR, + payload: { + message: error.message, + }, + }); + }); + } }; window.addEventListener("message", listener); diff --git a/src/hooks/useEventBusForStatistic.ts b/src/hooks/useEventBusForStatistic.ts index d6a8650c..62bc36db 100644 --- a/src/hooks/useEventBusForStatistic.ts +++ b/src/hooks/useEventBusForStatistic.ts @@ -1,16 +1,55 @@ -import { EVENT_NAMES_FROM_STATISTIC } from "../events"; +import { + EVENT_NAMES_FROM_STATISTIC, + EVENT_NAMES_TO_STATISTIC, + isReceiveDataForStatistic, +} from "../events"; import { usePostMessage } from "./usePostMessage"; +import { useEffect, useState } from "react"; +import { StatisticData } from "../services/refact"; export const useEventBusForStatistic = () => { const postMessage = usePostMessage(); + const [statisticData, setStatisticData] = useState( + null, + ); - function backFromStatistic() { + const backFromStatistic = () => { postMessage({ type: EVENT_NAMES_FROM_STATISTIC.BACK_FROM_STATISTIC, }); - } + }; + + useEffect(() => { + const listener = (event: MessageEvent) => { + if (isReceiveDataForStatistic(event.data)) { + if (event.data.payload !== undefined) { + const parsedData = JSON.parse( + event.data.payload.data, + ) as StatisticData; + setStatisticData(parsedData); + } + } + }; + + window.addEventListener("message", listener); + + return () => { + window.removeEventListener("message", listener); + }; + }, []); + + useEffect(() => { + const requestStatisticData = () => { + postMessage({ + type: EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA, + }); + }; + + requestStatisticData(); + }, [postMessage]); return { backFromStatistic, + statisticData, }; }; diff --git a/src/services/refact.ts b/src/services/refact.ts index 916d23f6..f8626854 100644 --- a/src/services/refact.ts +++ b/src/services/refact.ts @@ -1,6 +1,7 @@ import { getApiKey } from "../utils/ApiKey"; const CHAT_URL = `/v1/chat`; const CAPS_URL = `/v1/caps`; +const STATISTIC_URL = `/v1/get-dashboard-plots`; export type ChatRole = "user" | "assistant" | "context_file" | "system"; @@ -154,6 +155,37 @@ export async function getCaps(lspUrl?: string): Promise { return json; } +export function isStatisticDataResponse( + json: unknown, +): json is { data: string } { + if (!json || typeof json !== "object") return false; + return typeof (json as { data?: unknown }).data === "string"; +} + +export async function getStatisticData( + lspUrl?: string, +): Promise<{ data: string }> { + const statisticDataEndpoint = lspUrl + ? `${lspUrl.replace(/\/*$/, "")}${STATISTIC_URL}` + : STATISTIC_URL; + const response = await fetch(statisticDataEndpoint, { + method: "GET", + credentials: "same-origin", + headers: { + accept: "application/json", + }, + }); + if (!response.ok) { + throw new Error(response.statusText); + } + + const json: unknown = await response.json(); + if (!isStatisticDataResponse(json)) { + throw new Error("Invalid response for statistic data"); + } + return json; +} + type CodeChatModel = { default_scratchpad: string; n_ctx: number; @@ -211,7 +243,7 @@ export type RefactTableImpactDateObj = { export type RefactTableImpactLanguagesRow = { [key in ColumnName]: string | number; }; -export type RefactTableData = { +export type StatisticData = { refact_impact_dates: { data: { daily: Record; From b062e01037987cf163fbfa02891244a789d4c4f4 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Sun, 25 Feb 2024 22:36:24 +0100 Subject: [PATCH 2/7] updated ui of chart and table --- src/components/Chart/Chart.tsx | 24 ++++++++---------------- src/components/Table/Table.tsx | 8 ++++---- src/features/Statistic.tsx | 2 +- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/components/Chart/Chart.tsx b/src/components/Chart/Chart.tsx index 5a977af9..11ba2703 100644 --- a/src/components/Chart/Chart.tsx +++ b/src/components/Chart/Chart.tsx @@ -54,6 +54,7 @@ export const Chart: React.FC<{ left: "3%", right: "4%", bottom: "3%", + top: "10%", containLabel: true, }, xAxis: [ @@ -68,6 +69,10 @@ export const Chart: React.FC<{ yAxis: [ { type: "value", + name: "char.", + nameTextStyle: { + align: "right", + }, }, ], series: [ @@ -77,6 +82,7 @@ export const Chart: React.FC<{ stack: "Ad", data: humanData, barWidth: "80%", + itemStyle: { normal: { color: "#91cc75" } }, }, { name: "Refact", @@ -84,13 +90,14 @@ export const Chart: React.FC<{ stack: "Ad", data: refactData, barWidth: "80%", + itemStyle: { normal: { color: "#5470c6" } }, }, ], }; return ( - + Refact vs Human - - {dates.map((date: string, index: number) => ( - - - Date: {date} - - - Human: {humanData[index]} ch. - - - Refact: {refactData[index]} ch. - - - ))} - ); }; diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index 079d8215..c0949d74 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -12,10 +12,10 @@ import { formatTableCell } from "./formatTableCell"; const convertedColumnNames: Record = { lang: "Lang.", - refact: "Refact", - human: "Human", - total: "Total", - refact_impact: "Refact Impact", + refact: "Refact (char.)", + human: "Human (char.)", + total: "Total (char.)", + refact_impact: "Refact Impact (%)", completions: "Compl.", }; diff --git a/src/features/Statistic.tsx b/src/features/Statistic.tsx index ac8019b3..63f6f95a 100644 --- a/src/features/Statistic.tsx +++ b/src/features/Statistic.tsx @@ -88,7 +88,7 @@ export const Statistic: React.FC<{ width: "inherit", }} > - + Statistics {statisticData !== null && ( From 02a34c423db56f165045704b7c805663f2059232 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Mon, 26 Feb 2024 15:30:41 +0100 Subject: [PATCH 3/7] fix types --- src/services/refact.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/refact.ts b/src/services/refact.ts index f8626854..9d294274 100644 --- a/src/services/refact.ts +++ b/src/services/refact.ts @@ -159,7 +159,8 @@ export function isStatisticDataResponse( json: unknown, ): json is { data: string } { if (!json || typeof json !== "object") return false; - return typeof (json as { data?: unknown }).data === "string"; + if (!("data" in json)) return false; + return typeof json.data === "string"; } export async function getStatisticData( From 5f1cd81a1cbd1ba4424df29b6b45b6972531a674 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Mon, 26 Feb 2024 23:32:27 +0100 Subject: [PATCH 4/7] added error callout when fails getting data --- .../StatisticView/StatisticView.tsx | 47 ++++++++++++++++++ src/events/statistic.ts | 17 ++++++- src/features/Statistic.tsx | 49 ++++--------------- src/hooks/useEventBusForHost.ts | 1 - src/hooks/useEventBusForStatistic.ts | 8 ++- 5 files changed, 80 insertions(+), 42 deletions(-) create mode 100644 src/components/StatisticView/StatisticView.tsx diff --git a/src/components/StatisticView/StatisticView.tsx b/src/components/StatisticView/StatisticView.tsx new file mode 100644 index 00000000..9904fd45 --- /dev/null +++ b/src/components/StatisticView/StatisticView.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { Box, Flex, Heading } from "@radix-ui/themes"; +import { Table } from "../Table/Table"; +import { Chart } from "../Chart/Chart"; +import { StatisticData } from "../../services/refact"; +import { Spinner } from "../Spinner"; +import { ErrorCallout } from "../Callout"; + +export const StatisticView: React.FC<{ + statisticData: StatisticData | null; + isLoading: boolean; + error: string; +}> = ({ statisticData, isLoading, error }) => { + if (isLoading) { + return ; + } + + if (error || !statisticData) { + return {error}; + } + return ( + + + + Statistics + + +
+ + + + + ); +}; diff --git a/src/events/statistic.ts b/src/events/statistic.ts index 78f52ea5..e089c6dc 100644 --- a/src/events/statistic.ts +++ b/src/events/statistic.ts @@ -10,7 +10,7 @@ export enum EVENT_NAMES_TO_STATISTIC { interface BaseAction { type: EVENT_NAMES_FROM_STATISTIC | EVENT_NAMES_TO_STATISTIC; - payload?: { data: string }; + payload?: { data?: string; [key: string]: unknown }; } export interface ActionToStatistic extends BaseAction { @@ -46,3 +46,18 @@ export function isReceiveDataForStatistic( if (!isActionToStatistic(action)) return false; return action.type === EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA; } + +export interface ReceiveDataForStatisticError extends ActionToStatistic { + type: EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA_ERROR; + payload: { + data: string; + message: string; + }; +} + +export function isReceiveDataForStatisticError( + action: unknown, +): action is ReceiveDataForStatisticError { + if (!isActionToStatistic(action)) return false; + return action.type === EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA_ERROR; +} diff --git a/src/features/Statistic.tsx b/src/features/Statistic.tsx index 63f6f95a..def84686 100644 --- a/src/features/Statistic.tsx +++ b/src/features/Statistic.tsx @@ -1,19 +1,17 @@ import React, { useEffect, useState } from "react"; -import { Box, Flex, Button, Heading, Responsive } from "@radix-ui/themes"; -import { Table } from "../components/Table/Table"; -import { Chart } from "../components/Chart/Chart"; -import { Spinner } from "../components/Spinner"; +import { Flex, Button, Responsive } from "@radix-ui/themes"; import { ArrowLeftIcon } from "@radix-ui/react-icons"; import { useConfig } from "../contexts/config-context"; import { ScrollArea } from "../components/ScrollArea"; import { useEventBusForStatistic } from "../hooks"; +import { StatisticView } from "../components/StatisticView/StatisticView"; export const Statistic: React.FC<{ onCloseStatistic?: () => void; }> = ({ onCloseStatistic }) => { const [isLoading, setIsLoading] = useState(true); const { host, tabbed } = useConfig(); - const { backFromStatistic, statisticData } = useEventBusForStatistic(); + const { backFromStatistic, statisticData, error } = useEventBusForStatistic(); const LeftRightPadding: Responsive< "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" > = @@ -35,10 +33,10 @@ export const Statistic: React.FC<{ }; useEffect(() => { - if (statisticData) { + if (statisticData ?? error) { setIsLoading(false); } - }, [statisticData]); + }, [statisticData, error]); return ( - {isLoading ? ( - - ) : ( - - - - Statistics - - {statisticData !== null && ( - -
- - - )} - - - )} + diff --git a/src/hooks/useEventBusForHost.ts b/src/hooks/useEventBusForHost.ts index 0ca09c49..778fe476 100644 --- a/src/hooks/useEventBusForHost.ts +++ b/src/hooks/useEventBusForHost.ts @@ -122,7 +122,6 @@ export function useEventBusForHost() { ); }); } - if (isRequestDataForStatistic(event.data)) { getStatisticData(lspUrl) .then((data) => { diff --git a/src/hooks/useEventBusForStatistic.ts b/src/hooks/useEventBusForStatistic.ts index 62bc36db..5bc5cbec 100644 --- a/src/hooks/useEventBusForStatistic.ts +++ b/src/hooks/useEventBusForStatistic.ts @@ -2,6 +2,7 @@ import { EVENT_NAMES_FROM_STATISTIC, EVENT_NAMES_TO_STATISTIC, isReceiveDataForStatistic, + isReceiveDataForStatisticError, } from "../events"; import { usePostMessage } from "./usePostMessage"; import { useEffect, useState } from "react"; @@ -12,6 +13,7 @@ export const useEventBusForStatistic = () => { const [statisticData, setStatisticData] = useState( null, ); + const [error, setError] = useState(""); const backFromStatistic = () => { postMessage({ @@ -22,12 +24,15 @@ export const useEventBusForStatistic = () => { useEffect(() => { const listener = (event: MessageEvent) => { if (isReceiveDataForStatistic(event.data)) { - if (event.data.payload !== undefined) { + if (event.data.payload?.data !== undefined) { const parsedData = JSON.parse( event.data.payload.data, ) as StatisticData; setStatisticData(parsedData); + setError(""); } + } else if (isReceiveDataForStatisticError(event.data)) { + setError(event.data.payload.message); } }; @@ -51,5 +56,6 @@ export const useEventBusForStatistic = () => { return { backFromStatistic, statisticData, + error, }; }; From f5c3715de7c7cea04f02e9ef569465ba507391d2 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Mon, 26 Feb 2024 23:34:28 +0100 Subject: [PATCH 5/7] fix spaces --- src/components/StatisticView/StatisticView.tsx | 1 + src/hooks/useEventBusForHost.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/StatisticView/StatisticView.tsx b/src/components/StatisticView/StatisticView.tsx index 9904fd45..89ee5195 100644 --- a/src/components/StatisticView/StatisticView.tsx +++ b/src/components/StatisticView/StatisticView.tsx @@ -18,6 +18,7 @@ export const StatisticView: React.FC<{ if (error || !statisticData) { return {error}; } + return ( { From fcfd69233916ec7dac5c35ebe78ab1f8603134ca Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Tue, 27 Feb 2024 02:01:17 +0100 Subject: [PATCH 6/7] add functionality for updating statistic data every hour --- src/events/statistic.ts | 1 + src/features/Statistic.tsx | 17 ++-- src/hooks/useEventBusForStatistic.ts | 123 ++++++++++++++++++++++----- 3 files changed, 107 insertions(+), 34 deletions(-) diff --git a/src/events/statistic.ts b/src/events/statistic.ts index e089c6dc..8811d976 100644 --- a/src/events/statistic.ts +++ b/src/events/statistic.ts @@ -6,6 +6,7 @@ export enum EVENT_NAMES_TO_STATISTIC { REQUEST_STATISTIC_DATA = "request_statistic_data", RECEIVE_STATISTIC_DATA = "receive_statistic_data", RECEIVE_STATISTIC_DATA_ERROR = "receive_statistic_data_error", + SET_LOADING_STATISTIC_DATA = "set_loading_statistic_data", } interface BaseAction { diff --git a/src/features/Statistic.tsx b/src/features/Statistic.tsx index def84686..0dfe2b96 100644 --- a/src/features/Statistic.tsx +++ b/src/features/Statistic.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { Flex, Button, Responsive } from "@radix-ui/themes"; import { ArrowLeftIcon } from "@radix-ui/react-icons"; import { useConfig } from "../contexts/config-context"; @@ -9,9 +9,8 @@ import { StatisticView } from "../components/StatisticView/StatisticView"; export const Statistic: React.FC<{ onCloseStatistic?: () => void; }> = ({ onCloseStatistic }) => { - const [isLoading, setIsLoading] = useState(true); const { host, tabbed } = useConfig(); - const { backFromStatistic, statisticData, error } = useEventBusForStatistic(); + const { backFromStatistic, state } = useEventBusForStatistic(); const LeftRightPadding: Responsive< "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" > = @@ -32,12 +31,6 @@ export const Statistic: React.FC<{ initial: "5", }; - useEffect(() => { - if (statisticData ?? error) { - setIsLoading(false); - } - }, [statisticData, error]); - return ( diff --git a/src/hooks/useEventBusForStatistic.ts b/src/hooks/useEventBusForStatistic.ts index 5bc5cbec..0ae2a70b 100644 --- a/src/hooks/useEventBusForStatistic.ts +++ b/src/hooks/useEventBusForStatistic.ts @@ -1,19 +1,72 @@ import { + ActionToStatistic, EVENT_NAMES_FROM_STATISTIC, EVENT_NAMES_TO_STATISTIC, isReceiveDataForStatistic, isReceiveDataForStatisticError, } from "../events"; import { usePostMessage } from "./usePostMessage"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useReducer } from "react"; import { StatisticData } from "../services/refact"; +type StatisticState = { + statisticData: StatisticData | null; + isLoading: boolean; + error: string; +}; + +function createInitialState(): StatisticState { + return { + statisticData: null, + isLoading: true, + error: "", + }; +} + +const initialState = createInitialState(); + +function reducer( + state: StatisticState, + action: ActionToStatistic, +): StatisticState { + switch (action.type) { + case EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA: + return { + ...state, + isLoading: true, + error: "", + }; + case EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA: + return { + ...state, + statisticData: action.payload + ? (action.payload as StatisticData) + : null, + isLoading: false, + error: "", + }; + case EVENT_NAMES_TO_STATISTIC.SET_LOADING_STATISTIC_DATA: + return { + ...state, + isLoading: !!action.payload, + }; + case EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA_ERROR: + return { + ...state, + error: + typeof action.payload?.message === "string" + ? action.payload.message + : "", + isLoading: false, + }; + default: + return state; + } +} + export const useEventBusForStatistic = () => { const postMessage = usePostMessage(); - const [statisticData, setStatisticData] = useState( - null, - ); - const [error, setError] = useState(""); + const [state, dispatch] = useReducer(reducer, initialState); const backFromStatistic = () => { postMessage({ @@ -21,41 +74,67 @@ export const useEventBusForStatistic = () => { }); }; + const fetchData = useCallback(() => { + dispatch({ + type: EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA, + }); + postMessage({ + type: EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA, + }); + }, [postMessage]); + useEffect(() => { const listener = (event: MessageEvent) => { if (isReceiveDataForStatistic(event.data)) { if (event.data.payload?.data !== undefined) { - const parsedData = JSON.parse( + const parsedStatisticData = JSON.parse( event.data.payload.data, ) as StatisticData; - setStatisticData(parsedData); - setError(""); + dispatch({ + type: EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA, + payload: parsedStatisticData, + }); + localStorage.setItem( + "statisticData", + JSON.stringify(parsedStatisticData), + ); + dispatch({ + type: EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA_ERROR, + payload: { message: "" }, + }); } } else if (isReceiveDataForStatisticError(event.data)) { - setError(event.data.payload.message); + dispatch({ + type: EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA_ERROR, + payload: { message: event.data.payload.message }, + }); } }; window.addEventListener("message", listener); - return () => { - window.removeEventListener("message", listener); - }; - }, []); + const cachedStatisticData = localStorage.getItem("statisticData"); - useEffect(() => { - const requestStatisticData = () => { - postMessage({ - type: EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA, + if (cachedStatisticData) { + const parsedStatisticData = JSON.parse( + cachedStatisticData, + ) as StatisticData; + dispatch({ + type: EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA, + payload: parsedStatisticData, }); - }; + } else { + fetchData(); + } + setInterval(fetchData, 3600000); - requestStatisticData(); - }, [postMessage]); + return () => { + window.removeEventListener("message", listener); + }; + }, [fetchData, postMessage]); return { backFromStatistic, - statisticData, - error, + state, }; }; From 99a565c526464c52649883a01c928b327a549203 Mon Sep 17 00:00:00 2001 From: Viktoriia Date: Tue, 27 Feb 2024 23:08:47 +0100 Subject: [PATCH 7/7] replaced switch in reducer with typeguards --- src/events/statistic.ts | 12 +++++ src/hooks/useEventBusForStatistic.ts | 69 +++++++++++++++------------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/events/statistic.ts b/src/events/statistic.ts index 8811d976..6849228f 100644 --- a/src/events/statistic.ts +++ b/src/events/statistic.ts @@ -17,10 +17,15 @@ interface BaseAction { export interface ActionToStatistic extends BaseAction { type: EVENT_NAMES_TO_STATISTIC; } + export interface RequestDataForStatistic extends ActionToStatistic { type: EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA; } +export interface SetLoadingStatisticData extends ActionToStatistic { + type: EVENT_NAMES_TO_STATISTIC.SET_LOADING_STATISTIC_DATA; +} + export function isActionToStatistic( action: unknown, ): action is ActionToStatistic { @@ -62,3 +67,10 @@ export function isReceiveDataForStatisticError( if (!isActionToStatistic(action)) return false; return action.type === EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA_ERROR; } + +export function isSetLoadingStatisticData( + action: unknown, +): action is SetLoadingStatisticData { + if (!isActionToStatistic(action)) return false; + return action.type === EVENT_NAMES_TO_STATISTIC.SET_LOADING_STATISTIC_DATA; +} diff --git a/src/hooks/useEventBusForStatistic.ts b/src/hooks/useEventBusForStatistic.ts index 0ae2a70b..06780794 100644 --- a/src/hooks/useEventBusForStatistic.ts +++ b/src/hooks/useEventBusForStatistic.ts @@ -4,6 +4,8 @@ import { EVENT_NAMES_TO_STATISTIC, isReceiveDataForStatistic, isReceiveDataForStatisticError, + isRequestDataForStatistic, + isSetLoadingStatisticData, } from "../events"; import { usePostMessage } from "./usePostMessage"; import { useCallback, useEffect, useReducer } from "react"; @@ -29,39 +31,42 @@ function reducer( state: StatisticState, action: ActionToStatistic, ): StatisticState { - switch (action.type) { - case EVENT_NAMES_TO_STATISTIC.REQUEST_STATISTIC_DATA: - return { - ...state, - isLoading: true, - error: "", - }; - case EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA: - return { - ...state, - statisticData: action.payload - ? (action.payload as StatisticData) - : null, - isLoading: false, - error: "", - }; - case EVENT_NAMES_TO_STATISTIC.SET_LOADING_STATISTIC_DATA: - return { - ...state, - isLoading: !!action.payload, - }; - case EVENT_NAMES_TO_STATISTIC.RECEIVE_STATISTIC_DATA_ERROR: - return { - ...state, - error: - typeof action.payload?.message === "string" - ? action.payload.message - : "", - isLoading: false, - }; - default: - return state; + if (isRequestDataForStatistic(action)) { + return { + ...state, + isLoading: true, + error: "", + }; + } + + if (isReceiveDataForStatistic(action)) { + return { + ...state, + statisticData: action.payload ? (action.payload as StatisticData) : null, + isLoading: false, + error: "", + }; } + + if (isSetLoadingStatisticData(action)) { + return { + ...state, + isLoading: !!action.payload, + }; + } + + if (isReceiveDataForStatisticError(action)) { + return { + ...state, + error: + typeof action.payload.message === "string" + ? action.payload.message + : "", + isLoading: false, + }; + } + + return state; } export const useEventBusForStatistic = () => {