Multiple fixes in auto updates.

- Add "disable auto updates" setting (prevents downloading updates in the background but will still notify if there are newer versions)
- Prevent downloading multiple times the same update
- Hide nag when auto update modal is displayed
This commit is contained in:
Franco Montenegro 2022-02-18 15:44:00 -03:00 committed by jessopb
parent aa40a44ce3
commit 48c5f58a8e
10 changed files with 115 additions and 2 deletions

View file

@ -39,6 +39,12 @@ let autoUpdateDownloaded = false;
// that we show on Windows after you decline an upgrade and close the app later. // that we show on Windows after you decline an upgrade and close the app later.
let showingAutoUpdateCloseAlert = false; let showingAutoUpdateCloseAlert = false;
// This is used to prevent downloading updates multiple times.
// As read in the documentation:
// "Calling autoUpdater.checkForUpdates() twice will download the update two times."
// https://www.electronjs.org/docs/latest/api/auto-updater#autoupdatercheckforupdates
let keepCheckingForUpdates = true;
// Keep a global reference, if you don't, they will be closed automatically when the JavaScript // Keep a global reference, if you don't, they will be closed automatically when the JavaScript
// object is garbage collected. // object is garbage collected.
let rendererWindow; let rendererWindow;
@ -315,16 +321,49 @@ ipcMain.on('upgrade', (event, installerPath) => {
}); });
ipcMain.on('check-for-updates', () => { ipcMain.on('check-for-updates', () => {
// Prevent downloading the same update multiple times.
if (!keepCheckingForUpdates) {
return;
}
keepCheckingForUpdates = false;
autoUpdater.checkForUpdates(); autoUpdater.checkForUpdates();
}); });
autoUpdater.on('update-downloaded', () => { autoUpdater.on('update-downloaded', () => {
autoUpdateDownloaded = true; autoUpdateDownloaded = true;
// If this download was trigger by
// autoUpdateAccepted it means, the user
// wants to install the new update but
// needed to downloaded the files first.
if (appState.autoUpdateAccepted) {
autoUpdater.quitAndInstall();
}
});
autoUpdater.on('update-not-available', () => {
keepCheckingForUpdates = true;
}); });
ipcMain.on('autoUpdateAccepted', () => { ipcMain.on('autoUpdateAccepted', () => {
appState.autoUpdateAccepted = true; appState.autoUpdateAccepted = true;
// quitAndInstall can only be called if the
// update has been downloaded. Since the user
// can disable auto updates, we have to make
// sure it has been downloaded first.
if (autoUpdateDownloaded) {
autoUpdater.quitAndInstall(); autoUpdater.quitAndInstall();
return;
}
// If the update hasn't been downloaded,
// start downloading it. After it's done, the
// event 'update-downloaded' will be triggered,
// where we will be able to resume the
// update installation.
autoUpdater.downloadUpdate();
}); });
ipcMain.on('version-info-requested', () => { ipcMain.on('version-info-requested', () => {

View file

@ -2285,5 +2285,7 @@
"Enable Automatic Hosting": "Enable Automatic Hosting", "Enable Automatic Hosting": "Enable Automatic Hosting",
"Download and serve arbitrary data on the network.": "Download and serve arbitrary data on the network.", "Download and serve arbitrary data on the network.": "Download and serve arbitrary data on the network.",
"View History Hosting": "View History Hosting", "View History Hosting": "View History Hosting",
"Disable automatic updates": "Disable automatic updates",
"Preven't new updates to be downloaded automatically in the background (we will keep notifying you if there is an update)": "Preven't new updates to be downloaded automatically in the background (we will keep notifying you if there is an update)",
"--end--": "--end--" "--end--": "--end--"
} }

View file

@ -19,6 +19,7 @@ import {
selectAutoUpdateDownloaded, selectAutoUpdateDownloaded,
selectModal, selectModal,
selectActiveChannelClaim, selectActiveChannelClaim,
selectIsUpdateModelDisplayed,
} from 'redux/selectors/app'; } from 'redux/selectors/app';
import { doGetWalletSyncPreference, doSetLanguage } from 'redux/actions/settings'; import { doGetWalletSyncPreference, doSetLanguage } from 'redux/actions/settings';
import { doSyncLoop } from 'redux/actions/sync'; import { doSyncLoop } from 'redux/actions/sync';
@ -50,6 +51,7 @@ const select = (state) => ({
myChannelUrls: selectMyChannelUrls(state), myChannelUrls: selectMyChannelUrls(state),
myChannelClaimIds: selectMyChannelClaimIds(state), myChannelClaimIds: selectMyChannelClaimIds(state),
subscriptions: selectSubscriptions(state), subscriptions: selectSubscriptions(state),
isUpdateModalDisplayed: selectIsUpdateModelDisplayed(state),
}); });
const perform = (dispatch) => ({ const perform = (dispatch) => ({

View file

@ -76,6 +76,7 @@ type Props = {
fetchModBlockedList: () => void, fetchModBlockedList: () => void,
resolveUris: (Array<string>) => void, resolveUris: (Array<string>) => void,
fetchModAmIList: () => void, fetchModAmIList: () => void,
isUpdateModalDisplayed: boolean,
}; };
function App(props: Props) { function App(props: Props) {
@ -111,6 +112,7 @@ function App(props: Props) {
resolveUris, resolveUris,
subscriptions, subscriptions,
fetchModAmIList, fetchModAmIList,
isUpdateModalDisplayed,
} = props; } = props;
const appRef = useRef(); const appRef = useRef();
@ -345,7 +347,7 @@ function App(props: Props) {
<FileRenderFloating /> <FileRenderFloating />
{isEnhancedLayout && <Yrbl className="yrbl--enhanced" />} {isEnhancedLayout && <Yrbl className="yrbl--enhanced" />}
{showUpgradeButton && ( {showUpgradeButton && !isUpdateModalDisplayed && (
<Nag <Nag
message={__('An upgrade is available.')} message={__('An upgrade is available.')}
actionText={__('Install Now')} actionText={__('Install Now')}

View file

@ -0,0 +1,17 @@
import SettingDisableAutoUpdates from './view';
import * as SETTINGS from 'constants/settings';
import { connect } from 'react-redux';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { doSetClientSetting } from 'redux/actions/settings';
const select = (state) => {
return {
disableAutoUpdates: makeSelectClientSetting(SETTINGS.DISABLE_AUTO_UPDATES)(state),
};
};
const perform = (dispatch) => ({
setClientSetting: (value) => dispatch(doSetClientSetting(SETTINGS.DISABLE_AUTO_UPDATES, value)),
});
export default connect(select, perform)(SettingDisableAutoUpdates);

View file

@ -0,0 +1,30 @@
// @flow
import React from 'react';
import * as remote from '@electron/remote';
import { FormField } from 'component/common/form';
const { autoUpdater } = remote.require('electron-updater');
type Props = {
setClientSetting: (boolean) => void,
disableAutoUpdates: boolean,
};
function SettingDisableAutoUpdates(props: Props) {
const { setClientSetting, disableAutoUpdates } = props;
return (
<React.Fragment>
<FormField
type="checkbox"
name="autoupdates"
onChange={() => {
const newDisableAutoUpdates = !disableAutoUpdates;
autoUpdater.autoDownload = !newDisableAutoUpdates;
setClientSetting(newDisableAutoUpdates);
}}
checked={disableAutoUpdates}
/>
</React.Fragment>
);
}
export default SettingDisableAutoUpdates;

View file

@ -18,6 +18,7 @@ import Spinner from 'component/spinner';
import { getPasswordFromCookie } from 'util/saved-passwords'; import { getPasswordFromCookie } from 'util/saved-passwords';
import * as DAEMON_SETTINGS from 'constants/daemon_settings'; import * as DAEMON_SETTINGS from 'constants/daemon_settings';
import SettingEnablePrereleases from 'component/settingEnablePrereleases'; import SettingEnablePrereleases from 'component/settingEnablePrereleases';
import SettingDisableAutoUpdates from 'component/settingDisableAutoUpdates';
const IS_MAC = process.platform === 'darwin'; const IS_MAC = process.platform === 'darwin';
@ -222,6 +223,14 @@ export default function SettingSystem(props: Props) {
> >
<SettingEnablePrereleases /> <SettingEnablePrereleases />
</SettingsRow> </SettingsRow>
<SettingsRow
title={__('Disable automatic updates')}
subtitle={__(
"Preven't new updates to be downloaded automatically in the background (we will keep notifying you if there is an update)"
)}
>
<SettingDisableAutoUpdates />
</SettingsRow>
<SettingsRow <SettingsRow
title={ title={
<span> <span>

View file

@ -45,6 +45,7 @@ export const CUSTOM_COMMENTS_SERVERS = 'custom_comments_servers';
export const CUSTOM_SHARE_URL_ENABLED = 'custom_share_url_enabled'; export const CUSTOM_SHARE_URL_ENABLED = 'custom_share_url_enabled';
export const CUSTOM_SHARE_URL = 'custom_share_url'; export const CUSTOM_SHARE_URL = 'custom_share_url';
export const ENABLE_PRERELEASE_UPDATES = 'enable_prerelease_updates'; export const ENABLE_PRERELEASE_UPDATES = 'enable_prerelease_updates';
export const DISABLE_AUTO_UPDATES = 'disable_auto_updates';
export const SETTINGS_GRP = { export const SETTINGS_GRP = {
APPEARANCE: 'appearance', APPEARANCE: 'appearance',

View file

@ -200,6 +200,12 @@ function AppWrapper() {
useEffect(() => { useEffect(() => {
// @if TARGET='app' // @if TARGET='app'
// Enable/disable automatic updates download based on user's settings.
const state = store.getState();
const autoUpdatesDisabled = makeSelectClientSetting(SETTINGS.DISABLE_AUTO_UPDATES)(state);
autoUpdater.autoDownload = !autoUpdatesDisabled;
moment.locale(remote.app.getLocale()); moment.locale(remote.app.getLocale());
autoUpdater.on('error', (error) => { autoUpdater.on('error', (error) => {

View file

@ -1,5 +1,6 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { selectClaimsById, selectMyChannelClaims, selectTotalStakedAmountForChannelUri } from 'redux/selectors/claims'; import { selectClaimsById, selectMyChannelClaims, selectTotalStakedAmountForChannelUri } from 'redux/selectors/claims';
import { AUTO_UPDATE_DOWNLOADED } from 'constants/modal_types';
export const selectState = (state) => state.app || {}; export const selectState = (state) => state.app || {};
@ -51,6 +52,10 @@ export const selectAutoUpdateDownloaded = createSelector(selectState, (state) =>
export const selectAutoUpdateDeclined = createSelector(selectState, (state) => state.autoUpdateDeclined); export const selectAutoUpdateDeclined = createSelector(selectState, (state) => state.autoUpdateDeclined);
export const selectIsUpdateModelDisplayed = createSelector(selectState, (state) => {
return state.modal === AUTO_UPDATE_DOWNLOADED;
});
export const selectDaemonVersionMatched = createSelector(selectState, (state) => state.daemonVersionMatched); export const selectDaemonVersionMatched = createSelector(selectState, (state) => state.daemonVersionMatched);
export const selectVolume = createSelector(selectState, (state) => state.volume); export const selectVolume = createSelector(selectState, (state) => state.volume);