refactor: StockList
This commit is contained in:
		
							parent
							
								
									ea611f0cdc
								
							
						
					
					
						commit
						454850c6b3
					
				
					 3 changed files with 169 additions and 127 deletions
				
			
		| 
						 | 
				
			
			@ -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,78 +40,29 @@ function ToggleButton(props: ToggleButtonProps) {
 | 
			
		|||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type QueryStatus<T> = {
 | 
			
		||||
  type: "loading";
 | 
			
		||||
} | {
 | 
			
		||||
  type: "complete";
 | 
			
		||||
  data: T;
 | 
			
		||||
} | {
 | 
			
		||||
  type: "error";
 | 
			
		||||
  err: Error;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function useAsync<T>(fn: () => Promise<T>): Signal<QueryStatus<T>> {
 | 
			
		||||
  const state = useSignal({
 | 
			
		||||
    type: "loading",
 | 
			
		||||
  } as QueryStatus<T>);
 | 
			
		||||
  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 {
 | 
			
		||||
function StockListByDate(
 | 
			
		||||
  { prevSet, rows, name }: {
 | 
			
		||||
    prevSet: Set<string>;
 | 
			
		||||
    rows: Coperation[];
 | 
			
		||||
    name: string;
 | 
			
		||||
  description: string;
 | 
			
		||||
  corpListByDate: Record<string, Coperation[]>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface CorpSimple {
 | 
			
		||||
  code: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function StockListByDate({prevSet, rows, name}:{prevSet:Set<string>, 
 | 
			
		||||
  rows:Coperation[],
 | 
			
		||||
name: string}){
 | 
			
		||||
  },
 | 
			
		||||
) {
 | 
			
		||||
  const lastCount = useRef(rows.length);
 | 
			
		||||
  const curCount = rows.length;
 | 
			
		||||
  const parent = useRef<HTMLDivElement>(null);
 | 
			
		||||
  const controller = useRef<{
 | 
			
		||||
    isEnabled: ()=> boolean,
 | 
			
		||||
    disable: ()=>void,
 | 
			
		||||
    enable: ()=> void
 | 
			
		||||
  }| undefined>();
 | 
			
		||||
  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");
 | 
			
		||||
      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;
 | 
			
		||||
| 
						 | 
				
			
			@ -113,18 +73,18 @@ name: string}){
 | 
			
		|||
  useLayoutEffect(() => {
 | 
			
		||||
    if (controller.current) {
 | 
			
		||||
      if (Math.abs(curCount - lastCount.current) > 200) {
 | 
			
		||||
        console.log('disable animation', curCount, "from",  lastCount.current);
 | 
			
		||||
        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 <div ref={parent}>
 | 
			
		||||
  return (
 | 
			
		||||
    <div ref={parent}>
 | 
			
		||||
      <h2 class="text-lg">{name}</h2>
 | 
			
		||||
      {rows.map((row) => {
 | 
			
		||||
        const firstOccur = !prevSet.has(row.Code);
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +103,7 @@ name: string}){
 | 
			
		|||
        );
 | 
			
		||||
      })}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function StockList({ data }: { data: PageCorpsInfo }) {
 | 
			
		||||
| 
						 | 
				
			
			@ -159,14 +120,13 @@ function StockList({ data }: { data: PageCorpsInfo }) {
 | 
			
		|||
        const prevSet = i == 0 ? new Set<string>() : sets[i - 1];
 | 
			
		||||
        const rows = corpListByDate[x];
 | 
			
		||||
        return (
 | 
			
		||||
          <StockListByDate key={x} name={x} prevSet={prevSet} rows={rows}></StockListByDate>
 | 
			
		||||
          <StockListByDate key={x} name={x} prevSet={prevSet} rows={rows} />
 | 
			
		||||
        );
 | 
			
		||||
      })}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		|||
                    <p>File Loading Failed</p>
 | 
			
		||||
                  </div>
 | 
			
		||||
                )
 | 
			
		||||
                : <StockList data={applyFilter(sig.value.data[0], sig.value.data[1], sig.value.data[2])}></StockList>}
 | 
			
		||||
                : (
 | 
			
		||||
                  <StockList
 | 
			
		||||
                    data={applyFilter(
 | 
			
		||||
                      sig.value.data[0],
 | 
			
		||||
                      sig.value.data[1],
 | 
			
		||||
                      sig.value.data[2],
 | 
			
		||||
                    )}
 | 
			
		||||
                  />
 | 
			
		||||
                )}
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
  function applyFilter(data: PageCorpsInfo, kospi: CorpSimple[], kosdaq: CorpSimple[]): PageCorpsInfo{
 | 
			
		||||
  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[]) => {
 | 
			
		||||
        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
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
        items: kospi,
 | 
			
		||||
      }, {
 | 
			
		||||
        include: viewKosdaq.value,
 | 
			
		||||
          items: kosdaq
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
        items: kosdaq,
 | 
			
		||||
      }],
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										38
									
								
								util/api.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								util/api.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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<string, Coperation[]>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CorpSimple {
 | 
			
		||||
  code: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function fetchPageInfo(pageName: string): Promise<PageCorpsInfo>{
 | 
			
		||||
    const res = await fetch("/api/pages/" + encodeURIComponent(pageName));
 | 
			
		||||
    return await res.json();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function fetchKospiList(): Promise<CorpSimple[]>{
 | 
			
		||||
    const res = await fetch("/api/kospi");
 | 
			
		||||
    return await res.json();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function fetchKosdaqList(): Promise<CorpSimple[]> {
 | 
			
		||||
    const res = await fetch("/api/kosdaq");
 | 
			
		||||
    return await res.json();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								util/util.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								util/util.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
import { Signal, useSignal } from "@preact/signals";
 | 
			
		||||
import { useEffect } from "preact/hooks";
 | 
			
		||||
 | 
			
		||||
export type QueryStatus<T> = {
 | 
			
		||||
    type: "loading";
 | 
			
		||||
  } | {
 | 
			
		||||
    type: "complete";
 | 
			
		||||
    data: T;
 | 
			
		||||
  } | {
 | 
			
		||||
    type: "error";
 | 
			
		||||
    err: Error;
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
export function useAsync<T>(fn: () => Promise<T>): Signal<QueryStatus<T>> {
 | 
			
		||||
    const state = useSignal({
 | 
			
		||||
      type: "loading",
 | 
			
		||||
    } as QueryStatus<T>);
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      (async () => {
 | 
			
		||||
        try {
 | 
			
		||||
          const data = await fn();
 | 
			
		||||
          state.value = {
 | 
			
		||||
            type: "complete",
 | 
			
		||||
            data: data,
 | 
			
		||||
          };
 | 
			
		||||
        } catch (err) {
 | 
			
		||||
          state.value = {
 | 
			
		||||
            type: "error",
 | 
			
		||||
            err: err,
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
      })();
 | 
			
		||||
    }, []);
 | 
			
		||||
    return state;
 | 
			
		||||
  }
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue