Skip to content

Commit b791d1e

Browse files
committed
fix: 统一主应用更新检查行为
1 parent 870b185 commit b791d1e

7 files changed

Lines changed: 84 additions & 84 deletions

File tree

App.tsx

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,34 @@ import { useTheme } from './src/hooks/useTheme';
1414
import { setDynamicShortcuts } from 'shortcut';
1515
import { moveTaskToBack, setExcludeFromRecents } from 'native-util';
1616
import { networkAutoSwitchService } from './src/services/NetworkAutoSwitchService';
17-
import {
18-
isShareIntentUrl,
19-
parseQuickTileUrl,
20-
shouldEnableAutoUpdateCheck,
21-
} from './src/utils/appLaunch';
17+
18+
const QUICK_UPLOAD_URL = 'syncclipboard://quick-upload';
19+
const QUICK_DOWNLOAD_URL = 'syncclipboard://quick-download';
20+
21+
function parseQuickTileUrl(url: string | null): {
22+
isQuickTile: boolean;
23+
fromForeground: boolean;
24+
direction: SyncDirection;
25+
} {
26+
if (!url) return { isQuickTile: false, fromForeground: false, direction: SyncDirection.Download };
27+
const fromForeground = url.includes('fg=1');
28+
if (url.startsWith(QUICK_UPLOAD_URL)) {
29+
return { isQuickTile: true, fromForeground, direction: SyncDirection.Upload };
30+
}
31+
if (url.startsWith(QUICK_DOWNLOAD_URL)) {
32+
return { isQuickTile: true, fromForeground, direction: SyncDirection.Download };
33+
}
34+
return { isQuickTile: false, fromForeground: false, direction: SyncDirection.Download };
35+
}
36+
37+
function isShareIntentUrl(url: string | null): boolean {
38+
if (!url) return false;
39+
try {
40+
return new URL(url).hostname === 'expo-sharing';
41+
} catch {
42+
return false;
43+
}
44+
}
2245

2346
async function runOverlayNetworkPreflight(): Promise<void> {
2447
try {
@@ -32,7 +55,6 @@ type AppMode = 'checking' | 'home';
3255

3356
export default function App() {
3457
const [appMode, setAppMode] = useState<AppMode>('checking');
35-
const [autoUpdateCheckEnabled, setAutoUpdateCheckEnabled] = useState(false);
3658
// 快速操作覆盖层:始终以 overlay 形式显示,不卸载 AppNavigator/HomeScreen
3759
const [shareReceiveOverlay, setShareReceiveOverlay] = useState(false);
3860
const [quickActionOverlay, setQuickActionOverlay] = useState<{
@@ -91,9 +113,6 @@ export default function App() {
91113
await runOverlayNetworkPreflight();
92114
// fg=1 完成后留在 app,fg=0/无fg 完成后退出
93115
setQuickActionOverlay({ direction, exitAfterSync: !fromForeground });
94-
} else if (shouldEnableAutoUpdateCheck(url)) {
95-
// URL 解析完成且不是 overlay 冷启动,才允许首页自动检查更新。
96-
setAutoUpdateCheckEnabled(true);
97116
}
98117
});
99118

@@ -123,9 +142,7 @@ export default function App() {
123142
<ThemeProvider>
124143
<I18nProvider>
125144
<ThemedStatusBar />
126-
{appMode === 'checking' ? null : (
127-
<AppNavigator autoUpdateCheckEnabled={autoUpdateCheckEnabled} />
128-
)}
145+
{appMode === 'checking' ? null : <AppNavigator />}
129146
{shareReceiveOverlay && (
130147
<View style={StyleSheet.absoluteFill}>
131148
<ShareReceiveScreen

src/__tests__/UpdateService.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,43 @@ describe('UpdateService', () => {
150150
assets: updateResult.assets,
151151
});
152152
});
153+
154+
it('命中缓存并启动安装器后清空可更新状态', async () => {
155+
const dependencies = createDependencies({
156+
checkCache: jest.fn(async () => 'file:///cached-update.apk'),
157+
});
158+
const service = new UpdateService(dependencies);
159+
await service.checkForUpdates();
160+
161+
await expect(
162+
service.installCachedUpdate(updateResult.latestVersion, updateResult.assets)
163+
).resolves.toBe(true);
164+
expect(dependencies.install).toHaveBeenCalledWith('file:///cached-update.apk');
165+
expect(service.getState()).toMatchObject({
166+
updateAvailable: false,
167+
latestVersion: null,
168+
assets: [],
169+
releaseNotes: undefined,
170+
});
171+
});
172+
173+
it('缓存安装器启动失败时保留可重试的更新状态', async () => {
174+
const dependencies = createDependencies({
175+
checkCache: jest.fn(async () => 'file:///cached-update.apk'),
176+
install: jest.fn(async () => {
177+
throw new Error('installer unavailable');
178+
}),
179+
});
180+
const service = new UpdateService(dependencies);
181+
await service.checkForUpdates();
182+
183+
await expect(
184+
service.installCachedUpdate(updateResult.latestVersion, updateResult.assets)
185+
).rejects.toThrow('installer unavailable');
186+
expect(service.getState()).toMatchObject({
187+
updateAvailable: true,
188+
latestVersion: updateResult.latestVersion,
189+
assets: updateResult.assets,
190+
});
191+
});
153192
});

src/__tests__/appLaunch.test.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/navigation/AppNavigator.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,7 @@ const SettingsStackNavigator = () => {
7878
);
7979
};
8080

81-
interface AppNavigatorProps {
82-
autoUpdateCheckEnabled: boolean;
83-
}
84-
85-
export const AppNavigator = ({ autoUpdateCheckEnabled }: AppNavigatorProps) => {
81+
export const AppNavigator = () => {
8682
const { theme } = useTheme();
8783
const { t } = useTranslation();
8884

@@ -151,9 +147,7 @@ export const AppNavigator = ({ autoUpdateCheckEnabled }: AppNavigatorProps) => {
151147
},
152148
})}
153149
>
154-
<Tab.Screen name="Home" options={{ title: t('nav.home') }}>
155-
{() => <HomeScreen autoUpdateCheckEnabled={autoUpdateCheckEnabled} />}
156-
</Tab.Screen>
150+
<Tab.Screen name="Home" component={HomeScreen} options={{ title: t('nav.home') }} />
157151
<Tab.Screen
158152
name="History"
159153
component={HistoryScreen}

src/screens/HomeScreen.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ import { longRunningTaskManager } from '@/longRunningTask/LongRunningTaskManager
3737
import { updateService } from '@/services/update';
3838
import { useUpdateDialog } from '@/hooks/useUpdateDialog';
3939

40-
interface HomeScreenProps {
41-
autoUpdateCheckEnabled: boolean;
42-
}
43-
44-
export function HomeScreen({ autoUpdateCheckEnabled }: HomeScreenProps) {
40+
export function HomeScreen() {
4541
const { theme } = useTheme();
4642
const { t } = useTranslation();
4743
const navigation = useNavigation();
@@ -72,7 +68,7 @@ export function HomeScreen({ autoUpdateCheckEnabled }: HomeScreenProps) {
7268

7369
// 首页首次挂载后自动检查更新(默认每天一次)。
7470
useEffect(() => {
75-
if (!isLoaded || !autoUpdateCheckEnabled) return;
71+
if (!isLoaded) return;
7672
updateService
7773
.checkAutomatically()
7874
.then((result) => {
@@ -85,7 +81,7 @@ export function HomeScreen({ autoUpdateCheckEnabled }: HomeScreenProps) {
8581
console.warn('[HomeScreen] Auto update check failed:', error);
8682
}
8783
});
88-
}, [autoUpdateCheckEnabled, isLoaded, showUpdateDialog]);
84+
}, [isLoaded, showUpdateDialog]);
8985

9086
// 启动所有后台任务(先加载字体,再启动后台任务,避免后台繁重任务导致导航栏图标加载缓慢)
9187
useEffect(() => {

src/services/update/UpdateService.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export class UpdateService {
159159
if (!cached) return false;
160160

161161
await this.dependencies.install(cached);
162+
this.clearAvailableUpdate();
162163
return true;
163164
}
164165

@@ -199,12 +200,7 @@ export class UpdateService {
199200
this.throwIfCancelled(abortController, this.downloadAbortController);
200201

201202
await this.dependencies.install(fileUri);
202-
this.setState({
203-
updateAvailable: false,
204-
latestVersion: null,
205-
assets: [],
206-
releaseNotes: undefined,
207-
});
203+
this.clearAvailableUpdate();
208204
} finally {
209205
if (this.downloadAbortController === abortController) {
210206
this.downloadAbortController = null;
@@ -239,6 +235,15 @@ export class UpdateService {
239235
return findAssetForAbi(assets, preferredAbi);
240236
}
241237

238+
private clearAvailableUpdate(): void {
239+
this.setState({
240+
updateAvailable: false,
241+
latestVersion: null,
242+
assets: [],
243+
releaseNotes: undefined,
244+
});
245+
}
246+
242247
private throwIfCancelled(
243248
controller: AbortController,
244249
activeController: AbortController | null

src/utils/appLaunch.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)