mobile download

This commit is contained in:
Macbook
2026-05-28 14:44:19 +09:00
parent e2816b037f
commit 3503ef33f5
152 changed files with 11021 additions and 687 deletions
@@ -25,7 +25,8 @@ export type StrategyFlowLayoutStore = {
sell: SignalFlowLayoutSnapshot;
};
const STORAGE_KEY = 'gc_se_flow_layout_v1';
/** @deprecated 레거시 localStorage 키 — 신규 저장은 DB flow_layout_json만 사용 */
const LEGACY_STORAGE_KEY = 'gc_se_flow_layout_v1';
export function emptySignalFlowLayout(): SignalFlowLayoutSnapshot {
return {
@@ -42,44 +43,67 @@ export function emptyStrategyFlowLayout(): StrategyFlowLayoutStore {
return { buy: emptySignalFlowLayout(), sell: emptySignalFlowLayout() };
}
export function loadStrategyFlowLayout(strategyKey: string): StrategyFlowLayoutStore | null {
export function normalizeSignalFlowLayout(
snap: SignalFlowLayoutSnapshot | null | undefined,
): SignalFlowLayoutSnapshot {
if (!snap) return emptySignalFlowLayout();
return {
positions: snap.positions ?? {},
edgeHandles: snap.edgeHandles ?? {},
orphans: snap.orphans ?? [],
startMeta: snap.startMeta ?? defaultStartMeta(),
extraStartIds: snap.extraStartIds ?? [],
extraRoots: snap.extraRoots ?? {},
startCombineOp: snap.startCombineOp,
};
}
export function normalizeStrategyFlowLayout(
store: StrategyFlowLayoutStore | null | undefined,
): StrategyFlowLayoutStore | null {
if (!store) return null;
return {
buy: normalizeSignalFlowLayout(store.buy),
sell: normalizeSignalFlowLayout(store.sell),
};
}
export function buildStrategyFlowLayoutStore(
buy: Omit<SignalFlowLayoutSnapshot, 'orphans'>,
sell: Omit<SignalFlowLayoutSnapshot, 'orphans'>,
buyOrphans: LogicNode[],
sellOrphans: LogicNode[],
): StrategyFlowLayoutStore {
return {
buy: { ...buy, orphans: buyOrphans },
sell: { ...sell, orphans: sellOrphans },
};
}
/** DB에 flow layout이 없을 때만 1회 읽기 — 이후 저장은 서버로 이전 */
export function readLegacyFlowLayoutFromLocalStorage(
strategyKey: string,
): StrategyFlowLayoutStore | null {
try {
const raw = localStorage.getItem(STORAGE_KEY);
const raw = localStorage.getItem(LEGACY_STORAGE_KEY);
if (!raw) return null;
const all = JSON.parse(raw) as Record<string, StrategyFlowLayoutStore>;
return all[strategyKey] ?? null;
return normalizeStrategyFlowLayout(all[strategyKey] ?? null);
} catch {
return null;
}
}
export function saveStrategyFlowLayout(strategyKey: string, layout: StrategyFlowLayoutStore): void {
export function clearLegacyFlowLayoutFromLocalStorage(strategyKey: string): void {
try {
const raw = localStorage.getItem(STORAGE_KEY);
const all: Record<string, StrategyFlowLayoutStore> = raw ? JSON.parse(raw) : {};
all[strategyKey] = layout;
localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
} catch {
/* ignore quota / private mode */
}
}
export function deleteStrategyFlowLayout(strategyKey: string): void {
try {
const raw = localStorage.getItem(STORAGE_KEY);
const raw = localStorage.getItem(LEGACY_STORAGE_KEY);
if (!raw) return;
const all = JSON.parse(raw) as Record<string, StrategyFlowLayoutStore>;
if (!(strategyKey in all)) return;
delete all[strategyKey];
localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
if (Object.keys(all).length === 0) localStorage.removeItem(LEGACY_STORAGE_KEY);
else localStorage.setItem(LEGACY_STORAGE_KEY, JSON.stringify(all));
} catch {
/* ignore */
}
}
export function migrateStrategyFlowLayout(fromKey: string, toKey: string): void {
if (fromKey === toKey) return;
const layout = loadStrategyFlowLayout(fromKey);
if (!layout) return;
saveStrategyFlowLayout(toKey, layout);
deleteStrategyFlowLayout(fromKey);
}