mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
Desktop: Seamless-Updates: triggering updates (#11079)
This commit is contained in:
parent
99696637b9
commit
a56f104fe8
@ -332,6 +332,10 @@ export default class ElectronAppWrapper {
|
||||
this.updaterService_.updateApp();
|
||||
});
|
||||
|
||||
ipcMain.on('check-for-updates', () => {
|
||||
void this.updaterService_.checkForUpdates(true);
|
||||
});
|
||||
|
||||
// Let us register listeners on the window, so we can update the state
|
||||
// automatically (the listeners will be removed when the window is closed)
|
||||
// and restore the maximized or full screen state
|
||||
@ -470,7 +474,7 @@ export default class ElectronAppWrapper {
|
||||
}
|
||||
|
||||
// Electron's autoUpdater has to be init from the main process
|
||||
public async initializeAutoUpdaterService(logger: LoggerWrapper, devMode: boolean, includePreReleases: boolean) {
|
||||
public initializeAutoUpdaterService(logger: LoggerWrapper, devMode: boolean, includePreReleases: boolean) {
|
||||
if (shim.isWindows() || shim.isMac()) {
|
||||
if (!this.updaterService_) {
|
||||
this.updaterService_ = new AutoUpdaterService(this.win_, logger, devMode, includePreReleases);
|
||||
@ -482,7 +486,7 @@ export default class ElectronAppWrapper {
|
||||
private startPeriodicUpdateCheck = (updateInterval: number = defaultUpdateInterval): void => {
|
||||
this.stopPeriodicUpdateCheck();
|
||||
this.updatePollInterval_ = setInterval(() => {
|
||||
void this.updaterService_.checkForUpdates();
|
||||
void this.updaterService_.checkForUpdates(false);
|
||||
}, updateInterval);
|
||||
setTimeout(this.updaterService_.checkForUpdates, initialUpdateStartup);
|
||||
};
|
||||
@ -491,6 +495,7 @@ export default class ElectronAppWrapper {
|
||||
if (this.updatePollInterval_) {
|
||||
clearInterval(this.updatePollInterval_);
|
||||
this.updatePollInterval_ = null;
|
||||
this.updaterService_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -404,9 +404,9 @@ class Application extends BaseApplication {
|
||||
eventManager.on(EventName.ResourceChange, handleResourceChange);
|
||||
}
|
||||
|
||||
private async setupAutoUpdaterService() {
|
||||
private setupAutoUpdaterService() {
|
||||
if (Setting.value('featureFlag.autoUpdaterServiceEnabled')) {
|
||||
await bridge().electronApp().initializeAutoUpdaterService(
|
||||
bridge().electronApp().initializeAutoUpdaterService(
|
||||
Logger.create('AutoUpdaterService'),
|
||||
Setting.value('env') === 'dev',
|
||||
Setting.value('autoUpdate.includePreReleases'),
|
||||
@ -449,6 +449,8 @@ class Application extends BaseApplication {
|
||||
// Loads app-wide styles. (Markdown preview-specific styles loaded in app.js)
|
||||
await injectCustomStyles('appStyles', Setting.customCssFilePath(Setting.customCssFilenames.JOPLIN_APP));
|
||||
|
||||
this.setupAutoUpdaterService();
|
||||
|
||||
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
|
||||
AlarmService.setLogger(reg.logger());
|
||||
|
||||
@ -698,8 +700,6 @@ class Application extends BaseApplication {
|
||||
SearchEngine.instance().scheduleSyncTables();
|
||||
});
|
||||
|
||||
await this.setupAutoUpdaterService();
|
||||
|
||||
// setTimeout(() => {
|
||||
// void populateDatabase(reg.db(), {
|
||||
// clearDatabase: true,
|
||||
|
@ -26,6 +26,7 @@ import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/Plug
|
||||
import { getListRendererById, getListRendererIds } from '@joplin/lib/services/noteList/renderers';
|
||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||
import { EventName } from '@joplin/lib/eventManager';
|
||||
import { ipcRenderer } from 'electron';
|
||||
const packageInfo: PackageInfo = require('../packageInfo.js');
|
||||
const { clipboard } = require('electron');
|
||||
const Menu = bridge().Menu;
|
||||
@ -575,7 +576,12 @@ function useMenu(props: Props) {
|
||||
toolsItems.push(SpellCheckerService.instance().spellCheckerConfigMenuItem(props['spellChecker.languages'], props['spellChecker.enabled']));
|
||||
|
||||
function _checkForUpdates() {
|
||||
void checkForUpdates(false, bridge().window(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||
if (Setting.value('featureFlag.autoUpdaterServiceEnabled')) {
|
||||
ipcRenderer.send('check-for-updates');
|
||||
} else {
|
||||
void checkForUpdates(false, bridge().window(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _showAbout() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useContext, useCallback, useMemo } from 'react';
|
||||
import { useContext, useCallback, useMemo, useRef } from 'react';
|
||||
import { StateLastDeletion } from '@joplin/lib/reducer';
|
||||
import { _, _n } from '@joplin/lib/locale';
|
||||
import NotyfContext from '../NotyfContext';
|
||||
@ -9,6 +9,7 @@ import restoreItems from '@joplin/lib/services/trash/restoreItems';
|
||||
import { ModelType } from '@joplin/lib/BaseModel';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
import { Dispatch } from 'redux';
|
||||
import { NotyfNotification } from 'notyf';
|
||||
|
||||
interface Props {
|
||||
lastDeletion: StateLastDeletion;
|
||||
@ -19,6 +20,7 @@ interface Props {
|
||||
|
||||
export default (props: Props) => {
|
||||
const notyfContext = useContext(NotyfContext);
|
||||
const notificationRef = useRef<NotyfNotification | null>(null);
|
||||
|
||||
const theme = useMemo(() => {
|
||||
return themeStyle(props.themeId);
|
||||
@ -39,7 +41,8 @@ export default (props: Props) => {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const onCancelClick = useCallback(async (event: any) => {
|
||||
notyf.dismissAll();
|
||||
notyf.dismiss(notificationRef.current);
|
||||
notificationRef.current = null;
|
||||
|
||||
const lastDeletion: StateLastDeletion = JSON.parse(event.currentTarget.getAttribute('data-lastDeletion'));
|
||||
|
||||
@ -70,7 +73,8 @@ export default (props: Props) => {
|
||||
const linkId = `deletion-notification-cancel-${Math.floor(Math.random() * 1000000)}`;
|
||||
const cancelLabel = _('Cancel');
|
||||
|
||||
notyf.success(`${msg} <a href="#" class="cancel" data-lastDeletion="${htmlentities(JSON.stringify(props.lastDeletion))}" id="${linkId}">${cancelLabel}</a>`);
|
||||
const notification = notyf.success(`${msg} <a href="#" class="cancel" data-lastDeletion="${htmlentities(JSON.stringify(props.lastDeletion))}" id="${linkId}">${cancelLabel}</a>`);
|
||||
notificationRef.current = notification;
|
||||
|
||||
const element: HTMLAnchorElement = await waitForElement(document, linkId);
|
||||
if (event.cancelled) return;
|
||||
|
@ -5,7 +5,7 @@ import NotyfContext from '../NotyfContext';
|
||||
import { UpdateInfo } from 'electron-updater';
|
||||
import { ipcRenderer, IpcRendererEvent } from 'electron';
|
||||
import { AutoUpdaterEvents } from '../../services/autoUpdater/AutoUpdaterService';
|
||||
import { NotyfNotification } from 'notyf';
|
||||
import { NotyfEvent, NotyfNotification } from 'notyf';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { htmlentities } from '@joplin/utils/html';
|
||||
import shim from '@joplin/lib/shim';
|
||||
@ -16,6 +16,7 @@ interface UpdateNotificationProps {
|
||||
|
||||
export enum UpdateNotificationEvents {
|
||||
ApplyUpdate = 'apply-update',
|
||||
UpdateNotAvailable = 'update-not-available',
|
||||
Dismiss = 'dismiss-update-notification',
|
||||
}
|
||||
|
||||
@ -86,17 +87,46 @@ const UpdateNotification = ({ themeId }: UpdateNotificationProps) => {
|
||||
notificationRef.current = notification;
|
||||
}, [notyf, theme]);
|
||||
|
||||
const handleUpdateNotAvailable = useCallback(() => {
|
||||
if (notificationRef.current) return;
|
||||
|
||||
const noUpdateMessageHtml = htmlentities(_('No updates available'));
|
||||
|
||||
const messageHtml = `
|
||||
<div class="update-notification" style="color: ${theme.color2};">
|
||||
${noUpdateMessageHtml}
|
||||
</div>
|
||||
`;
|
||||
|
||||
const notification: NotyfNotification = notyf.open({
|
||||
type: 'success',
|
||||
message: messageHtml,
|
||||
position: {
|
||||
x: 'right',
|
||||
y: 'bottom',
|
||||
},
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
notification.on(NotyfEvent.Dismiss, () => {
|
||||
notificationRef.current = null;
|
||||
});
|
||||
|
||||
notificationRef.current = notification;
|
||||
}, [notyf, theme]);
|
||||
|
||||
useEffect(() => {
|
||||
ipcRenderer.on(AutoUpdaterEvents.UpdateDownloaded, handleUpdateDownloaded);
|
||||
ipcRenderer.on(AutoUpdaterEvents.UpdateNotAvailable, handleUpdateNotAvailable);
|
||||
document.addEventListener(UpdateNotificationEvents.ApplyUpdate, handleApplyUpdate);
|
||||
document.addEventListener(UpdateNotificationEvents.Dismiss, handleDismissNotification);
|
||||
|
||||
return () => {
|
||||
ipcRenderer.removeListener(AutoUpdaterEvents.UpdateDownloaded, handleUpdateDownloaded);
|
||||
ipcRenderer.removeListener(AutoUpdaterEvents.UpdateNotAvailable, handleUpdateNotAvailable);
|
||||
document.removeEventListener(UpdateNotificationEvents.ApplyUpdate, handleApplyUpdate);
|
||||
document.removeEventListener(UpdateNotificationEvents.Dismiss, handleDismissNotification);
|
||||
};
|
||||
}, [handleApplyUpdate, handleDismissNotification, handleUpdateDownloaded]);
|
||||
}, [handleApplyUpdate, handleDismissNotification, handleUpdateDownloaded, handleUpdateNotAvailable]);
|
||||
|
||||
|
||||
return (
|
||||
|
@ -37,7 +37,7 @@ const supportedPlatformAssets: PlatformAssets = {
|
||||
};
|
||||
|
||||
export interface AutoUpdaterServiceInterface {
|
||||
checkForUpdates(): void;
|
||||
checkForUpdates(isManualCheck: boolean): void;
|
||||
updateApp(): void;
|
||||
fetchLatestRelease(includePreReleases: boolean): Promise<GitHubRelease>;
|
||||
getDownloadUrlForPlatform(release: GitHubRelease, platform: string, arch: string): string;
|
||||
@ -48,10 +48,11 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
||||
private logger_: LoggerWrapper;
|
||||
private devMode_: boolean;
|
||||
private enableDevMode = true; // force the updater to work in "dev" mode
|
||||
private enableAutoDownload = false; // automatically download an update when it is found
|
||||
private enableAutoDownload = true; // automatically download an update when it is found
|
||||
private autoInstallOnAppQuit = false; // automatically install the downloaded update once the user closes the application
|
||||
private includePreReleases_ = false;
|
||||
private allowDowngrade = false;
|
||||
private isManualCheckInProgress = false;
|
||||
|
||||
public constructor(mainWindow: BrowserWindow, logger: LoggerWrapper, devMode: boolean, includePreReleases: boolean) {
|
||||
this.window_ = mainWindow;
|
||||
@ -61,8 +62,9 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
||||
this.configureAutoUpdater();
|
||||
}
|
||||
|
||||
public checkForUpdates = async (): Promise<void> => {
|
||||
public checkForUpdates = async (isManualCheck = false): Promise<void> => {
|
||||
try {
|
||||
this.isManualCheckInProgress = isManualCheck;
|
||||
await this.checkForLatestRelease();
|
||||
} catch (error) {
|
||||
this.logger_.error('Failed to check for updates:', error);
|
||||
@ -132,6 +134,7 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
||||
assetUrl = assetUrl.substring(0, assetUrl.lastIndexOf('/'));
|
||||
autoUpdater.setFeedURL({ provider: 'generic', url: assetUrl });
|
||||
await autoUpdater.checkForUpdates();
|
||||
this.isManualCheckInProgress = false;
|
||||
} catch (error) {
|
||||
this.logger_.error(`Update download url failed: ${error.message}`);
|
||||
}
|
||||
@ -167,6 +170,10 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
||||
};
|
||||
|
||||
private onUpdateNotAvailable = (_info: UpdateInfo): void => {
|
||||
if (this.isManualCheckInProgress) {
|
||||
this.window_.webContents.send(AutoUpdaterEvents.UpdateNotAvailable);
|
||||
}
|
||||
|
||||
this.logger_.info('Update not available.');
|
||||
};
|
||||
|
||||
|
@ -1127,7 +1127,7 @@ const builtInMetadata = (Setting: typeof SettingType) => {
|
||||
},
|
||||
|
||||
|
||||
autoUpdateEnabled: { value: true, type: SettingItemType.Bool, storage: SettingStorage.File, isGlobal: true, section: 'application', public: platform !== 'linux', appTypes: [AppType.Desktop], label: () => _('Automatically check for updates') },
|
||||
autoUpdateEnabled: { value: false, type: SettingItemType.Bool, storage: SettingStorage.File, isGlobal: true, section: 'application', public: false, appTypes: [AppType.Desktop], label: () => _('Automatically check for updates') },
|
||||
'autoUpdate.includePreReleases': { value: false, type: SettingItemType.Bool, section: 'application', storage: SettingStorage.File, isGlobal: true, public: true, appTypes: [AppType.Desktop], label: () => _('Get pre-releases when checking for updates'), description: () => _('See the pre-release page for more details: %s', 'https://joplinapp.org/help/about/prereleases') },
|
||||
|
||||
'autoUploadCrashDumps': {
|
||||
|
Loading…
Reference in New Issue
Block a user