mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 13:35:42 +09:00
fix(desktop): shell-compatible module updates, no linux walls (#3452)
This commit is contained in:
1 parent
f501d34179
commit
959b03b5f4
73 files changed
+4142
-4311
No files matched your search
@@ -25,8 +25,7 @@ import {
|
||||
type UpdaterMachineEvent,
|
||||
type UpdaterMachineSnapshot,
|
||||
} from '@app/features/updater/state/UpdaterStateMachine';
|
||||
import {buildLinuxManualUpdateOptions} from '@app/features/updater/utils/LinuxManualUpdateOptions';
|
||||
import type {UpdaterEvent as NativeUpdaterEvent, UpdaterDownloadOption} from '@app/types/electron.d';
|
||||
import type {UpdaterEvent as NativeUpdaterEvent} from '@app/types/electron.d';
|
||||
import {OFFICIAL_WEB_APP_HOSTS} from '@fluxer/instance_bootstrap/src/OfficialWebAppHosts';
|
||||
import {makeAutoObservable, runInAction} from 'mobx';
|
||||
|
||||
@@ -41,7 +40,6 @@ type UpdaterEvent =
|
||||
context: UpdaterContext;
|
||||
version: string | null;
|
||||
downloadUrl?: string;
|
||||
downloadOptions?: Array<UpdaterDownloadOption>;
|
||||
}
|
||||
| {
|
||||
type: 'not-available';
|
||||
@@ -66,7 +64,6 @@ type UpdaterEvent =
|
||||
const logger = new Logger('Updater');
|
||||
const CHECK_INTERVAL_MS = 5 * 60 * 1000;
|
||||
const MIN_CHECK_INTERVAL_MS = 60 * 1000;
|
||||
const MANUAL_DOWNLOAD_REFRESH_TIMEOUT_MS = 5 * 1000;
|
||||
const WEB_CHECK_TIMEOUT_MS = 15 * 1000;
|
||||
const NATIVE_CHECK_TIMEOUT_MS = 30 * 1000;
|
||||
const VERSION_ENDPOINT = '/version.json';
|
||||
@@ -120,7 +117,6 @@ function normalizeUpdaterEvent(event: NativeUpdaterEvent): UpdaterEvent | null {
|
||||
context,
|
||||
version: event.version ?? null,
|
||||
downloadUrl: event.downloadUrl,
|
||||
downloadOptions: event.downloadOptions,
|
||||
};
|
||||
case 'not-available':
|
||||
return {type: 'not-available', context};
|
||||
@@ -151,7 +147,7 @@ class Updater {
|
||||
private snapshot: UpdaterMachineSnapshot = createUpdaterMachineSnapshot();
|
||||
currentVersion: string | null = null;
|
||||
channel: string | null = null;
|
||||
private desktopArch: string | null = null;
|
||||
private flatpakAppId: string | null = null;
|
||||
private isNative: boolean;
|
||||
private backgroundCheckStarted = false;
|
||||
private backgroundCheckInterval: number | null = null;
|
||||
@@ -164,7 +160,6 @@ class Updater {
|
||||
private olderShellUpdateDownloaded = false;
|
||||
private olderShellInstallWhenDownloaded = false;
|
||||
private desktopUpdateStarting = false;
|
||||
private pendingManualDownloadRefreshes = 0;
|
||||
private checkInProgress = false;
|
||||
|
||||
constructor() {
|
||||
@@ -192,10 +187,6 @@ class Updater {
|
||||
return this.snapshot.context.nativeManualDownloadUrl;
|
||||
}
|
||||
|
||||
get nativeManualDownloadOptions(): ReadonlyArray<UpdaterDownloadOption> {
|
||||
return this.snapshot.context.nativeManualDownloadOptions;
|
||||
}
|
||||
|
||||
private get nativeCheckFailed(): boolean {
|
||||
return this.snapshot.context.nativeCheckFailed;
|
||||
}
|
||||
@@ -260,11 +251,16 @@ class Updater {
|
||||
|
||||
private async bootstrapNative(): Promise<void> {
|
||||
try {
|
||||
const info = await getClientInfo();
|
||||
const [info, desktopInfo] = await Promise.all([
|
||||
getClientInfo(),
|
||||
getElectronAPI()
|
||||
?.getDesktopInfo()
|
||||
.catch(() => null),
|
||||
]);
|
||||
runInAction(() => {
|
||||
this.currentVersion = info.desktopVersion ?? null;
|
||||
this.channel = info.desktopChannel ?? null;
|
||||
this.desktopArch = info.desktopArch ?? info.arch ?? null;
|
||||
this.flatpakAppId = desktopInfo?.flatpak === true ? (desktopInfo.flatpakAppId ?? null) : null;
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warn('Failed to read desktop info', error);
|
||||
@@ -376,35 +372,22 @@ class Updater {
|
||||
}
|
||||
|
||||
private handleNativeEvent(event: UpdaterEvent): void {
|
||||
const isManualDownloadRefreshResult =
|
||||
event.context === 'user' &&
|
||||
this.pendingManualDownloadRefreshes > 0 &&
|
||||
(event.type === 'available' ||
|
||||
event.type === 'not-available' ||
|
||||
event.type === 'error' ||
|
||||
event.type === 'unsupported');
|
||||
if (isManualDownloadRefreshResult) {
|
||||
this.pendingManualDownloadRefreshes -= 1;
|
||||
}
|
||||
const isUserCheck = event.context === 'user' && !isManualDownloadRefreshResult;
|
||||
const isUserCheck = event.context === 'user';
|
||||
const shouldShowImmediateUserResult = isUserCheck && !this.checkInProgress;
|
||||
switch (event.type) {
|
||||
case 'checking':
|
||||
this.transition({type: 'check.started'});
|
||||
break;
|
||||
case 'available': {
|
||||
const manualDownloadOptions = this.resolveManualNativeDownloadOptions(event, event.downloadOptions ?? []);
|
||||
case 'available':
|
||||
this.transition({
|
||||
type: 'native.available',
|
||||
version: event.version ?? null,
|
||||
downloadUrl: manualDownloadOptions[0]?.url ?? event.downloadUrl ?? null,
|
||||
downloadOptions: manualDownloadOptions,
|
||||
downloadUrl: getElectronAPI()?.platform === 'linux' ? DESKTOP_DOWNLOAD_URL : (event.downloadUrl ?? null),
|
||||
});
|
||||
if (shouldShowImmediateUserResult) {
|
||||
this.showCurrentUpdateState();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'not-available':
|
||||
this.transition({type: 'native.notAvailable', now: Date.now()});
|
||||
if (shouldShowImmediateUserResult) {
|
||||
@@ -442,22 +425,6 @@ class Updater {
|
||||
}
|
||||
}
|
||||
|
||||
private resolveManualNativeDownloadOptions(
|
||||
event: Extract<UpdaterEvent, {type: 'available'}>,
|
||||
options: ReadonlyArray<UpdaterDownloadOption>,
|
||||
): ReadonlyArray<UpdaterDownloadOption> {
|
||||
if (getElectronAPI()?.platform !== 'linux') {
|
||||
return options;
|
||||
}
|
||||
return buildLinuxManualUpdateOptions({
|
||||
downloadUrl: event.downloadUrl ?? options[0]?.url ?? null,
|
||||
channel: this.channel ?? Config.PUBLIC_RELEASE_CHANNEL,
|
||||
arch: this.desktopArch,
|
||||
version: event.version ?? null,
|
||||
knownOptions: options,
|
||||
});
|
||||
}
|
||||
|
||||
private startBackgroundChecks(): void {
|
||||
if (this.backgroundCheckStarted) return;
|
||||
this.backgroundCheckStarted = true;
|
||||
@@ -601,11 +568,7 @@ class Updater {
|
||||
await this.startDesktopUpdate();
|
||||
return;
|
||||
}
|
||||
if (this.isNative && this.nativeUnsupported?.reason === 'managed-package' && !this.updateInfo.web.available) {
|
||||
showUpdaterModal((modals) => modals.pushUnsupportedUpdateModal('managed-package'));
|
||||
return;
|
||||
}
|
||||
if (this.isNative && this.nativeManualUpdateAvailable && !this.nativeUnsupported) {
|
||||
if (this.isNative && this.nativeManualUpdateAvailable) {
|
||||
this.showManualNativeUpdateModal();
|
||||
return;
|
||||
}
|
||||
@@ -617,15 +580,7 @@ class Updater {
|
||||
}
|
||||
if (this.isNative && this.updateInfo.native.available) {
|
||||
logger.info('Native update is available but not installable in-app; opening desktop downloads...');
|
||||
const url = this.nativeManualDownloadUrl ?? this.nativeUnsupported?.downloadUrl ?? null;
|
||||
if (url) {
|
||||
await openExternalUrl(url);
|
||||
} else if (this.nativeUnsupported) {
|
||||
const {reason, downloadUrl} = this.nativeUnsupported;
|
||||
showUpdaterModal((modals) => modals.pushUnsupportedUpdateModal(reason, downloadUrl));
|
||||
} else {
|
||||
await openExternalUrl(DESKTOP_DOWNLOAD_URL);
|
||||
}
|
||||
await openExternalUrl(DESKTOP_DOWNLOAD_URL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,63 +618,17 @@ class Updater {
|
||||
|
||||
private showManualNativeUpdateModal(): void {
|
||||
const version = this.updateInfo.native.version;
|
||||
const options = this.nativeManualDownloadOptions;
|
||||
if (options.length > 0) {
|
||||
const currentVersion = this.currentVersion;
|
||||
showUpdaterModal((modals) =>
|
||||
modals.pushManualUpdateAvailableModal({
|
||||
currentVersion,
|
||||
version,
|
||||
options,
|
||||
onDownload: (option) => this.downloadManualNativeUpdateOption(option),
|
||||
}),
|
||||
);
|
||||
if (getElectronAPI()?.platform === 'linux') {
|
||||
const flatpakAppId = this.flatpakAppId;
|
||||
showUpdaterModal((modals) => modals.pushManagedShellUpdateModal({version, flatpakAppId}));
|
||||
return;
|
||||
}
|
||||
const url = this.nativeManualDownloadUrl ?? this.nativeUnsupported?.downloadUrl ?? DESKTOP_DOWNLOAD_URL;
|
||||
const url = this.nativeManualDownloadUrl ?? DESKTOP_DOWNLOAD_URL;
|
||||
showUpdaterModal((modals) =>
|
||||
modals.pushUpdateAvailableModal(version, () => this.downloadManualNativeUpdateOrOpen(url)),
|
||||
);
|
||||
}
|
||||
|
||||
private async refreshManualNativeDownloadOption(option: UpdaterDownloadOption): Promise<UpdaterDownloadOption> {
|
||||
if (this.checkInProgress) {
|
||||
return option;
|
||||
}
|
||||
this.checkInProgress = true;
|
||||
this.transition({type: 'check.started'});
|
||||
let timeoutId: number | undefined;
|
||||
const timedOut = new Promise<boolean>((resolve) => {
|
||||
timeoutId = window.setTimeout(() => resolve(false), MANUAL_DOWNLOAD_REFRESH_TIMEOUT_MS);
|
||||
});
|
||||
try {
|
||||
this.pendingManualDownloadRefreshes += 1;
|
||||
const checked = await Promise.race([this.checkNativeUpdate('user'), timedOut]);
|
||||
if (!checked) {
|
||||
return option;
|
||||
}
|
||||
return this.nativeManualDownloadOptions.find((candidate) => candidate.format === option.format) ?? option;
|
||||
} finally {
|
||||
window.clearTimeout(timeoutId);
|
||||
this.checkInProgress = false;
|
||||
this.transition({type: 'check.finished', now: Date.now()});
|
||||
}
|
||||
}
|
||||
|
||||
private async downloadManualNativeUpdateOption(option: UpdaterDownloadOption): Promise<void> {
|
||||
if (this.manualNativeDownloadInFlight) {
|
||||
return;
|
||||
}
|
||||
this.transition({type: 'manualDownload.started'});
|
||||
let currentOption = option;
|
||||
try {
|
||||
currentOption = await this.refreshManualNativeDownloadOption(option);
|
||||
} finally {
|
||||
this.transition({type: 'manualDownload.finished'});
|
||||
}
|
||||
await this.downloadManualNativeUpdateOrOpen(currentOption.url, currentOption.suggestedName, currentOption.sha256);
|
||||
}
|
||||
|
||||
private async downloadManualNativeUpdateOrOpen(
|
||||
url: string,
|
||||
suggestedName?: string,
|
||||
|
||||
Reference in new issue
Block a user