로딩 중…
diff --git a/app/src/components/AppUpdateGate.tsx b/app/src/components/AppUpdateGate.tsx index 0b5452b..4d7550f 100644 --- a/app/src/components/AppUpdateGate.tsx +++ b/app/src/components/AppUpdateGate.tsx @@ -1,50 +1,52 @@ import React, { useEffect, useState } from 'react'; +import { App } from '@capacitor/app'; import { Capacitor } from '@capacitor/core'; -import { promptAppUpdateIfNeeded } from '../services/appUpdate'; +import { promptAppUpdate } from '../services/appUpdate'; type Props = { children: React.ReactNode; }; -/** Android 네이티브 — 시작 시 서버 APK 버전 확인 (무음 스킵, 이후 설정에서 수동 확인) */ +const isAndroidNative = () => + Capacitor.isNativePlatform() && Capacitor.getPlatform() === 'android'; + +/** Android — 시작·복귀 시 서버 APK 버전 확인 */ export default function AppUpdateGate({ children }: Props) { - const [ready, setReady] = useState(() => - !Capacitor.isNativePlatform() || Capacitor.getPlatform() !== 'android', - ); + const [ready, setReady] = useState(() => !isAndroidNative()); useEffect(() => { - if (ready) return; + if (!isAndroidNative()) return; + let cancelled = false; - void (async () => { + + const runCheck = async () => { try { - const { checkForAppUpdate, openApkDownload } = await import('../services/appUpdate'); - const check = await checkForAppUpdate(); - if (!cancelled && check.updateAvailable && check.remoteVersion) { - const dismissed = sessionStorage.getItem(`gc_skip_update_${check.remoteVersion}`); - if (!dismissed) { - const ok = window.confirm( - `GoldenChart v${check.remoteVersion} 업데이트가 있습니다.\n` - + `(현재 v${check.currentVersion})\n\n지금 APK를 다운로드하시겠습니까?`, - ); - if (ok) { - await openApkDownload(check.release); - } else { - sessionStorage.setItem(`gc_skip_update_${check.remoteVersion}`, '1'); - } - } - } + await promptAppUpdate({ skipIfDismissed: true }); } catch { /* 네트워크 오류 — 앱 진입 허용 */ - } finally { - if (!cancelled) setReady(true); } + }; + + void (async () => { + await runCheck(); + if (!cancelled) setReady(true); })(); - return () => { cancelled = true; }; - }, [ready]); + + const sub = App.addListener('appStateChange', ({ isActive }) => { + if (isActive && !cancelled) { + void runCheck(); + } + }); + + return () => { + cancelled = true; + void sub.then(h => h.remove()); + }; + }, []); if (!ready) { return
가상매매·전략편집기·푸시 알림을 모바일에서 사용하세요.
+Android 휴대폰·태블릿에서 가상매매·전략·푸시 알림을 사용하세요.
로딩 중…
앱 정보를 불러오지 못했습니다.
'; diff --git a/frontend/src/components/AppDownloadModal.tsx b/frontend/src/components/AppDownloadModal.tsx index cadd167..8c7a2c5 100644 --- a/frontend/src/components/AppDownloadModal.tsx +++ b/frontend/src/components/AppDownloadModal.tsx @@ -107,12 +107,15 @@ const MobileTab: React.FC = () => {{info.version && v{info.version}} {info.sizeBytes != null && {formatBytes(info.sizeBytes)}} {info.updatedAt && {info.updatedAt}}
++ 휴대폰·태블릿(패드) 동일 APK +
Android에서 APK 다운로드 페이지가 열립니다. 다운로드 후 설치하세요.
{downloadUrl}
- iPad는 PC 프로그램 탭의 TestFlight 링크를 이용하세요. 이 탭의 QR·APK는 Android 전용입니다. -
> ); }; const PlatformCard: React.FC<{ - platform: 'mac' | 'windows' | 'android' | 'ipad'; + platform: 'mac' | 'windows' | 'android'; release?: DesktopAppPlatformReleaseDto; mobile?: MobileAppReleaseInfoDto | null; }> = ({ platform, release, mobile }) => { @@ -162,7 +162,6 @@ const PlatformCard: React.FC<{ mac: { label: 'macOS (Apple Silicon · Intel)', icon: '🍎', ext: '.dmg', isLink: false }, windows: { label: 'Windows (64-bit)', icon: '🪟', ext: '.exe', isLink: false }, android: { label: 'Android (패드 · 폰)', icon: '🤖', ext: '.apk', isLink: false }, - ipad: { label: 'iPad (TestFlight)', icon: '📱', ext: '', isLink: true }, } as const; const { label, icon, ext } = configs[platform]; @@ -203,43 +202,6 @@ const PlatformCard: React.FC<{ ); } - if (platform === 'ipad') { - const testFlightUrl = mobile?.iosTestFlightUrl?.trim(); - if (!mobile?.iosAvailable || !testFlightUrl) { - return ( -TestFlight 링크 준비 중
-PC 프로그램 정보 불러오는 중…
; if (error) return{error}
; @@ -534,7 +495,7 @@ const DesktopTab: React.FC<{ canBuildDesktop: boolean }> = ({ canBuildDesktop })
- macOS(.dmg), Windows(.exe), Android(.apk) 또는 iPad TestFlight 링크가 아직 준비되지 않았습니다.
+ macOS(.dmg), Windows(.exe) 또는 Android(.apk)가 아직 준비되지 않았습니다.
{canBuildDesktop
? '우측 상단 「앱 빌드」 버튼으로 Jenkins 빌드를 시작하세요.'
@@ -554,7 +515,6 @@ const DesktopTab: React.FC<{ canBuildDesktop: boolean }> = ({ canBuildDesktop })
main 최신 커밋 기준이며, 완료 후 설치 파일이 자동으로 배포됩니다.