mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +02:00
Desktop: Added button to skip an application update
And made auto-updates enabled by default
This commit is contained in:
parent
6959f14a3f
commit
a31b402b9e
@ -200,6 +200,9 @@ packages/app-desktop/app.js.map
|
|||||||
packages/app-desktop/bridge.d.ts
|
packages/app-desktop/bridge.d.ts
|
||||||
packages/app-desktop/bridge.js
|
packages/app-desktop/bridge.js
|
||||||
packages/app-desktop/bridge.js.map
|
packages/app-desktop/bridge.js.map
|
||||||
|
packages/app-desktop/checkForUpdates.d.ts
|
||||||
|
packages/app-desktop/checkForUpdates.js
|
||||||
|
packages/app-desktop/checkForUpdates.js.map
|
||||||
packages/app-desktop/commands/copyDevCommand.d.ts
|
packages/app-desktop/commands/copyDevCommand.d.ts
|
||||||
packages/app-desktop/commands/copyDevCommand.js
|
packages/app-desktop/commands/copyDevCommand.js
|
||||||
packages/app-desktop/commands/copyDevCommand.js.map
|
packages/app-desktop/commands/copyDevCommand.js.map
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -186,6 +186,9 @@ packages/app-desktop/app.js.map
|
|||||||
packages/app-desktop/bridge.d.ts
|
packages/app-desktop/bridge.d.ts
|
||||||
packages/app-desktop/bridge.js
|
packages/app-desktop/bridge.js
|
||||||
packages/app-desktop/bridge.js.map
|
packages/app-desktop/bridge.js.map
|
||||||
|
packages/app-desktop/checkForUpdates.d.ts
|
||||||
|
packages/app-desktop/checkForUpdates.js
|
||||||
|
packages/app-desktop/checkForUpdates.js.map
|
||||||
packages/app-desktop/commands/copyDevCommand.d.ts
|
packages/app-desktop/commands/copyDevCommand.d.ts
|
||||||
packages/app-desktop/commands/copyDevCommand.js
|
packages/app-desktop/commands/copyDevCommand.js
|
||||||
packages/app-desktop/commands/copyDevCommand.js.map
|
packages/app-desktop/commands/copyDevCommand.js.map
|
||||||
|
@ -103,6 +103,7 @@ const globalCommands = [
|
|||||||
|
|
||||||
import editorCommandDeclarations from './gui/NoteEditor/commands/editorCommandDeclarations';
|
import editorCommandDeclarations from './gui/NoteEditor/commands/editorCommandDeclarations';
|
||||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||||
|
import checkForUpdates from './checkForUpdates';
|
||||||
|
|
||||||
const pluginClasses = [
|
const pluginClasses = [
|
||||||
require('./plugins/GotoAnything').default,
|
require('./plugins/GotoAnything').default,
|
||||||
@ -167,10 +168,6 @@ class Application extends BaseApplication {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkForUpdateLoggerPath() {
|
|
||||||
return `${Setting.value('profileDir')}/log-autoupdater.txt`;
|
|
||||||
}
|
|
||||||
|
|
||||||
reducer(state: AppState = appDefaultState, action: any) {
|
reducer(state: AppState = appDefaultState, action: any) {
|
||||||
let newState = state;
|
let newState = state;
|
||||||
|
|
||||||
@ -711,7 +708,7 @@ class Application extends BaseApplication {
|
|||||||
if (shim.isWindows() || shim.isMac()) {
|
if (shim.isWindows() || shim.isMac()) {
|
||||||
const runAutoUpdateCheck = () => {
|
const runAutoUpdateCheck = () => {
|
||||||
if (Setting.value('autoUpdateEnabled')) {
|
if (Setting.value('autoUpdateEnabled')) {
|
||||||
bridge().checkForUpdates(true, bridge().window(), this.checkForUpdateLoggerPath(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
void checkForUpdates(true, bridge().window(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import ElectronAppWrapper from './ElectronAppWrapper';
|
import ElectronAppWrapper from './ElectronAppWrapper';
|
||||||
import shim from '@joplin/lib/shim';
|
import shim from '@joplin/lib/shim';
|
||||||
|
|
||||||
import { _, setLocale } from '@joplin/lib/locale';
|
import { _, setLocale } from '@joplin/lib/locale';
|
||||||
const { dirname, toSystemSlashes } = require('@joplin/lib/path-utils');
|
const { dirname, toSystemSlashes } = require('@joplin/lib/path-utils');
|
||||||
const { BrowserWindow, nativeTheme } = require('electron');
|
const { BrowserWindow, nativeTheme } = require('electron');
|
||||||
@ -174,11 +173,6 @@ export class Bridge {
|
|||||||
return require('electron').shell.openPath(fullPath);
|
return require('electron').shell.openPath(fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkForUpdates(inBackground: boolean, window: any, logFilePath: string, options: any) {
|
|
||||||
const { checkForUpdates } = require('./checkForUpdates.js');
|
|
||||||
checkForUpdates(inBackground, window, logFilePath, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildDir() {
|
buildDir() {
|
||||||
return this.electronApp().buildDir();
|
return this.electronApp().buildDir();
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,42 @@
|
|||||||
const { dialog } = require('electron');
|
import shim from '@joplin/lib/shim';
|
||||||
const shim = require('@joplin/lib/shim').default;
|
import Logger from '@joplin/lib/Logger';
|
||||||
const Logger = require('@joplin/lib/Logger').default;
|
import { _ } from '@joplin/lib/locale';
|
||||||
const { _ } = require('@joplin/lib/locale');
|
import bridge from './services/bridge';
|
||||||
const fetch = require('node-fetch');
|
import KvStore from '@joplin/lib/services/KvStore';
|
||||||
const { fileExtension } = require('@joplin/lib/path-utils');
|
const { fileExtension } = require('@joplin/lib/path-utils');
|
||||||
|
const ArrayUtils = require('@joplin/lib/ArrayUtils');
|
||||||
const packageInfo = require('./packageInfo.js');
|
const packageInfo = require('./packageInfo.js');
|
||||||
const compareVersions = require('compare-versions');
|
const compareVersions = require('compare-versions');
|
||||||
|
|
||||||
let autoUpdateLogger_ = new Logger();
|
const logger = Logger.create('checkForUpdates');
|
||||||
|
|
||||||
let checkInBackground_ = false;
|
let checkInBackground_ = false;
|
||||||
let isCheckingForUpdate_ = false;
|
let isCheckingForUpdate_ = false;
|
||||||
let parentWindow_ = null;
|
|
||||||
|
|
||||||
function showErrorMessageBox(message) {
|
interface CheckForUpdateOptions {
|
||||||
return dialog.showMessageBox(parentWindow_, {
|
includePreReleases?: boolean;
|
||||||
type: 'error',
|
|
||||||
message: message,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCheckStarted() {
|
function onCheckStarted() {
|
||||||
autoUpdateLogger_.info('checkForUpdates: Starting...');
|
logger.info('Starting...');
|
||||||
isCheckingForUpdate_ = true;
|
isCheckingForUpdate_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCheckEnded() {
|
function onCheckEnded() {
|
||||||
autoUpdateLogger_.info('checkForUpdates: Done.');
|
logger.info('Done.');
|
||||||
isCheckingForUpdate_ = false;
|
isCheckingForUpdate_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMajorMinorTagName(tagName) {
|
function getMajorMinorTagName(tagName: string) {
|
||||||
const s = tagName.split('.');
|
const s = tagName.split('.');
|
||||||
s.pop();
|
s.pop();
|
||||||
return s.join('.');
|
return s.join('.');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchLatestRelease(options) {
|
async function fetchLatestRelease(options: CheckForUpdateOptions) {
|
||||||
options = Object.assign({}, { includePreReleases: false }, options);
|
options = Object.assign({}, { includePreReleases: false }, options);
|
||||||
|
|
||||||
const response = await fetch('https://api.github.com/repos/laurent22/joplin/releases');
|
const response = await shim.fetch('https://api.github.com/repos/laurent22/joplin/releases');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
@ -104,7 +102,7 @@ async function fetchLatestRelease(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanUpReleaseNotes(releaseNotes) {
|
function cleanUpReleaseNotes(releaseNotes: string[]) {
|
||||||
const lines = releaseNotes.join('\n\n* * *\n\n').split('\n');
|
const lines = releaseNotes.join('\n\n* * *\n\n').split('\n');
|
||||||
const output = [];
|
const output = [];
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
@ -129,7 +127,7 @@ async function fetchLatestRelease(options) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function truncateText(text, length) {
|
function truncateText(text: string, length: number) {
|
||||||
let truncated = text.substring(0, length);
|
let truncated = text.substring(0, length);
|
||||||
const lastNewLine = truncated.lastIndexOf('\n');
|
const lastNewLine = truncated.lastIndexOf('\n');
|
||||||
// Cut off at a line break unless we'd be cutting off half the text
|
// Cut off at a line break unless we'd be cutting off half the text
|
||||||
@ -141,40 +139,51 @@ function truncateText(text, length) {
|
|||||||
return truncated;
|
return truncated;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForUpdates(inBackground, window, logFilePath, options) {
|
async function getSkippedVersions(): Promise<string[]> {
|
||||||
|
const r = await KvStore.instance().value<string>('updateCheck::skippedVersions');
|
||||||
|
return r ? JSON.parse(r) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isSkippedVersion(v: string): Promise<boolean> {
|
||||||
|
const versions = await getSkippedVersions();
|
||||||
|
return versions.includes(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addSkippedVersion(s: string) {
|
||||||
|
let versions = await getSkippedVersions();
|
||||||
|
versions.push(s);
|
||||||
|
versions = ArrayUtils.unique(versions);
|
||||||
|
await KvStore.instance().setValue('updateCheck::skippedVersions', JSON.stringify(versions));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function checkForUpdates(inBackground: boolean, parentWindow: any, options: CheckForUpdateOptions) {
|
||||||
if (isCheckingForUpdate_) {
|
if (isCheckingForUpdate_) {
|
||||||
autoUpdateLogger_.info('checkForUpdates: Skipping check because it is already running');
|
logger.info('Skipping check because it is already running');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parentWindow_ = window;
|
|
||||||
|
|
||||||
onCheckStarted();
|
onCheckStarted();
|
||||||
|
|
||||||
if (logFilePath && !autoUpdateLogger_.targets().length) {
|
|
||||||
autoUpdateLogger_ = new Logger();
|
|
||||||
autoUpdateLogger_.addTarget('file', { path: logFilePath });
|
|
||||||
autoUpdateLogger_.setLevel(Logger.LEVEL_DEBUG);
|
|
||||||
autoUpdateLogger_.info('checkForUpdates: Initializing...');
|
|
||||||
}
|
|
||||||
|
|
||||||
checkInBackground_ = inBackground;
|
checkInBackground_ = inBackground;
|
||||||
|
|
||||||
autoUpdateLogger_.info(`checkForUpdates: Checking with options ${JSON.stringify(options)}`);
|
logger.info(`Checking with options ${JSON.stringify(options)}`);
|
||||||
|
|
||||||
fetchLatestRelease(options).then(async (release) => {
|
try {
|
||||||
autoUpdateLogger_.info(`Current version: ${packageInfo.version}`);
|
const release = await fetchLatestRelease(options);
|
||||||
autoUpdateLogger_.info(`Latest version: ${release.version}`);
|
|
||||||
autoUpdateLogger_.info('Is Pre-release:', release.prerelease);
|
logger.info(`Current version: ${packageInfo.version}`);
|
||||||
|
logger.info(`Latest version: ${release.version}`);
|
||||||
|
logger.info('Is Pre-release:', release.prerelease);
|
||||||
|
|
||||||
if (compareVersions(release.version, packageInfo.version) <= 0) {
|
if (compareVersions(release.version, packageInfo.version) <= 0) {
|
||||||
if (!checkInBackground_) {
|
if (!checkInBackground_) {
|
||||||
await dialog.showMessageBox({
|
await bridge().showMessageBox(_('Current version is up-to-date.'));
|
||||||
type: 'info',
|
|
||||||
message: _('Current version is up-to-date.'),
|
|
||||||
buttons: [_('OK')],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const shouldSkip = checkInBackground_ && await isSkippedVersion(release.version);
|
||||||
|
|
||||||
|
if (shouldSkip) {
|
||||||
|
logger.info('Not displaying notification because version has been skipped');
|
||||||
} else {
|
} else {
|
||||||
const fullReleaseNotes = release.notes.trim() ? `\n\n${release.notes.trim()}` : '';
|
const fullReleaseNotes = release.notes.trim() ? `\n\n${release.notes.trim()}` : '';
|
||||||
const MAX_RELEASE_NOTES_LENGTH = 1000;
|
const MAX_RELEASE_NOTES_LENGTH = 1000;
|
||||||
@ -183,24 +192,27 @@ function checkForUpdates(inBackground, window, logFilePath, options) {
|
|||||||
|
|
||||||
const newVersionString = release.prerelease ? _('%s (pre-release)', release.version) : release.version;
|
const newVersionString = release.prerelease ? _('%s (pre-release)', release.version) : release.version;
|
||||||
|
|
||||||
const result = await dialog.showMessageBox(parentWindow_, {
|
const buttonIndex = await bridge().showMessageBox(parentWindow, {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message: `${_('An update is available, do you want to download it now?')}`,
|
message: `${_('An update is available, do you want to download it now?')}`,
|
||||||
detail: `${_('Your version: %s', packageInfo.version)}\n${_('New version: %s', newVersionString)}${releaseNotes}`,
|
detail: `${_('Your version: %s', packageInfo.version)}\n${_('New version: %s', newVersionString)}${releaseNotes}`,
|
||||||
buttons: [_('Download'), _('Cancel'), _('Full changelog')],
|
buttons: [_('Download'), _('Skip this version'), _('Full changelog'), _('Cancel')],
|
||||||
cancelId: 1,
|
cancelId: 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttonIndex = result.response;
|
if (buttonIndex === 0) {
|
||||||
if (buttonIndex === 0) require('electron').shell.openExternal(release.downloadUrl ? release.downloadUrl : release.pageUrl);
|
bridge().openExternal(release.downloadUrl ? release.downloadUrl : release.pageUrl);
|
||||||
if (buttonIndex === 2) require('electron').shell.openExternal('https://joplinapp.org/changelog/');
|
} else if (buttonIndex === 1) {
|
||||||
|
await addSkippedVersion(release.version);
|
||||||
|
} else if (buttonIndex === 2) {
|
||||||
|
bridge().openExternal('https://joplinapp.org/changelog/');
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}
|
||||||
autoUpdateLogger_.error(error);
|
}
|
||||||
if (!checkInBackground_) showErrorMessageBox(error.message);
|
} catch (error) {
|
||||||
}).then(() => {
|
logger.error(error);
|
||||||
|
if (!checkInBackground_) await bridge().showErrorMessageBox(error.message);
|
||||||
|
} finally {
|
||||||
onCheckEnded();
|
onCheckEnded();
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.checkForUpdates = checkForUpdates;
|
|
@ -17,6 +17,7 @@ import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerS
|
|||||||
import menuCommandNames from './menuCommandNames';
|
import menuCommandNames from './menuCommandNames';
|
||||||
import stateToWhenClauseContext from '../services/commands/stateToWhenClauseContext';
|
import stateToWhenClauseContext from '../services/commands/stateToWhenClauseContext';
|
||||||
import bridge from '../services/bridge';
|
import bridge from '../services/bridge';
|
||||||
|
import checkForUpdates from '../checkForUpdates';
|
||||||
|
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
import { reg } from '@joplin/lib/registry';
|
import { reg } from '@joplin/lib/registry';
|
||||||
@ -430,7 +431,7 @@ function useMenu(props: Props) {
|
|||||||
toolsItems.push(SpellCheckerService.instance().spellCheckerConfigMenuItem(props['spellChecker.language'], props['spellChecker.enabled']));
|
toolsItems.push(SpellCheckerService.instance().spellCheckerConfigMenuItem(props['spellChecker.language'], props['spellChecker.enabled']));
|
||||||
|
|
||||||
function _checkForUpdates() {
|
function _checkForUpdates() {
|
||||||
bridge().checkForUpdates(false, bridge().window(), `${Setting.value('profileDir')}/log-autoupdater.txt`, { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
void checkForUpdates(false, bridge().window(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||||
}
|
}
|
||||||
|
|
||||||
function _showAbout() {
|
function _showAbout() {
|
||||||
|
@ -992,7 +992,7 @@ class Setting extends BaseModel {
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
autoUpdateEnabled: { value: false, type: SettingItemType.Bool, storage: SettingStorage.File, section: 'application', public: platform !== 'linux', appTypes: ['desktop'], label: () => _('Automatically update the application') },
|
autoUpdateEnabled: { value: true, type: SettingItemType.Bool, storage: SettingStorage.File, section: 'application', public: platform !== 'linux', appTypes: ['desktop'], label: () => _('Automatically update the application') },
|
||||||
'autoUpdate.includePreReleases': { value: false, type: SettingItemType.Bool, section: 'application', storage: SettingStorage.File, public: true, appTypes: ['desktop'], label: () => _('Get pre-releases when checking for updates'), description: () => _('See the pre-release page for more details: %s', 'https://joplinapp.org/prereleases') },
|
'autoUpdate.includePreReleases': { value: false, type: SettingItemType.Bool, section: 'application', storage: SettingStorage.File, public: true, appTypes: ['desktop'], label: () => _('Get pre-releases when checking for updates'), description: () => _('See the pre-release page for more details: %s', 'https://joplinapp.org/prereleases') },
|
||||||
'clipperServer.autoStart': { value: false, type: SettingItemType.Bool, storage: SettingStorage.File, public: false },
|
'clipperServer.autoStart': { value: false, type: SettingItemType.Bool, storage: SettingStorage.File, public: false },
|
||||||
'sync.interval': {
|
'sync.interval': {
|
||||||
|
@ -169,7 +169,7 @@ const shim = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
fetch: (_url: string, _options: any): any => {
|
fetch: (_url: string, _options: any = null): any => {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user