- web/lib/recent.ts: stockchart.recent KEY, push/get/clear/subscribe (storage + custom event) - [code]/page.tsx: 페이지 진입 시 recent.push(code) — 자동 트래킹 - web/components/RecentTiles.tsx: 홈 카드 그리드 (최대 10개, 2~5컬럼) - web/app/page.tsx: SearchBox 아래에 RecentTiles 배치 (빈 리스트면 자동 숨김) - TopNav: 검색어 비면서 포커스 시 최근 본 종목 popover 표시 + "지우기" 버튼 토스 "최근 본 종목" 톤 — 검색 빈 상태에서도 popover 가 비어 있지 않게. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
53 lines
1.5 KiB
TypeScript
53 lines
1.5 KiB
TypeScript
// 최근 본 종목 — localStorage 기반. 종목 페이지 진입 시 자동으로 push.
|
|
// 단순 string[] 직렬화. 최신이 앞쪽, 최대 MAX 개로 캡.
|
|
|
|
const KEY = "stockchart.recent";
|
|
const MAX = 10;
|
|
|
|
function read(): string[] {
|
|
if (typeof window === "undefined") return [];
|
|
try {
|
|
const raw = window.localStorage.getItem(KEY);
|
|
if (!raw) return [];
|
|
const v = JSON.parse(raw);
|
|
return Array.isArray(v) ? v.filter((s) => typeof s === "string") : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function write(codes: string[]) {
|
|
if (typeof window === "undefined") return;
|
|
window.localStorage.setItem(KEY, JSON.stringify(codes));
|
|
// 같은 탭은 storage 이벤트가 안 떠서 수동 dispatch.
|
|
window.dispatchEvent(new CustomEvent("recent:change"));
|
|
}
|
|
|
|
export const recent = {
|
|
get(): string[] {
|
|
return read();
|
|
},
|
|
push(code: string): void {
|
|
if (!code) return;
|
|
const cur = read().filter((c) => c !== code);
|
|
cur.unshift(code);
|
|
write(cur.slice(0, MAX));
|
|
},
|
|
clear(): void {
|
|
write([]);
|
|
},
|
|
subscribe(cb: () => void): () => void {
|
|
if (typeof window === "undefined") return () => {};
|
|
const onStorage = (e: StorageEvent) => {
|
|
if (e.key === KEY) cb();
|
|
};
|
|
const onCustom = () => cb();
|
|
window.addEventListener("storage", onStorage);
|
|
window.addEventListener("recent:change", onCustom as EventListener);
|
|
return () => {
|
|
window.removeEventListener("storage", onStorage);
|
|
window.removeEventListener("recent:change", onCustom as EventListener);
|
|
};
|
|
},
|
|
};
|