- web/lib/watchlist.ts: KEY=stockchart.watchlist, storage+custom event 구독 - StarButton: 종목 페이지 헤더에서 ☆/★ 토글 - /watchlist 페이지: 카드 그리드 + 현재가/전일대비 + ✕ 제거 - DisclosuresPanel: 기존 /api/news?source=dart 재사용 (백엔드 신설 없음) - 종목 페이지 하단에 공시 패널 부착, 홈에 ★ 관심종목 링크 추가 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
65 lines
1.9 KiB
TypeScript
65 lines
1.9 KiB
TypeScript
// 관심종목 — localStorage 기반. 로그인 없이 토스의 '내 종목' 느낌.
|
|
// 단순 string[] 직렬화. 다중 탭 동기화는 'storage' 이벤트로 처리.
|
|
|
|
const KEY = "stockchart.watchlist";
|
|
|
|
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("watchlist:change"));
|
|
}
|
|
|
|
export const watchlist = {
|
|
get(): string[] {
|
|
return read();
|
|
},
|
|
has(code: string): boolean {
|
|
return read().includes(code);
|
|
},
|
|
add(code: string): void {
|
|
const cur = read();
|
|
if (cur.includes(code)) return;
|
|
write([code, ...cur]);
|
|
},
|
|
remove(code: string): void {
|
|
const cur = read();
|
|
write(cur.filter((c) => c !== code));
|
|
},
|
|
toggle(code: string): boolean {
|
|
const cur = read();
|
|
if (cur.includes(code)) {
|
|
write(cur.filter((c) => c !== code));
|
|
return false;
|
|
}
|
|
write([code, ...cur]);
|
|
return true;
|
|
},
|
|
// 컴포넌트가 변경 구독. unsubscribe 함수 반환.
|
|
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("watchlist:change", onCustom as EventListener);
|
|
return () => {
|
|
window.removeEventListener("storage", onStorage);
|
|
window.removeEventListener("watchlist:change", onCustom as EventListener);
|
|
};
|
|
},
|
|
};
|