테블릿 로딩 이슈 수정
This commit is contained in:
@@ -52,6 +52,14 @@ function getDeviceId(): string {
|
||||
return id;
|
||||
}
|
||||
|
||||
/** 정식 회원(userId) — 매매·전략·모의 API 필수 */
|
||||
export function hasRegisteredUser(): boolean {
|
||||
const id = localStorage.getItem('gc_user_id');
|
||||
if (!id) return false;
|
||||
const n = Number(id);
|
||||
return Number.isFinite(n) && n > 0;
|
||||
}
|
||||
|
||||
function authHeaders(): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -90,7 +98,12 @@ async function request<T>(path: string, init?: RequestInit): Promise<T | null> {
|
||||
});
|
||||
if (res.status === 204) return null;
|
||||
if (!res.ok) {
|
||||
console.error(`[backendApi] ${init?.method ?? 'GET'} ${path} → ${res.status}`);
|
||||
const method = init?.method ?? 'GET';
|
||||
const guest403 = res.status === 403 && !hasRegisteredUser();
|
||||
const missing404 = res.status === 404 && method === 'GET';
|
||||
if (!guest403 && !missing404) {
|
||||
console.error(`[backendApi] ${method} ${path} → ${res.status}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return (await res.json()) as T;
|
||||
@@ -759,6 +772,7 @@ export interface PaperOrderRequest {
|
||||
}
|
||||
|
||||
export async function loadPaperSummary(markPrices?: Record<string, number>): Promise<PaperSummaryDto | null> {
|
||||
if (!hasRegisteredUser()) return null;
|
||||
if (markPrices && Object.keys(markPrices).length > 0) {
|
||||
return request<PaperSummaryDto>('/paper/summary', {
|
||||
method: 'POST',
|
||||
@@ -1041,11 +1055,13 @@ export interface StrategyDto {
|
||||
|
||||
/** 전략 목록 로드 */
|
||||
export async function loadStrategies(): Promise<StrategyDto[]> {
|
||||
if (!hasRegisteredUser()) return [];
|
||||
return (await request<StrategyDto[]>('/strategies')) ?? [];
|
||||
}
|
||||
|
||||
/** 단일 전략 로드 */
|
||||
export async function loadStrategy(id: number): Promise<StrategyDto | null> {
|
||||
if (!hasRegisteredUser()) return null;
|
||||
return request<StrategyDto>(`/strategies/${id}`);
|
||||
}
|
||||
|
||||
@@ -1290,6 +1306,9 @@ export interface LiveStrategySettingsDto {
|
||||
export async function loadLiveStrategySettings(
|
||||
market: string,
|
||||
): Promise<LiveStrategySettingsDto> {
|
||||
if (!hasRegisteredUser()) {
|
||||
return { market, strategyId: null, isLiveCheck: false, executionType: 'CANDLE_CLOSE', positionMode: 'LONG_ONLY' };
|
||||
}
|
||||
return (await request<LiveStrategySettingsDto>(
|
||||
`/strategy/settings?market=${encodeURIComponent(market)}`,
|
||||
)) ?? { market, strategyId: null, isLiveCheck: false, executionType: 'CANDLE_CLOSE', positionMode: 'LONG_ONLY' };
|
||||
@@ -1317,6 +1336,7 @@ export function expandLiveStrategySubscriptions(
|
||||
|
||||
/** 현재 디바이스에서 실시간 체크 ON + 전략 지정된 종목 목록 */
|
||||
export async function loadActiveLiveStrategySettings(): Promise<LiveStrategySettingsDto[]> {
|
||||
if (!hasRegisteredUser()) return [];
|
||||
return (await request<LiveStrategySettingsDto[]>('/strategy/settings/active')) ?? [];
|
||||
}
|
||||
|
||||
@@ -1376,12 +1396,14 @@ export async function fetchLiveConditionStatus(
|
||||
|
||||
/** 전략 DSL에 포함된 평가 시간봉 목록 (실시간 체크·STOMP 구독용) */
|
||||
export async function loadStrategyTimeframes(strategyId: number): Promise<string[]> {
|
||||
if (!hasRegisteredUser()) return ['1m'];
|
||||
const list = await request<string[]>(`/strategies/${strategyId}/timeframes`);
|
||||
return list?.length ? list : ['1m'];
|
||||
}
|
||||
|
||||
/** DB DSL TIMEFRAME 래핑 보정 (3분봉 전략이 1m으로 평가되는 레거시 수정) */
|
||||
export async function repairStrategyTimeframes(strategyId: number): Promise<void> {
|
||||
if (!hasRegisteredUser()) return;
|
||||
await request(`/strategies/${strategyId}/repair-timeframes`, { method: 'POST' });
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ export async function syncStrategyTimeframesFromLayoutIfNeeded(
|
||||
strategy?: StrategyDto | null,
|
||||
): Promise<boolean> {
|
||||
const strat = (await loadStrategy(strategyId)) ?? strategy ?? null;
|
||||
if (!strat) return true;
|
||||
if (!strat) return false;
|
||||
|
||||
const uiTfs = collectUiEvaluationTimeframes(strategyId, strat);
|
||||
if (uiTfs.length === 0) return true;
|
||||
|
||||
@@ -64,6 +64,12 @@ function listenerCount(): number {
|
||||
return listeners.size;
|
||||
}
|
||||
|
||||
function mayOpenSocket(): boolean {
|
||||
if (listenerCount() === 0) return false;
|
||||
if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function cancelPendingDisconnect(): void {
|
||||
if (disconnectTimer) {
|
||||
clearTimeout(disconnectTimer);
|
||||
@@ -134,7 +140,7 @@ function dispatch(text: string): void {
|
||||
}
|
||||
|
||||
function scheduleReconnect(): void {
|
||||
if (listenerCount() === 0) return;
|
||||
if (!mayOpenSocket()) return;
|
||||
if (reconnectTimer) return;
|
||||
const delay = Math.min(RECONNECT_BASE * 2 ** retryCount, RECONNECT_MAX);
|
||||
retryCount += 1;
|
||||
@@ -236,6 +242,17 @@ export function subscribeUpbitWsConnection(listener: ConnectionListener): () =>
|
||||
return () => connectionListeners.delete(listener);
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
cancelPendingConnect();
|
||||
if (listenerCount() === 0) teardownSocket();
|
||||
return;
|
||||
}
|
||||
if (mayOpenSocket()) scheduleConnect();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 업비트 WS 채널 구독 (참조 카운트). codes 가 비어 있으면 구독하지 않음.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* ui_preferences.virtual.targets 가 비었을 때 서버·레거시에서 투자대상 복구
|
||||
*/
|
||||
import { loadActiveLiveStrategySettings, loadWatchlist } from './backendApi';
|
||||
import { hasRegisteredUser, loadActiveLiveStrategySettings, loadWatchlist } from './backendApi';
|
||||
import {
|
||||
loadVirtualSession,
|
||||
loadVirtualTargets,
|
||||
@@ -67,6 +67,8 @@ export async function hydrateVirtualTargetsIfEmpty(): Promise<VirtualTargetItem[
|
||||
const session = loadVirtualSession();
|
||||
const globalId = session.globalStrategyId;
|
||||
|
||||
if (!hasRegisteredUser()) return [];
|
||||
|
||||
try {
|
||||
const active = await loadActiveLiveStrategySettings();
|
||||
if (active.length > 0) return fromLiveSettings(active, globalId);
|
||||
|
||||
Reference in New Issue
Block a user