Compare commits
14 commits
master
...
update-tes
Author | SHA1 | Date | |
---|---|---|---|
|
e54dded90f | ||
|
11effeda92 | ||
|
fd457701b5 | ||
|
44bb539cfb | ||
|
11161dbcb3 | ||
|
f094350a79 | ||
|
d2648c9f62 | ||
|
50fc724b53 | ||
|
4eb3fd70e1 | ||
|
8a7d89c857 | ||
|
441628bac8 | ||
|
4b922d47f6 | ||
|
0ea5d01062 | ||
|
6e97fbf5f8 |
17 changed files with 1359 additions and 1080 deletions
0
.yarn/versions/145a6c62.yml
vendored
Normal file
0
.yarn/versions/145a6c62.yml
vendored
Normal file
|
@ -29,6 +29,10 @@
|
||||||
"from": "./static/font",
|
"from": "./static/font",
|
||||||
"to": "static/font",
|
"to": "static/font",
|
||||||
"filter": ["**/*"]
|
"filter": ["**/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "./static/app-update.yml",
|
||||||
|
"to": "app-update.yml"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"publish": [
|
"publish": [
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { diskSpaceLinux, diskSpaceWindows, diskSpaceMac } from '../ui/util/disks
|
||||||
const { download } = require('electron-dl');
|
const { download } = require('electron-dl');
|
||||||
const remote = require('@electron/remote/main');
|
const remote = require('@electron/remote/main');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
const sudo = require('sudo-prompt');
|
||||||
|
|
||||||
remote.initialize();
|
remote.initialize();
|
||||||
const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled');
|
const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled');
|
||||||
|
@ -33,29 +34,23 @@ try {
|
||||||
upgradeDisabled = false;
|
upgradeDisabled = false;
|
||||||
}
|
}
|
||||||
autoUpdater.autoDownload = !upgradeDisabled;
|
autoUpdater.autoDownload = !upgradeDisabled;
|
||||||
|
autoUpdater.allowPrerelease = false;
|
||||||
|
|
||||||
// This is set to true if an auto update has been downloaded through the Electron
|
const UPDATE_STATE_INIT = 0;
|
||||||
// auto-update system and is ready to install. If the user declined an update earlier,
|
const UPDATE_STATE_CHECKING = 1;
|
||||||
// it will still install on shutdown.
|
const UPDATE_STATE_UPDATES_FOUND = 2;
|
||||||
let autoUpdateDownloaded = false;
|
const UPDATE_STATE_NO_UPDATES_FOUND = 3;
|
||||||
|
const UPDATE_STATE_DOWNLOADING = 4;
|
||||||
|
const UPDATE_STATE_DOWNLOADED = 5;
|
||||||
|
let updateState = UPDATE_STATE_INIT;
|
||||||
|
let updateDownloadItem;
|
||||||
|
|
||||||
|
const isAutoUpdateSupported = ['win32', 'darwin'].includes(process.platform) || !!process.env.APPIMAGE;
|
||||||
|
|
||||||
// This is used to keep track of whether we are showing the special dialog
|
// This is used to keep track of whether we are showing the special dialog
|
||||||
// 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 when
|
|
||||||
// using the auto updater API.
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Auto updater doesn't support Linux installations (only trough AppImages)
|
|
||||||
// this is why, for that case, we download a full executable (.deb package)
|
|
||||||
// as a fallback support. This variable will be used to prevent
|
|
||||||
// multiple downloads when auto updater isn't supported.
|
|
||||||
let downloadUpgradeInProgress = false;
|
|
||||||
|
|
||||||
// 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;
|
||||||
|
@ -243,7 +238,8 @@ app.on('activate', () => {
|
||||||
app.on('will-quit', event => {
|
app.on('will-quit', event => {
|
||||||
if (
|
if (
|
||||||
process.platform === 'win32' &&
|
process.platform === 'win32' &&
|
||||||
autoUpdateDownloaded &&
|
updateState === UPDATE_STATE_DOWNLOADED &&
|
||||||
|
isAutoUpdateSupported &&
|
||||||
!appState.autoUpdateAccepted &&
|
!appState.autoUpdateAccepted &&
|
||||||
!showingAutoUpdateCloseAlert
|
!showingAutoUpdateCloseAlert
|
||||||
) {
|
) {
|
||||||
|
@ -327,87 +323,6 @@ ipcMain.on('get-disk-space', async (event) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('download-upgrade', async (event, params) => {
|
|
||||||
if (downloadUpgradeInProgress) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { url, options } = params;
|
|
||||||
const dir = fs.mkdtempSync(app.getPath('temp') + path.sep);
|
|
||||||
options.onProgress = function(p) {
|
|
||||||
rendererWindow.webContents.send('download-progress-update', p);
|
|
||||||
};
|
|
||||||
options.directory = dir;
|
|
||||||
options.onCompleted = function(c) {
|
|
||||||
downloadUpgradeInProgress = false;
|
|
||||||
rendererWindow.webContents.send('download-update-complete', c);
|
|
||||||
};
|
|
||||||
const win = BrowserWindow.getFocusedWindow();
|
|
||||||
downloadUpgradeInProgress = true;
|
|
||||||
await download(win, url, options).catch(e => console.log('e', e));
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on('upgrade', (event, installerPath) => {
|
|
||||||
app.on('quit', () => {
|
|
||||||
console.log('Launching upgrade installer at', installerPath);
|
|
||||||
// This gets triggered called after *all* other quit-related events, so
|
|
||||||
// we'll only get here if we're fully prepared and quitting for real.
|
|
||||||
shell.openPath(installerPath);
|
|
||||||
});
|
|
||||||
// what to do if no shutdown in a long time?
|
|
||||||
console.log('Update downloaded to', installerPath);
|
|
||||||
console.log('The app will close and you will be prompted to install the latest version of LBRY.');
|
|
||||||
console.log('After the install is complete, please reopen the app.');
|
|
||||||
app.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on('check-for-updates', (event, autoDownload) => {
|
|
||||||
// Prevent downloading the same update multiple times.
|
|
||||||
if (!keepCheckingForUpdates) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
keepCheckingForUpdates = false;
|
|
||||||
autoUpdater.autoDownload = autoDownload;
|
|
||||||
autoUpdater.checkForUpdates();
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on('update-downloaded', () => {
|
|
||||||
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', () => {
|
|
||||||
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();
|
|
||||||
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', () => {
|
||||||
function formatRc(ver) {
|
function formatRc(ver) {
|
||||||
// Adds dash if needed to make RC suffix SemVer friendly
|
// Adds dash if needed to make RC suffix SemVer friendly
|
||||||
|
@ -500,3 +415,151 @@ process.on('uncaughtException', error => {
|
||||||
if (daemon) daemon.quit();
|
if (daemon) daemon.quit();
|
||||||
app.exit(1);
|
app.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Auto updater
|
||||||
|
// autoUpdater.on('download-progress', (p) => {
|
||||||
|
// rendererWindow.webContents.send('download-progress-update', p);
|
||||||
|
// });
|
||||||
|
|
||||||
|
autoUpdater.on('update-downloaded', () => {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADED;
|
||||||
|
|
||||||
|
// 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-available', () => {
|
||||||
|
updateState = UPDATE_STATE_UPDATES_FOUND;
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-not-available', () => {
|
||||||
|
updateState = UPDATE_STATE_NO_UPDATES_FOUND;
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('error', () => {
|
||||||
|
updateState = UPDATE_STATE_INIT;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Manual (.deb) update
|
||||||
|
ipcMain.on('cancel-download-upgrade', () => {
|
||||||
|
if (updateDownloadItem) {
|
||||||
|
// Cancel the download and execute the onCancel
|
||||||
|
// callback set in the options.
|
||||||
|
updateDownloadItem.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('download-upgrade', (event, params) => {
|
||||||
|
if (updateState !== UPDATE_STATE_UPDATES_FOUND) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isAutoUpdateSupported) {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
autoUpdater.downloadUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { url, options } = params;
|
||||||
|
const dir = fs.mkdtempSync(app.getPath('temp') + path.sep);
|
||||||
|
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
|
||||||
|
// Grab the download item's handler to allow
|
||||||
|
// cancelling the operation if required.
|
||||||
|
options.onStarted = function(downloadItem) {
|
||||||
|
updateDownloadItem = downloadItem;
|
||||||
|
};
|
||||||
|
options.onCancel = function() {
|
||||||
|
updateState = UPDATE_STATE_UPDATES_FOUND;
|
||||||
|
updateDownloadItem = undefined;
|
||||||
|
};
|
||||||
|
options.onProgress = function(p) {
|
||||||
|
rendererWindow.webContents.send('download-progress-update', p);
|
||||||
|
};
|
||||||
|
options.onCompleted = function(c) {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADED;
|
||||||
|
updateDownloadItem = undefined;
|
||||||
|
rendererWindow.webContents.send('download-update-complete', c);
|
||||||
|
};
|
||||||
|
options.directory = dir;
|
||||||
|
const win = BrowserWindow.getFocusedWindow();
|
||||||
|
download(win, url, options).catch(e => {
|
||||||
|
updateState = UPDATE_STATE_UPDATES_FOUND;
|
||||||
|
console.log('e', e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update behavior
|
||||||
|
ipcMain.on('autoUpdateAccepted', () => {
|
||||||
|
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 (updateState === UPDATE_STATE_DOWNLOADED) {
|
||||||
|
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.
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
autoUpdater.downloadUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('check-for-updates', (event, autoDownload) => {
|
||||||
|
if (![UPDATE_STATE_INIT, UPDATE_STATE_NO_UPDATES_FOUND].includes(updateState)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState = UPDATE_STATE_CHECKING;
|
||||||
|
|
||||||
|
// If autoDownload is true, checkForUpdates will begin the
|
||||||
|
// download automatically.
|
||||||
|
if (autoDownload) {
|
||||||
|
updateState = UPDATE_STATE_DOWNLOADING;
|
||||||
|
}
|
||||||
|
|
||||||
|
autoUpdater.autoDownload = autoDownload;
|
||||||
|
autoUpdater.checkForUpdates();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('upgrade', (event, installerPath) => {
|
||||||
|
// what to do if no shutdown in a long time?
|
||||||
|
console.log('Update downloaded to', installerPath);
|
||||||
|
console.log('The app will close and you will be prompted to install the latest version of LBRY.');
|
||||||
|
console.log('After the install is complete, please reopen the app.');
|
||||||
|
|
||||||
|
// Prevent .deb package from opening with archive manager (Ubuntu >= 20)
|
||||||
|
if (process.platform === 'linux' && !process.env.APPIMAGE) {
|
||||||
|
sudo.exec(`dpkg -i ${installerPath}`, { name: app.name }, (err, stdout, stderr) => {
|
||||||
|
if (err || stderr) {
|
||||||
|
rendererWindow.webContents.send('upgrade-installing-error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-launch the application when the installation finishes.
|
||||||
|
app.relaunch();
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('quit', () => {
|
||||||
|
console.log('Launching upgrade installer at', installerPath);
|
||||||
|
// This gets triggered called after *all* other quit-related events, so
|
||||||
|
// we'll only get here if we're fully prepared and quitting for real.
|
||||||
|
shell.openPath(installerPath);
|
||||||
|
});
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "lbry",
|
"name": "lbry",
|
||||||
"version": "0.53.4",
|
"version": "0.53.5-alpha.test7495b3",
|
||||||
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
|
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"lbry"
|
"lbry"
|
||||||
|
@ -67,6 +67,7 @@
|
||||||
"remove-markdown": "^0.3.0",
|
"remove-markdown": "^0.3.0",
|
||||||
"rss": "^1.2.2",
|
"rss": "^1.2.2",
|
||||||
"source-map-explorer": "^2.5.2",
|
"source-map-explorer": "^2.5.2",
|
||||||
|
"sudo-prompt": "^9.2.1",
|
||||||
"tempy": "^0.6.0",
|
"tempy": "^0.6.0",
|
||||||
"videojs-logo": "^2.1.4"
|
"videojs-logo": "^2.1.4"
|
||||||
},
|
},
|
||||||
|
@ -243,5 +244,6 @@
|
||||||
"lbrynetDaemonDir": "static/daemon",
|
"lbrynetDaemonDir": "static/daemon",
|
||||||
"lbrynetDaemonFileName": "lbrynet"
|
"lbrynetDaemonFileName": "lbrynet"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@3.2.0"
|
"packageManager": "yarn@3.2.0",
|
||||||
|
"stableVersion": "0.53.4"
|
||||||
}
|
}
|
||||||
|
|
3
static/app-update.yml
Normal file
3
static/app-update.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
owner: lbryio
|
||||||
|
repo: lbry-desktop
|
||||||
|
provider: github
|
|
@ -1,3 +1,11 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectRemoteVersion } from 'redux/selectors/app';
|
||||||
import LastReleaseChanges from './view';
|
import LastReleaseChanges from './view';
|
||||||
|
|
||||||
export default LastReleaseChanges;
|
const select = (state) => ({
|
||||||
|
releaseVersion: selectRemoteVersion(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = {};
|
||||||
|
|
||||||
|
export default connect(select, perform)(LastReleaseChanges);
|
||||||
|
|
|
@ -5,11 +5,12 @@ import Button from 'component/button';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
releaseVersion: string,
|
||||||
hideReleaseVersion?: boolean,
|
hideReleaseVersion?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const LastReleaseChanges = (props: Props) => {
|
const LastReleaseChanges = (props: Props) => {
|
||||||
const { hideReleaseVersion } = props;
|
const { hideReleaseVersion, releaseVersion } = props;
|
||||||
const [releaseTag, setReleaseTag] = useState('');
|
const [releaseTag, setReleaseTag] = useState('');
|
||||||
const [releaseChanges, setReleaseChanges] = useState('');
|
const [releaseChanges, setReleaseChanges] = useState('');
|
||||||
const [fetchingReleaseChanges, setFetchingReleaseChanges] = useState(false);
|
const [fetchingReleaseChanges, setFetchingReleaseChanges] = useState(false);
|
||||||
|
@ -35,7 +36,7 @@ const LastReleaseChanges = (props: Props) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const lastReleaseUrl = 'https://api.github.com/repos/lbryio/lbry-desktop/releases/latest';
|
const lastReleaseUrl = `https://api.github.com/repos/lbryio/lbry-desktop/releases/tags/${releaseVersion}`;
|
||||||
const options = {
|
const options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: { Accept: 'application/vnd.github.v3+json' },
|
headers: { Accept: 'application/vnd.github.v3+json' },
|
||||||
|
@ -54,7 +55,7 @@ const LastReleaseChanges = (props: Props) => {
|
||||||
setFetchingReleaseChanges(false);
|
setFetchingReleaseChanges(false);
|
||||||
setFetchReleaseFailed(true);
|
setFetchReleaseFailed(true);
|
||||||
});
|
});
|
||||||
}, []);
|
}, [releaseVersion, setFetchingReleaseChanges, setReleaseTag, setReleaseChanges, setFetchReleaseFailed]);
|
||||||
|
|
||||||
if (fetchingReleaseChanges) {
|
if (fetchingReleaseChanges) {
|
||||||
return <p>{__('Loading...')}</p>;
|
return <p>{__('Loading...')}</p>;
|
||||||
|
|
|
@ -45,6 +45,8 @@ export const DOWNLOAD_UPGRADE = 'DOWNLOAD_UPGRADE';
|
||||||
export const UPGRADE_DOWNLOAD_STARTED = 'UPGRADE_DOWNLOAD_STARTED';
|
export const UPGRADE_DOWNLOAD_STARTED = 'UPGRADE_DOWNLOAD_STARTED';
|
||||||
export const UPGRADE_DOWNLOAD_COMPLETED = 'UPGRADE_DOWNLOAD_COMPLETED';
|
export const UPGRADE_DOWNLOAD_COMPLETED = 'UPGRADE_DOWNLOAD_COMPLETED';
|
||||||
export const UPGRADE_DOWNLOAD_PROGRESSED = 'UPGRADE_DOWNLOAD_PROGRESSED';
|
export const UPGRADE_DOWNLOAD_PROGRESSED = 'UPGRADE_DOWNLOAD_PROGRESSED';
|
||||||
|
export const UPGRADE_INIT_INSTALL = 'UPGRADE_INIT_INSTALL';
|
||||||
|
export const UPGRADE_INSTALL_ERROR = 'UPGRADE_INSTALL_ERROR';
|
||||||
export const CHECK_UPGRADE_AVAILABLE = 'CHECK_UPGRADE_AVAILABLE';
|
export const CHECK_UPGRADE_AVAILABLE = 'CHECK_UPGRADE_AVAILABLE';
|
||||||
export const CHECK_UPGRADE_START = 'CHECK_UPGRADE_START';
|
export const CHECK_UPGRADE_START = 'CHECK_UPGRADE_START';
|
||||||
export const CHECK_UPGRADE_SUCCESS = 'CHECK_UPGRADE_SUCCESS';
|
export const CHECK_UPGRADE_SUCCESS = 'CHECK_UPGRADE_SUCCESS';
|
||||||
|
@ -55,6 +57,8 @@ export const UPDATE_REMOTE_VERSION = 'UPDATE_REMOTE_VERSION';
|
||||||
export const SKIP_UPGRADE = 'SKIP_UPGRADE';
|
export const SKIP_UPGRADE = 'SKIP_UPGRADE';
|
||||||
export const START_UPGRADE = 'START_UPGRADE';
|
export const START_UPGRADE = 'START_UPGRADE';
|
||||||
export const AUTO_UPDATE_DECLINED = 'AUTO_UPDATE_DECLINED';
|
export const AUTO_UPDATE_DECLINED = 'AUTO_UPDATE_DECLINED';
|
||||||
|
export const AUTO_UPDATE_RESET = 'AUTO_UPDATE_RESET';
|
||||||
|
export const AUTO_UPDATE_FAILED = 'AUTO_UPDATE_FAILED';
|
||||||
export const AUTO_UPDATE_DOWNLOADED = 'AUTO_UPDATE_DOWNLOADED';
|
export const AUTO_UPDATE_DOWNLOADED = 'AUTO_UPDATE_DOWNLOADED';
|
||||||
export const CLEAR_UPGRADE_TIMER = 'CLEAR_UPGRADE_TIMER';
|
export const CLEAR_UPGRADE_TIMER = 'CLEAR_UPGRADE_TIMER';
|
||||||
|
|
||||||
|
|
22
ui/index.jsx
22
ui/index.jsx
|
@ -21,6 +21,9 @@ import {
|
||||||
doToggle3PAnalytics,
|
doToggle3PAnalytics,
|
||||||
doUpdateDownloadProgress,
|
doUpdateDownloadProgress,
|
||||||
doNotifyUpdateAvailable,
|
doNotifyUpdateAvailable,
|
||||||
|
doShowUpgradeInstallationError,
|
||||||
|
doAutoUpdateReset,
|
||||||
|
doAutoUpdateFail,
|
||||||
} from 'redux/actions/app';
|
} from 'redux/actions/app';
|
||||||
import { isURIValid } from 'util/lbryURI';
|
import { isURIValid } from 'util/lbryURI';
|
||||||
import { setSearchApi } from 'redux/actions/search';
|
import { setSearchApi } from 'redux/actions/search';
|
||||||
|
@ -128,10 +131,27 @@ ipcRenderer.on('open-uri-requested', (event, url, newSession) => {
|
||||||
handleError();
|
handleError();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('checking-for-update', () => {
|
||||||
|
app.store.dispatch(doAutoUpdateReset());
|
||||||
|
});
|
||||||
|
|
||||||
autoUpdater.on('update-available', (e) => {
|
autoUpdater.on('update-available', (e) => {
|
||||||
|
app.store.dispatch(doAutoUpdateReset());
|
||||||
app.store.dispatch(doNotifyUpdateAvailable(e));
|
app.store.dispatch(doNotifyUpdateAvailable(e));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('update-downloaded', () => {
|
||||||
|
app.store.dispatch(doAutoUpdateReset());
|
||||||
|
});
|
||||||
|
|
||||||
|
autoUpdater.on('error', () => {
|
||||||
|
app.store.dispatch(doAutoUpdateFail());
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('upgrade-installing-error', () => {
|
||||||
|
app.store.dispatch(doShowUpgradeInstallationError());
|
||||||
|
});
|
||||||
|
|
||||||
ipcRenderer.on('download-progress-update', (e, p) => {
|
ipcRenderer.on('download-progress-update', (e, p) => {
|
||||||
app.store.dispatch(doUpdateDownloadProgress(Math.round(p.percent * 100)));
|
app.store.dispatch(doUpdateDownloadProgress(Math.round(p.percent * 100)));
|
||||||
});
|
});
|
||||||
|
@ -208,6 +228,8 @@ function AppWrapper() {
|
||||||
const enabled = makeSelectClientSetting(SETTINGS.ENABLE_PRERELEASE_UPDATES)(state);
|
const enabled = makeSelectClientSetting(SETTINGS.ENABLE_PRERELEASE_UPDATES)(state);
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
autoUpdater.allowPrerelease = true;
|
autoUpdater.allowPrerelease = true;
|
||||||
|
} else {
|
||||||
|
autoUpdater.allowPrerelease = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [persistDone]);
|
}, [persistDone]);
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doAutoUpdateDeclined, doHideModal } from 'redux/actions/app';
|
import { doAutoUpdateDeclined, doHideModal } from 'redux/actions/app';
|
||||||
|
import { selectAutoUpdateFailed } from 'redux/selectors/app';
|
||||||
import ModalAutoUpdateDownloaded from './view';
|
import ModalAutoUpdateDownloaded from './view';
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const select = (state, props) => ({
|
||||||
|
errorWhileUpdating: selectAutoUpdateFailed(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
closeModal: () => dispatch(doHideModal()),
|
closeModal: () => dispatch(doHideModal()),
|
||||||
declineAutoUpdate: () => dispatch(doAutoUpdateDeclined()),
|
declineAutoUpdate: () => dispatch(doAutoUpdateDeclined()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(select, perform)(ModalAutoUpdateDownloaded);
|
||||||
null,
|
|
||||||
perform
|
|
||||||
)(ModalAutoUpdateDownloaded);
|
|
||||||
|
|
|
@ -7,13 +7,15 @@ import { Modal } from 'modal/modal';
|
||||||
import LastReleaseChanges from 'component/lastReleaseChanges';
|
import LastReleaseChanges from 'component/lastReleaseChanges';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
closeModal: any => any,
|
closeModal: (any) => any,
|
||||||
declineAutoUpdate: () => any,
|
declineAutoUpdate: () => any,
|
||||||
|
errorWhileUpdating: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModalAutoUpdateDownloaded = (props: Props) => {
|
const ModalAutoUpdateDownloaded = (props: Props) => {
|
||||||
const { closeModal, declineAutoUpdate } = props;
|
const { closeModal, declineAutoUpdate, errorWhileUpdating } = props;
|
||||||
const [disabled, setDisabled] = useState(false);
|
const [disabled, setDisabled] = useState(false);
|
||||||
|
const isDownloading = disabled && !errorWhileUpdating;
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
setDisabled(true);
|
setDisabled(true);
|
||||||
|
@ -31,13 +33,14 @@ const ModalAutoUpdateDownloaded = (props: Props) => {
|
||||||
type="confirm"
|
type="confirm"
|
||||||
contentLabel={__('Upgrade Downloaded')}
|
contentLabel={__('Upgrade Downloaded')}
|
||||||
title={__('LBRY leveled up')}
|
title={__('LBRY leveled up')}
|
||||||
confirmButtonLabel={__('Upgrade Now')}
|
confirmButtonLabel={isDownloading ? __('Downloading...') : __('Upgrade Now')}
|
||||||
abortButtonLabel={__('Not Now')}
|
abortButtonLabel={__('Not Now')}
|
||||||
confirmButtonDisabled={disabled}
|
confirmButtonDisabled={isDownloading}
|
||||||
onConfirmed={handleConfirm}
|
onConfirmed={handleConfirm}
|
||||||
onAborted={handleAbort}
|
onAborted={handleAbort}
|
||||||
>
|
>
|
||||||
<LastReleaseChanges />
|
<LastReleaseChanges />
|
||||||
|
{errorWhileUpdating && <p>{__('There was an error while updating. Please try again.')}</p>}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doStartUpgrade, doCancelUpgrade, doHideModal } from 'redux/actions/app';
|
import { doStartUpgrade, doCancelUpgrade, doHideModal } from 'redux/actions/app';
|
||||||
import { selectDownloadProgress, selectDownloadComplete, selectUpgradeDownloadPath } from 'redux/selectors/app';
|
import {
|
||||||
|
selectDownloadProgress,
|
||||||
|
selectDownloadComplete,
|
||||||
|
selectUpgradeDownloadPath,
|
||||||
|
selectUpgradeInitialized,
|
||||||
|
selectUpgradeFailedInstallation,
|
||||||
|
} from 'redux/selectors/app';
|
||||||
import ModalDownloading from './view';
|
import ModalDownloading from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = (state) => ({
|
||||||
downloadProgress: selectDownloadProgress(state),
|
downloadProgress: selectDownloadProgress(state),
|
||||||
downloadComplete: selectDownloadComplete(state),
|
downloadComplete: selectDownloadComplete(state),
|
||||||
downloadItem: selectUpgradeDownloadPath(state),
|
downloadItem: selectUpgradeDownloadPath(state),
|
||||||
|
upgradeInitialized: selectUpgradeInitialized(state),
|
||||||
|
failedInstallation: selectUpgradeFailedInstallation(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = (dispatch) => ({
|
||||||
startUpgrade: () => dispatch(doStartUpgrade()),
|
startUpgrade: () => dispatch(doStartUpgrade()),
|
||||||
cancelUpgrade: () => {
|
cancelUpgrade: () => {
|
||||||
dispatch(doHideModal());
|
dispatch(doHideModal());
|
||||||
|
@ -17,7 +25,4 @@ const perform = dispatch => ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(select, perform)(ModalDownloading);
|
||||||
select,
|
|
||||||
perform
|
|
||||||
)(ModalDownloading);
|
|
||||||
|
|
|
@ -10,11 +10,21 @@ type Props = {
|
||||||
downloadItem: string,
|
downloadItem: string,
|
||||||
startUpgrade: () => void,
|
startUpgrade: () => void,
|
||||||
cancelUpgrade: () => void,
|
cancelUpgrade: () => void,
|
||||||
|
upgradeInitialized: boolean,
|
||||||
|
failedInstallation: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalDownloading extends React.PureComponent<Props> {
|
class ModalDownloading extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { downloadProgress, downloadComplete, downloadItem, startUpgrade, cancelUpgrade } = this.props;
|
const {
|
||||||
|
downloadProgress,
|
||||||
|
downloadComplete,
|
||||||
|
downloadItem,
|
||||||
|
startUpgrade,
|
||||||
|
cancelUpgrade,
|
||||||
|
upgradeInitialized,
|
||||||
|
failedInstallation,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal title={__('Downloading update')} isOpen contentLabel={__('Downloading update')} type="custom">
|
<Modal title={__('Downloading update')} isOpen contentLabel={__('Downloading update')} type="custom">
|
||||||
|
@ -40,9 +50,18 @@ class ModalDownloading extends React.PureComponent<Props> {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{failedInstallation && <p>{__('There was an error during installation. Please, try again.')}</p>}
|
||||||
|
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
{downloadComplete ? <Button button="primary" label={__('Begin Upgrade')} onClick={startUpgrade} /> : null}
|
{downloadComplete ? (
|
||||||
<Button button="link" label={__('Cancel')} onClick={cancelUpgrade} />
|
<Button
|
||||||
|
disabled={upgradeInitialized}
|
||||||
|
button="primary"
|
||||||
|
label={__(upgradeInitialized ? 'Installing, please wait...' : 'Begin Upgrade')}
|
||||||
|
onClick={startUpgrade}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Button disabled={upgradeInitialized} button="link" label={__('Cancel')} onClick={cancelUpgrade} />
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,7 +28,6 @@ import {
|
||||||
import {
|
import {
|
||||||
selectIsUpgradeSkipped,
|
selectIsUpgradeSkipped,
|
||||||
selectUpdateUrl,
|
selectUpdateUrl,
|
||||||
selectUpgradeDownloadItem,
|
|
||||||
selectUpgradeDownloadPath,
|
selectUpgradeDownloadPath,
|
||||||
selectAutoUpdateDeclined,
|
selectAutoUpdateDeclined,
|
||||||
selectRemoteVersion,
|
selectRemoteVersion,
|
||||||
|
@ -85,6 +84,15 @@ export function doStartUpgrade() {
|
||||||
const upgradeDownloadPath = selectUpgradeDownloadPath(state);
|
const upgradeDownloadPath = selectUpgradeDownloadPath(state);
|
||||||
|
|
||||||
ipcRenderer.send('upgrade', upgradeDownloadPath);
|
ipcRenderer.send('upgrade', upgradeDownloadPath);
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.UPGRADE_INIT_INSTALL,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doShowUpgradeInstallationError() {
|
||||||
|
return {
|
||||||
|
type: ACTIONS.UPGRADE_INSTALL_ERROR,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,25 +162,8 @@ export function doAutoUpdateDeclined() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doCancelUpgrade() {
|
export function doCancelUpgrade() {
|
||||||
return (dispatch, getState) => {
|
ipcRenderer.send('cancel-download-upgrade');
|
||||||
const state = getState();
|
return { type: ACTIONS.UPGRADE_CANCELLED };
|
||||||
const upgradeDownloadItem = selectUpgradeDownloadItem(state);
|
|
||||||
|
|
||||||
if (upgradeDownloadItem) {
|
|
||||||
/*
|
|
||||||
* Right now the remote reference to the download item gets garbage collected as soon as the
|
|
||||||
* the download is over (maybe even earlier), so trying to cancel a finished download may
|
|
||||||
* throw an error.
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
upgradeDownloadItem.cancel();
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err); // eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({ type: ACTIONS.UPGRADE_CANCELLED });
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doCheckUpgradeAvailable() {
|
export function doCheckUpgradeAvailable() {
|
||||||
|
@ -229,6 +220,18 @@ export function doNotifyUpdateAvailable(e) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doAutoUpdateReset() {
|
||||||
|
return {
|
||||||
|
type: ACTIONS.AUTO_UPDATE_RESET,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doAutoUpdateFail() {
|
||||||
|
return {
|
||||||
|
type: ACTIONS.AUTO_UPDATE_FAILED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initiate a timer that will check for an app upgrade every 10 minutes.
|
Initiate a timer that will check for an app upgrade every 10 minutes.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,6 +27,7 @@ export type AppState = {
|
||||||
badgeNumber: number,
|
badgeNumber: number,
|
||||||
volume: number,
|
volume: number,
|
||||||
autoUpdateDeclined: boolean,
|
autoUpdateDeclined: boolean,
|
||||||
|
autoUpdateFailed: boolean,
|
||||||
modalsAllowed: boolean,
|
modalsAllowed: boolean,
|
||||||
downloadProgress: ?number,
|
downloadProgress: ?number,
|
||||||
upgradeDownloading: ?boolean,
|
upgradeDownloading: ?boolean,
|
||||||
|
@ -64,9 +65,12 @@ const defaultState: AppState = {
|
||||||
muted: false,
|
muted: false,
|
||||||
autoUpdateDownloaded: false,
|
autoUpdateDownloaded: false,
|
||||||
autoUpdateDeclined: false,
|
autoUpdateDeclined: false,
|
||||||
|
autoUpdateFailed: false,
|
||||||
modalsAllowed: true,
|
modalsAllowed: true,
|
||||||
hasClickedComment: false,
|
hasClickedComment: false,
|
||||||
downloadProgress: undefined,
|
downloadProgress: undefined,
|
||||||
|
upgradeInitialized: false,
|
||||||
|
upgradeFailedInstallation: false,
|
||||||
upgradeDownloading: undefined,
|
upgradeDownloading: undefined,
|
||||||
upgradeDownloadComplete: undefined,
|
upgradeDownloadComplete: undefined,
|
||||||
checkUpgradeTimer: undefined,
|
checkUpgradeTimer: undefined,
|
||||||
|
@ -150,6 +154,16 @@ reducers[ACTIONS.AUTO_UPDATE_DECLINED] = (state) =>
|
||||||
autoUpdateDeclined: true,
|
autoUpdateDeclined: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.AUTO_UPDATE_RESET] = (state) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
autoUpdateFailed: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.AUTO_UPDATE_FAILED] = (state) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
autoUpdateFailed: true,
|
||||||
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.UPGRADE_DOWNLOAD_COMPLETED] = (state, action) =>
|
reducers[ACTIONS.UPGRADE_DOWNLOAD_COMPLETED] = (state, action) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
downloadPath: action.data.path,
|
downloadPath: action.data.path,
|
||||||
|
@ -162,6 +176,18 @@ reducers[ACTIONS.UPGRADE_DOWNLOAD_STARTED] = (state) =>
|
||||||
upgradeDownloading: true,
|
upgradeDownloading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.UPGRADE_INIT_INSTALL] = (state) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
upgradeInitialized: true,
|
||||||
|
upgradeFailedInstallation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.UPGRADE_INSTALL_ERROR] = (state) =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
upgradeInitialized: false,
|
||||||
|
upgradeFailedInstallation: true,
|
||||||
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.CHANGE_MODALS_ALLOWED] = (state, action) =>
|
reducers[ACTIONS.CHANGE_MODALS_ALLOWED] = (state, action) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
modalsAllowed: action.data.modalsAllowed,
|
modalsAllowed: action.data.modalsAllowed,
|
||||||
|
|
|
@ -6,12 +6,20 @@ export const selectState = (state) => state.app || {};
|
||||||
|
|
||||||
export const selectPlatform = createSelector(selectState, (state) => state.platform);
|
export const selectPlatform = createSelector(selectState, (state) => state.platform);
|
||||||
|
|
||||||
export const selectUpdateUrl = createSelector(selectPlatform, (platform) => {
|
export const selectRemoteVersion = createSelector(selectState, (state) => state.remoteVersion);
|
||||||
|
|
||||||
|
export const selectUpdateUrl = createSelector(selectPlatform, selectRemoteVersion, (platform, releaseVersion) => {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
return 'https://lbry.com/get/lbry.dmg';
|
return 'https://lbry.com/get/lbry.dmg';
|
||||||
case 'linux':
|
case 'linux':
|
||||||
return 'https://lbry.com/get/lbry.deb';
|
// releaseVersion can be used as the tag name
|
||||||
|
// Example: v0.53.5-alpha.test7495b
|
||||||
|
// When downloading, we need to remove the initial
|
||||||
|
// v, ending up with a file name like
|
||||||
|
// LBRY_0.53.5-alpha.test7495b.deb
|
||||||
|
const fileName = 'LBRY_' + (releaseVersion || '').replace('v', '') + '.deb';
|
||||||
|
return `https://github.com/lbryio/lbry-desktop/releases/download/${releaseVersion}/${fileName}`;
|
||||||
case 'win32':
|
case 'win32':
|
||||||
return 'https://lbry.com/get/lbry.exe';
|
return 'https://lbry.com/get/lbry.exe';
|
||||||
default:
|
default:
|
||||||
|
@ -21,10 +29,12 @@ export const selectUpdateUrl = createSelector(selectPlatform, (platform) => {
|
||||||
|
|
||||||
export const selectHasClickedComment = createSelector(selectState, (state) => state.hasClickedComment);
|
export const selectHasClickedComment = createSelector(selectState, (state) => state.hasClickedComment);
|
||||||
|
|
||||||
export const selectRemoteVersion = createSelector(selectState, (state) => state.remoteVersion);
|
|
||||||
|
|
||||||
export const selectIsUpgradeAvailable = createSelector(selectState, (state) => state.isUpgradeAvailable);
|
export const selectIsUpgradeAvailable = createSelector(selectState, (state) => state.isUpgradeAvailable);
|
||||||
|
|
||||||
|
export const selectUpgradeInitialized = createSelector(selectState, (state) => state.upgradeInitialized);
|
||||||
|
|
||||||
|
export const selectUpgradeFailedInstallation = createSelector(selectState, (state) => state.upgradeFailedInstallation);
|
||||||
|
|
||||||
export const selectUpgradeFilename = createSelector(selectPlatform, selectRemoteVersion, (platform, version) => {
|
export const selectUpgradeFilename = createSelector(selectPlatform, selectRemoteVersion, (platform, version) => {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
|
@ -46,12 +56,12 @@ export const selectIsUpgradeSkipped = createSelector(selectState, (state) => sta
|
||||||
|
|
||||||
export const selectUpgradeDownloadPath = createSelector(selectState, (state) => state.downloadPath);
|
export const selectUpgradeDownloadPath = createSelector(selectState, (state) => state.downloadPath);
|
||||||
|
|
||||||
export const selectUpgradeDownloadItem = createSelector(selectState, (state) => state.downloadItem);
|
|
||||||
|
|
||||||
export const selectAutoUpdateDownloaded = createSelector(selectState, (state) => state.autoUpdateDownloaded);
|
export const selectAutoUpdateDownloaded = createSelector(selectState, (state) => state.autoUpdateDownloaded);
|
||||||
|
|
||||||
export const selectAutoUpdateDeclined = createSelector(selectState, (state) => state.autoUpdateDeclined);
|
export const selectAutoUpdateDeclined = createSelector(selectState, (state) => state.autoUpdateDeclined);
|
||||||
|
|
||||||
|
export const selectAutoUpdateFailed = createSelector(selectState, (state) => state.autoUpdateFailed);
|
||||||
|
|
||||||
export const selectIsUpdateModalDisplayed = createSelector(selectState, (state) => {
|
export const selectIsUpdateModalDisplayed = createSelector(selectState, (state) => {
|
||||||
return [MODALS.AUTO_UPDATE_DOWNLOADED, MODALS.UPGRADE, MODALS.DOWNLOADING].includes(state.modal);
|
return [MODALS.AUTO_UPDATE_DOWNLOADED, MODALS.UPGRADE, MODALS.DOWNLOADING].includes(state.modal);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue