mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix: remove Android self-install updater (#1125)
* fix: remove Android self-install updater * fix: open Android updates in native browser
This commit is contained in:
@@ -250,8 +250,6 @@ describe('InterfaceSettings', () => {
|
||||
availableUpdate: {
|
||||
runtime: 'android',
|
||||
targetVersion: '9.9.9',
|
||||
assetName: '5chan-9.9.9.apk',
|
||||
downloadUrl: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9.apk',
|
||||
releaseUrl: 'https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const testState = vi.hoisted(() => ({
|
||||
androidDownloadAndInstallUpdateMock: vi.fn(),
|
||||
capacitorPlatform: 'web',
|
||||
browserOpenMock: vi.fn(),
|
||||
electronDownloadAndInstallUpdateMock: vi.fn(),
|
||||
electronGetPlatformMock: vi.fn(),
|
||||
fetchMock: vi.fn(),
|
||||
openMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@capacitor/core', () => ({
|
||||
@@ -14,9 +15,9 @@ vi.mock('@capacitor/core', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../plugins/app-updater', () => ({
|
||||
default: {
|
||||
downloadAndInstallUpdate: (options: unknown) => testState.androidDownloadAndInstallUpdateMock(options),
|
||||
vi.mock('@capacitor/browser', () => ({
|
||||
Browser: {
|
||||
open: (options: unknown) => testState.browserOpenMock(options),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -38,13 +39,16 @@ const loadModule = async () => {
|
||||
describe('app-update', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
testState.androidDownloadAndInstallUpdateMock.mockReset();
|
||||
testState.capacitorPlatform = 'web';
|
||||
testState.browserOpenMock.mockReset();
|
||||
testState.electronDownloadAndInstallUpdateMock.mockReset();
|
||||
testState.electronGetPlatformMock.mockReset();
|
||||
testState.fetchMock.mockReset();
|
||||
testState.openMock.mockReset();
|
||||
testState.openMock.mockReturnValue({ closed: false });
|
||||
vi.stubEnv('VITE_APP_VERSION', '0.8.1');
|
||||
vi.stubGlobal('fetch', testState.fetchMock);
|
||||
vi.stubGlobal('open', testState.openMock);
|
||||
window.electronApi = undefined;
|
||||
Object.defineProperty(navigator, 'serviceWorker', {
|
||||
configurable: true,
|
||||
@@ -103,14 +107,13 @@ describe('app-update', () => {
|
||||
applyAvailableAppUpdate({
|
||||
runtime: 'android',
|
||||
targetVersion: '9.9.9',
|
||||
assetName: '5chan-9.9.9.apk',
|
||||
downloadUrl: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9.apk',
|
||||
releaseUrl: 'https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9',
|
||||
}),
|
||||
).rejects.toThrow('App updates are disabled for this build');
|
||||
expect(isAppUpdateEnabled).toBe(false);
|
||||
expect(testState.fetchMock).not.toHaveBeenCalled();
|
||||
expect(testState.androidDownloadAndInstallUpdateMock).not.toHaveBeenCalled();
|
||||
expect(testState.browserOpenMock).not.toHaveBeenCalled();
|
||||
expect(testState.openMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('selects the matching electron release asset for the current desktop platform', async () => {
|
||||
@@ -267,17 +270,12 @@ describe('app-update', () => {
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('selects the latest android apk release asset', async () => {
|
||||
it('resolves Android updates to the GitHub release page', async () => {
|
||||
testState.capacitorPlatform = 'android';
|
||||
testState.fetchMock.mockResolvedValueOnce(
|
||||
createFetchResponse({
|
||||
tag_name: 'v9.9.9',
|
||||
assets: [
|
||||
{
|
||||
name: '5chan-9.9.9.apk',
|
||||
browser_download_url: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9.apk',
|
||||
},
|
||||
],
|
||||
assets: [],
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -287,23 +285,32 @@ describe('app-update', () => {
|
||||
expect(result).toEqual({
|
||||
runtime: 'android',
|
||||
targetVersion: '9.9.9',
|
||||
assetName: '5chan-9.9.9.apk',
|
||||
downloadUrl: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9.apk',
|
||||
releaseUrl: 'https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts configured local test asset hosts for native e2e builds', async () => {
|
||||
vi.stubEnv('VITE_APP_UPDATE_ALLOWED_DOWNLOAD_HOSTS', '10.0.2.2');
|
||||
testState.capacitorPlatform = 'android';
|
||||
it('accepts configured local test asset hosts for desktop e2e builds', async () => {
|
||||
vi.stubEnv('VITE_APP_UPDATE_ALLOWED_DOWNLOAD_HOSTS', '127.0.0.1');
|
||||
window.electronApi = {
|
||||
isElectron: true,
|
||||
getPlatform: () => testState.electronGetPlatformMock(),
|
||||
downloadAndInstallUpdate: (options) => testState.electronDownloadAndInstallUpdateMock(options),
|
||||
copyToClipboard: vi.fn(),
|
||||
automateUploadMedia: vi.fn(),
|
||||
} as Window['electronApi'];
|
||||
testState.electronGetPlatformMock.mockResolvedValue({
|
||||
platform: 'linux',
|
||||
arch: 'x64',
|
||||
version: 'v20.0.0',
|
||||
});
|
||||
testState.fetchMock.mockResolvedValueOnce(
|
||||
createFetchResponse({
|
||||
tag_name: 'v9.9.9',
|
||||
html_url: 'http://10.0.2.2:4010/releases/v9.9.9',
|
||||
html_url: 'http://127.0.0.1:4010/releases/v9.9.9',
|
||||
assets: [
|
||||
{
|
||||
name: '5chan-9.9.9.apk',
|
||||
browser_download_url: 'http://10.0.2.2:4010/assets/5chan-9.9.9.apk',
|
||||
name: '5chan-9.9.9-x64.AppImage',
|
||||
browser_download_url: 'http://127.0.0.1:4010/assets/5chan-9.9.9-x64.AppImage',
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -313,11 +320,11 @@ describe('app-update', () => {
|
||||
const result = await resolveAvailableAppUpdate();
|
||||
|
||||
expect(result).toEqual({
|
||||
runtime: 'android',
|
||||
runtime: 'electron',
|
||||
targetVersion: '9.9.9',
|
||||
assetName: '5chan-9.9.9.apk',
|
||||
downloadUrl: 'http://10.0.2.2:4010/assets/5chan-9.9.9.apk',
|
||||
releaseUrl: 'http://10.0.2.2:4010/releases/v9.9.9',
|
||||
assetName: '5chan-9.9.9-x64.AppImage',
|
||||
downloadUrl: 'http://127.0.0.1:4010/assets/5chan-9.9.9-x64.AppImage',
|
||||
releaseUrl: 'http://127.0.0.1:4010/releases/v9.9.9',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -341,7 +348,7 @@ describe('app-update', () => {
|
||||
expect(reloadMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('routes native update installs through the matching platform bridge', async () => {
|
||||
it('routes desktop update installs through the electron platform bridge', async () => {
|
||||
window.electronApi = {
|
||||
isElectron: true,
|
||||
getPlatform: () => testState.electronGetPlatformMock(),
|
||||
@@ -358,21 +365,26 @@ describe('app-update', () => {
|
||||
downloadUrl: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9-x64.Setup.exe',
|
||||
releaseUrl: 'https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9',
|
||||
});
|
||||
await applyAvailableAppUpdate({
|
||||
runtime: 'android',
|
||||
targetVersion: '9.9.9',
|
||||
assetName: '5chan-9.9.9.apk',
|
||||
downloadUrl: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9.apk',
|
||||
releaseUrl: 'https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9',
|
||||
});
|
||||
|
||||
expect(testState.electronDownloadAndInstallUpdateMock).toHaveBeenCalledWith({
|
||||
url: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9-x64.Setup.exe',
|
||||
fileName: '5chan-9.9.9-x64.Setup.exe',
|
||||
});
|
||||
expect(testState.androidDownloadAndInstallUpdateMock).toHaveBeenCalledWith({
|
||||
url: 'https://github.com/bitsocialnet/5chan/releases/download/v9.9.9/5chan-9.9.9.apk',
|
||||
fileName: '5chan-9.9.9.apk',
|
||||
expect(testState.browserOpenMock).not.toHaveBeenCalled();
|
||||
expect(testState.openMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('opens the GitHub release page when applying an Android update', async () => {
|
||||
const { applyAvailableAppUpdate } = await loadModule();
|
||||
await applyAvailableAppUpdate({
|
||||
runtime: 'android',
|
||||
targetVersion: '9.9.9',
|
||||
releaseUrl: 'https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9',
|
||||
});
|
||||
|
||||
expect(testState.browserOpenMock).toHaveBeenCalledWith({
|
||||
url: 'https://github.com/bitsocialnet/5chan/releases/tag/v9.9.9',
|
||||
});
|
||||
expect(testState.openMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
+39
-10
@@ -24,14 +24,22 @@ interface WebAppUpdateInfo {
|
||||
releaseUrl: string;
|
||||
}
|
||||
|
||||
interface NativeAppUpdateInfo {
|
||||
runtime: 'electron' | 'android';
|
||||
interface ElectronAppUpdateInfo {
|
||||
runtime: 'electron';
|
||||
targetVersion: string;
|
||||
assetName: string;
|
||||
downloadUrl: string;
|
||||
releaseUrl: string;
|
||||
}
|
||||
|
||||
interface AndroidAppUpdateInfo {
|
||||
runtime: 'android';
|
||||
targetVersion: string;
|
||||
releaseUrl: string;
|
||||
}
|
||||
|
||||
type NativeAppUpdateInfo = ElectronAppUpdateInfo | AndroidAppUpdateInfo;
|
||||
|
||||
type AvailableAppUpdate = WebAppUpdateInfo | NativeAppUpdateInfo;
|
||||
|
||||
const getAppRuntime = (): AppRuntime => {
|
||||
@@ -160,10 +168,21 @@ const fetchLatestReleaseUpdate = async (runtime: Extract<AppRuntime, 'electron'
|
||||
return null;
|
||||
}
|
||||
|
||||
const releaseUrl =
|
||||
typeof releaseData.html_url === 'string' && releaseData.html_url.trim().length > 0 ? releaseData.html_url : getReleaseUrl(releaseData.tag_name || targetVersion);
|
||||
|
||||
if (runtime === 'android') {
|
||||
return {
|
||||
runtime,
|
||||
targetVersion,
|
||||
releaseUrl,
|
||||
};
|
||||
}
|
||||
|
||||
const assets = Array.isArray(releaseData.assets)
|
||||
? releaseData.assets.filter((asset) => typeof asset?.name === 'string' && typeof asset?.browser_download_url === 'string')
|
||||
: [];
|
||||
const matchedAsset = runtime === 'android' ? assets.find((asset) => asset.name.toLowerCase().endsWith('.apk')) || null : await findMatchingElectronAsset(assets);
|
||||
const matchedAsset = await findMatchingElectronAsset(assets);
|
||||
|
||||
if (!matchedAsset || !isAllowedDownloadUrl(matchedAsset.browser_download_url)) {
|
||||
return null;
|
||||
@@ -174,8 +193,7 @@ const fetchLatestReleaseUpdate = async (runtime: Extract<AppRuntime, 'electron'
|
||||
targetVersion,
|
||||
assetName: matchedAsset.name,
|
||||
downloadUrl: matchedAsset.browser_download_url,
|
||||
releaseUrl:
|
||||
typeof releaseData.html_url === 'string' && releaseData.html_url.trim().length > 0 ? releaseData.html_url : getReleaseUrl(releaseData.tag_name || targetVersion),
|
||||
releaseUrl,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -204,6 +222,18 @@ const resolveAvailableAppUpdate = async (): Promise<AvailableAppUpdate | null> =
|
||||
return fetchLatestReleaseUpdate(runtime);
|
||||
};
|
||||
|
||||
const openReleasePage = (url: string): void => {
|
||||
const openedWindow = window.open(url, '_blank', 'noopener,noreferrer');
|
||||
if (!openedWindow) {
|
||||
window.location.assign(url);
|
||||
}
|
||||
};
|
||||
|
||||
const openAndroidReleasePage = async (url: string): Promise<void> => {
|
||||
const { Browser } = await import('@capacitor/browser');
|
||||
await Browser.open({ url });
|
||||
};
|
||||
|
||||
const applyAvailableAppUpdate = async (update: AvailableAppUpdate): Promise<void> => {
|
||||
if (!isAppUpdateEnabled) {
|
||||
throw new Error('App updates are disabled for this build');
|
||||
@@ -229,12 +259,11 @@ const applyAvailableAppUpdate = async (update: AvailableAppUpdate): Promise<void
|
||||
return;
|
||||
}
|
||||
|
||||
const { default: AppUpdater } = await import('../plugins/app-updater');
|
||||
await AppUpdater.downloadAndInstallUpdate({
|
||||
url: update.downloadUrl,
|
||||
fileName: update.assetName,
|
||||
await openAndroidReleasePage(update.releaseUrl).catch((error) => {
|
||||
console.error('Failed to open Android release page with native browser', error);
|
||||
openReleasePage(update.releaseUrl);
|
||||
});
|
||||
};
|
||||
|
||||
export type { AppRuntime, AvailableAppUpdate, NativeAppUpdateInfo, WebAppUpdateInfo };
|
||||
export type { AndroidAppUpdateInfo, AppRuntime, AvailableAppUpdate, ElectronAppUpdateInfo, NativeAppUpdateInfo, WebAppUpdateInfo };
|
||||
export { applyAvailableAppUpdate, fetchLatestStableVersion, getAppRuntime, isAppUpdateEnabled, isElectron, refreshServiceWorkerRegistration, resolveAvailableAppUpdate };
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { registerPlugin } from '@capacitor/core';
|
||||
|
||||
interface DownloadAndInstallUpdateOptions {
|
||||
url: string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
interface AppUpdaterPlugin {
|
||||
downloadAndInstallUpdate(options: DownloadAndInstallUpdateOptions): Promise<void>;
|
||||
}
|
||||
|
||||
const AppUpdater = registerPlugin<AppUpdaterPlugin>('AppUpdater');
|
||||
|
||||
export default AppUpdater;
|
||||
Reference in New Issue
Block a user