From 454850c6b354665a30d313f60b6ba5d643200659 Mon Sep 17 00:00:00 2001 From: monoid Date: Fri, 28 Jul 2023 17:52:07 +0900 Subject: [PATCH] refactor: StockList --- islands/StockList.tsx | 223 ++++++++++++++++++------------------------ util/api.ts | 38 +++++++ util/util.ts | 35 +++++++ 3 files changed, 169 insertions(+), 127 deletions(-) create mode 100644 util/api.ts create mode 100644 util/util.ts diff --git a/islands/StockList.tsx b/islands/StockList.tsx index 8636fbd..ca37166 100644 --- a/islands/StockList.tsx +++ b/islands/StockList.tsx @@ -1,9 +1,18 @@ import { Button } from "../components/Button.tsx"; -import { useEffect, useRef, useLayoutEffect } from "preact/hooks"; +import { useEffect, useLayoutEffect, useRef } from "preact/hooks"; import { ComponentChildren } from "preact"; import { Signal, useSignal } from "@preact/signals"; import { IS_BROWSER } from "$fresh/runtime.ts"; import { mapValues } from "$std/collections/map_values.ts"; +import { useAsync } from "../util/util.ts"; +import { + Coperation, + CorpSimple, + fetchKosdaqList, + fetchKospiList, + fetchPageInfo, + PageCorpsInfo, +} from "../util/api.ts"; interface StockProps { pageName: string; @@ -31,118 +40,70 @@ function ToggleButton(props: ToggleButtonProps) { ); } -type QueryStatus = { - type: "loading"; -} | { - type: "complete"; - data: T; -} | { - type: "error"; - err: Error; -}; - -function useAsync(fn: () => Promise): Signal> { - const state = useSignal({ - type: "loading", - } as QueryStatus); - useEffect(() => { - (async () => { - try { - const data = await fn(); - state.value = { - type: "complete", - data: data, - }; - } catch (err) { - state.value = { - type: "error", - err: err, - }; - } - })(); - }, []); - return state; -} - -interface Coperation { - Name: string; - Code: string; - Sector: string; - Product: string; - ListingDay: string; - ClosingMonth: string; - Representative: string; - Homepage: string; - AddressArea: string; - LastUpdate: string; -} - -interface PageCorpsInfo { - name: string; - description: string; - corpListByDate: Record; -} - -interface CorpSimple { - code: string; - name: string; -} - -function StockListByDate({prevSet, rows, name}:{prevSet:Set, - rows:Coperation[], -name: string}){ +function StockListByDate( + { prevSet, rows, name }: { + prevSet: Set; + rows: Coperation[]; + name: string; + }, +) { const lastCount = useRef(rows.length); const curCount = rows.length; const parent = useRef(null); - const controller = useRef<{ - isEnabled: ()=> boolean, - disable: ()=>void, - enable: ()=> void - }| undefined>(); - useEffect(()=>{ - (async ()=>{ - console.log("animation mount on ",name); - const {default:autoAnimate} = await import("https://esm.sh/@formkit/auto-animate@0.7.0"); - if (parent.current){ + const controller = useRef< + { + isEnabled: () => boolean; + disable: () => void; + enable: () => void; + } | undefined + >(); + useEffect(() => { + (async () => { + console.log("animation mount on ", name); + const { default: autoAnimate } = await import( + "https://esm.sh/@formkit/auto-animate@0.7.0" + ); + if (parent.current) { const cntr = autoAnimate(parent.current); controller.current = cntr; } })(); - },[parent]); + }, [parent]); - useLayoutEffect(()=>{ - if (controller.current){ - if (Math.abs(curCount - lastCount.current) > 200){ - console.log('disable animation', curCount, "from", lastCount.current); + useLayoutEffect(() => { + if (controller.current) { + if (Math.abs(curCount - lastCount.current) > 200) { + console.log("disable animation", curCount, "from", lastCount.current); controller.current.disable(); - } - else { - console.log('enable animation', curCount, "from", lastCount.current); + } else { + console.log("enable animation", curCount, "from", lastCount.current); controller.current.enable(); } lastCount.current = curCount; } }, [parent, rows]); - return
-

{name}

- {rows.map((row) => { - const firstOccur = !prevSet.has(row.Code); - return ( - - ); - })} -
+ return ( +
+

{name}

+ {rows.map((row) => { + const firstOccur = !prevSet.has(row.Code); + return ( + + ); + })} +
+ ); } function StockList({ data }: { data: PageCorpsInfo }) { @@ -159,14 +120,13 @@ function StockList({ data }: { data: PageCorpsInfo }) { const prevSet = i == 0 ? new Set() : sets[i - 1]; const rows = corpListByDate[x]; return ( - + ); })} ); } - type FilterInfoOption = { list: { items: CorpSimple[]; @@ -193,17 +153,13 @@ function filterInfo(info: Coperation[], filterList: FilterInfoOption) { } export default function StockListUI(props: StockProps) { - const sig = useAsync<[PageCorpsInfo, CorpSimple[], CorpSimple[]]>(async () => { - const res = await Promise.all([ - fetch("/api/pages/" + encodeURIComponent(props.pageName)), - fetch("/api/kospi"), - fetch("/api/kosdaq"), - ]); - const corpsInfo = await res[0].json() as PageCorpsInfo; - const kospi = await res[1].json(); - const kosdaq = await res[2].json(); - return [corpsInfo, kospi, kosdaq]; - }); + const sig = useAsync<[PageCorpsInfo, CorpSimple[], CorpSimple[]]>(() => + Promise.all([ + fetchPageInfo(props.pageName), + fetchKospiList(), + fetchKosdaqList(), + ]) + ); const viewKospi = useSignal(true); const viewKosdaq = useSignal(false); const viewOtherwise = useSignal(false); @@ -227,34 +183,47 @@ export default function StockListUI(props: StockProps) {

File Loading Failed

) - : } + : ( + + )} )} ); - function applyFilter(data: PageCorpsInfo, kospi: CorpSimple[], kosdaq: CorpSimple[]): PageCorpsInfo{ - const filter = getFilters(kospi,kosdaq); + function applyFilter( + data: PageCorpsInfo, + kospi: CorpSimple[], + kosdaq: CorpSimple[], + ): PageCorpsInfo { + const filter = getFilters(kospi, kosdaq); return { name: data.name, description: data.description, - corpListByDate: mapValues(data.corpListByDate, (it: Coperation[])=>{ + corpListByDate: mapValues(data.corpListByDate, (it: Coperation[]) => { return filterInfo(it, filter); - }) - } + }), + }; } - function getFilters(kospi: CorpSimple[], kosdaq: CorpSimple[]): FilterInfoOption{ + function getFilters( + kospi: CorpSimple[], + kosdaq: CorpSimple[], + ): FilterInfoOption { return { otherwise: viewOtherwise.value, list: [{ - include: viewKospi.value, - items: kospi - }, - { - include: viewKosdaq.value, - items: kosdaq - } - ] - } + include: viewKospi.value, + items: kospi, + }, { + include: viewKosdaq.value, + items: kosdaq, + }], + }; } } diff --git a/util/api.ts b/util/api.ts new file mode 100644 index 0000000..8cc5eb0 --- /dev/null +++ b/util/api.ts @@ -0,0 +1,38 @@ +export interface Coperation { + Name: string; + Code: string; + Sector: string; + Product: string; + ListingDay: string; + ClosingMonth: string; + Representative: string; + Homepage: string; + AddressArea: string; + LastUpdate: string; +} + +export interface PageCorpsInfo { + name: string; + description: string; + corpListByDate: Record; +} + +export interface CorpSimple { + code: string; + name: string; +} + +export async function fetchPageInfo(pageName: string): Promise{ + const res = await fetch("/api/pages/" + encodeURIComponent(pageName)); + return await res.json(); +} + +export async function fetchKospiList(): Promise{ + const res = await fetch("/api/kospi"); + return await res.json(); +} + +export async function fetchKosdaqList(): Promise { + const res = await fetch("/api/kosdaq"); + return await res.json(); +} \ No newline at end of file diff --git a/util/util.ts b/util/util.ts new file mode 100644 index 0000000..35feca1 --- /dev/null +++ b/util/util.ts @@ -0,0 +1,35 @@ +import { Signal, useSignal } from "@preact/signals"; +import { useEffect } from "preact/hooks"; + +export type QueryStatus = { + type: "loading"; + } | { + type: "complete"; + data: T; + } | { + type: "error"; + err: Error; + }; + +export function useAsync(fn: () => Promise): Signal> { + const state = useSignal({ + type: "loading", + } as QueryStatus); + useEffect(() => { + (async () => { + try { + const data = await fn(); + state.value = { + type: "complete", + data: data, + }; + } catch (err) { + state.value = { + type: "error", + err: err, + }; + } + })(); + }, []); + return state; + } \ No newline at end of file