Merge pull request #808 from lbryio/auto-update
Auto Update with electron-updater (WIP)
This commit is contained in:
commit
896a894ee1
19 changed files with 366 additions and 45 deletions
|
@ -79,6 +79,12 @@ if [ "$FULL_BUILD" == "true" ]; then
|
|||
|
||||
yarn build
|
||||
|
||||
# Workaround: TeamCity expects the dmg to be in dist/mac, but in the new electron-builder
|
||||
# it's put directly in dist/ (the right way to solve this is to update the TeamCity config)
|
||||
if $OSX; then
|
||||
cp dist/*.dmg dist/mac
|
||||
fi
|
||||
|
||||
# electron-build has a publish feature, but I had a hard time getting
|
||||
# it to reliably work and it also seemed difficult to configure. Not proud of
|
||||
# this, but it seemed better to write my own.
|
||||
|
|
|
@ -9,48 +9,42 @@ import github
|
|||
import uritemplate
|
||||
import boto3
|
||||
|
||||
|
||||
def main():
|
||||
upload_to_github_if_tagged('lbryio/lbry-app')
|
||||
upload_to_s3('app')
|
||||
|
||||
|
||||
def get_asset_filename():
|
||||
def get_asset_path():
|
||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
system = platform.system()
|
||||
if system == 'Darwin':
|
||||
return glob.glob(this_dir + '/../dist/LBRY*.dmg')[0]
|
||||
suffix = 'dmg'
|
||||
elif system == 'Linux':
|
||||
return glob.glob(this_dir + '/../dist/LBRY*.deb')[0]
|
||||
suffix = 'deb'
|
||||
elif system == 'Windows':
|
||||
return glob.glob(this_dir + '/../dist/LBRY*.exe')[0]
|
||||
suffix = 'exe'
|
||||
else:
|
||||
raise Exception("I don't know about any artifact on {}".format(system))
|
||||
|
||||
return os.path.realpath(glob.glob(this_dir + '/../dist/LBRY*.' + suffix)[0])
|
||||
|
||||
def upload_to_s3(folder):
|
||||
tag = subprocess.check_output(['git', 'describe', '--always', '--abbrev=8', 'HEAD']).strip()
|
||||
commit_date = subprocess.check_output([
|
||||
'git', 'show', '-s', '--format=%cd', '--date=format:%Y%m%d-%H%I%S', 'HEAD']).strip()
|
||||
def get_update_asset_path():
|
||||
# Get the asset used used for updates. On Mac, this is a .zip; on
|
||||
# Windows it's just the installer file.
|
||||
if platform.system() == 'Darwin':
|
||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
return os.path.realpath(glob.glob(this_dir + '/../dist/LBRY*.zip')[0])
|
||||
else:
|
||||
return get_asset_path()
|
||||
|
||||
asset_path = get_asset_filename()
|
||||
bucket = 'releases.lbry.io'
|
||||
key = folder + '/' + commit_date + '-' + tag + '/' + os.path.basename(asset_path)
|
||||
|
||||
print "Uploading " + asset_path + " to s3://" + bucket + '/' + key + ''
|
||||
def get_latest_file_path():
|
||||
# The update metadata file is called latest.yml on Windows, latest-mac.yml on
|
||||
# Mac, latest-linux.yml on Linux
|
||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
if 'AWS_ACCESS_KEY_ID' not in os.environ or 'AWS_SECRET_ACCESS_KEY' not in os.environ:
|
||||
print 'Must set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to publish assets to s3'
|
||||
return 1
|
||||
|
||||
s3 = boto3.resource(
|
||||
's3',
|
||||
aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
|
||||
aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'],
|
||||
config=boto3.session.Config(signature_version='s3v4')
|
||||
)
|
||||
s3.meta.client.upload_file(asset_path, bucket, key)
|
||||
latestfilematches = glob.glob(this_dir + '/../dist/latest*.yml')
|
||||
|
||||
return latestfilematches[0] if latestfilematches else None
|
||||
|
||||
def upload_to_github_if_tagged(repo_name):
|
||||
try:
|
||||
|
@ -75,7 +69,7 @@ def upload_to_github_if_tagged(repo_name):
|
|||
# TODO: maybe this should be an error
|
||||
return 1
|
||||
|
||||
asset_path = get_asset_filename()
|
||||
asset_path = get_asset_path()
|
||||
print "Uploading " + asset_path + " to Github tag " + current_tag
|
||||
release = get_github_release(repo, current_tag)
|
||||
upload_asset_to_github(release, asset_path, gh_token)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
{
|
||||
"appId": "io.lbry.LBRY",
|
||||
"productName": "LBRY",
|
||||
"publish": {
|
||||
"provider": "s3",
|
||||
"bucket": "releases.lbry.io",
|
||||
"path": "app/latest"
|
||||
},
|
||||
"mac": {
|
||||
"category": "public.app-category.entertainment"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "LBRY",
|
||||
"version": "0.19.4",
|
||||
"version": "0.19.4-dev",
|
||||
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
|
||||
"homepage": "https://lbry.io/",
|
||||
"bugs": {
|
||||
|
@ -38,6 +38,9 @@
|
|||
"classnames": "^2.2.5",
|
||||
"country-data": "^0.0.31",
|
||||
"electron-dl": "^1.6.0",
|
||||
"electron-log": "^2.2.12",
|
||||
"electron-publisher-s3": "^19.47.0",
|
||||
"electron-updater": "^2.16.1",
|
||||
"formik": "^0.10.4",
|
||||
"from2": "^2.3.0",
|
||||
"install": "^0.10.2",
|
||||
|
|
|
@ -5,10 +5,25 @@ import SemVer from 'semver';
|
|||
import url from 'url';
|
||||
import https from 'https';
|
||||
import { shell, app, ipcMain, dialog } from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import Daemon from './Daemon';
|
||||
import Tray from './Tray';
|
||||
import createWindow from './createWindow';
|
||||
|
||||
autoUpdater.autoDownload = true;
|
||||
|
||||
// This is set to true if an auto update has been downloaded through the Electron
|
||||
// auto-update system and is ready to install. If the user declined an update earlier,
|
||||
// it will still install on shutdown.
|
||||
let autoUpdateDownloaded = false;
|
||||
|
||||
// Keeps track of whether the user has accepted an auto-update through the interface.
|
||||
let autoUpdateAccepted = false;
|
||||
|
||||
// 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.
|
||||
let showingAutoUpdateCloseAlert = false;
|
||||
|
||||
// Keep a global reference, if you don't, they will be closed automatically when the JavaScript
|
||||
// object is garbage collected.
|
||||
let rendererWindow;
|
||||
|
@ -68,7 +83,26 @@ app.on('activate', () => {
|
|||
if (!rendererWindow) rendererWindow = createWindow();
|
||||
});
|
||||
|
||||
app.on('will-quit', () => {
|
||||
app.on('will-quit', (event) => {
|
||||
if (process.platform === 'win32' && autoUpdateDownloaded && !autoUpdateAccepted && !showingAutoUpdateCloseAlert) {
|
||||
// We're on Win and have an update downloaded, but the user declined it (or closed
|
||||
// the app without accepting it). Now the user is closing the app, so the new update
|
||||
// will install. On Mac this is silent, but on Windows they get a confusing permission
|
||||
// escalation dialog, so we show Windows users a warning dialog first.
|
||||
|
||||
showingAutoUpdateCloseAlert = true;
|
||||
dialog.showMessageBox({
|
||||
type: 'info',
|
||||
title: 'LBRY Will Upgrade',
|
||||
message: 'LBRY has a pending upgrade. Please select "Yes" to install it on the prompt shown after this one.',
|
||||
}, () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
isQuitting = true;
|
||||
if (daemon) daemon.quit();
|
||||
});
|
||||
|
@ -108,6 +142,15 @@ ipcMain.on('upgrade', (event, installerPath) => {
|
|||
app.quit();
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', () => {
|
||||
autoUpdateDownloaded = true;
|
||||
})
|
||||
|
||||
ipcMain.on('autoUpdateAccepted', () => {
|
||||
autoUpdateAccepted = true;
|
||||
autoUpdater.quitAndInstall();
|
||||
});
|
||||
|
||||
ipcMain.on('version-info-requested', () => {
|
||||
function formatRc(ver) {
|
||||
// Adds dash if needed to make RC suffix SemVer friendly
|
||||
|
@ -195,4 +238,4 @@ const isSecondInstance = app.makeSingleInstance(argv => {
|
|||
|
||||
if (isSecondInstance) {
|
||||
app.exit();
|
||||
}
|
||||
}
|
|
@ -5,13 +5,14 @@ import { selectIsBackDisabled, selectIsForwardDisabled } from 'redux/selectors/n
|
|||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
|
||||
import Header from './view';
|
||||
import { selectIsUpgradeAvailable } from 'redux/selectors/app';
|
||||
import { doDownloadUpgrade } from 'redux/actions/app';
|
||||
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
||||
import { doDownloadUpgradeRequested } from 'redux/actions/app';
|
||||
|
||||
const select = state => ({
|
||||
isBackDisabled: selectIsBackDisabled(state),
|
||||
isForwardDisabled: selectIsForwardDisabled(state),
|
||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||
autoUpdateDownloaded: selectAutoUpdateDownloaded(state),
|
||||
balance: formatCredits(selectBalance(state) || 0, 2),
|
||||
});
|
||||
|
||||
|
@ -19,7 +20,7 @@ const perform = dispatch => ({
|
|||
navigate: path => dispatch(doNavigate(path)),
|
||||
back: () => dispatch(doHistoryBack()),
|
||||
forward: () => dispatch(doHistoryForward()),
|
||||
downloadUpgrade: () => dispatch(doDownloadUpgrade()),
|
||||
downloadUpgradeRequested: () => dispatch(doDownloadUpgradeRequested()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(Header);
|
||||
|
|
|
@ -10,8 +10,9 @@ export const Header = props => {
|
|||
isBackDisabled,
|
||||
isForwardDisabled,
|
||||
isUpgradeAvailable,
|
||||
autoUpdateDownloaded,
|
||||
navigate,
|
||||
downloadUpgrade,
|
||||
downloadUpgradeRequested,
|
||||
} = props;
|
||||
return (
|
||||
<header id="header">
|
||||
|
@ -86,9 +87,9 @@ export const Header = props => {
|
|||
title={__('Settings')}
|
||||
/>
|
||||
</div>
|
||||
{isUpgradeAvailable && (
|
||||
{(autoUpdateDownloaded || (process.platform === 'linux' && isUpgradeAvailable)) && (
|
||||
<Link
|
||||
onClick={() => downloadUpgrade()}
|
||||
onClick={() => downloadUpgradeRequested()}
|
||||
button="primary button--flat"
|
||||
icon="icon-arrow-up"
|
||||
label={__('Upgrade App')}
|
||||
|
|
|
@ -28,6 +28,8 @@ export const UPDATE_VERSION = 'UPDATE_VERSION';
|
|||
export const UPDATE_REMOTE_VERSION = 'UPDATE_REMOTE_VERSION';
|
||||
export const SKIP_UPGRADE = 'SKIP_UPGRADE';
|
||||
export const START_UPGRADE = 'START_UPGRADE';
|
||||
export const AUTO_UPDATE_DECLINED = 'AUTO_UPDATE_DECLINED';
|
||||
export const AUTO_UPDATE_DOWNLOADED = 'AUTO_UPDATE_DOWNLOADED';
|
||||
|
||||
// Wallet
|
||||
export const GET_NEW_ADDRESS_STARTED = 'GET_NEW_ADDRESS_STARTED';
|
||||
|
|
|
@ -2,6 +2,8 @@ export const CONFIRM_FILE_REMOVE = 'confirmFileRemove';
|
|||
export const INCOMPATIBLE_DAEMON = 'incompatibleDaemon';
|
||||
export const FILE_TIMEOUT = 'file_timeout';
|
||||
export const DOWNLOADING = 'downloading';
|
||||
export const AUTO_UPDATE_DOWNLOADED = "auto_update_downloaded";
|
||||
export const AUTO_UPDATE_CONFIRM = 'auto_update_confirm';
|
||||
export const ERROR = 'error';
|
||||
export const INSUFFICIENT_CREDITS = 'insufficient_credits';
|
||||
export const UPGRADE = 'upgrade';
|
||||
|
|
|
@ -9,7 +9,7 @@ import lbry from 'lbry';
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { doConditionalAuthNavigate, doDaemonReady, doShowSnackBar } from 'redux/actions/app';
|
||||
import { doConditionalAuthNavigate, doDaemonReady, doShowSnackBar, doAutoUpdate } from 'redux/actions/app';
|
||||
import { doUpdateIsNightAsync } from 'redux/actions/settings';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doDownloadLanguages } from 'redux/actions/settings';
|
||||
|
@ -18,6 +18,15 @@ import 'scss/all.scss';
|
|||
import store from 'store';
|
||||
import app from './app';
|
||||
|
||||
const { autoUpdater } = remote.require('electron-updater');
|
||||
|
||||
autoUpdater.logger = remote.require("electron-log");
|
||||
|
||||
window.addEventListener('contextmenu', event => {
|
||||
contextMenu(remote.getCurrentWindow(), event.x, event.y, app.env === 'development');
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
|
||||
if (uri && uri.startsWith('lbry://')) {
|
||||
if (uri.startsWith('lbry://?verify=')) {
|
||||
|
@ -91,6 +100,22 @@ document.addEventListener('click', event => {
|
|||
});
|
||||
|
||||
const init = () => {
|
||||
autoUpdater.on("update-downloaded", () => {
|
||||
app.store.dispatch(doAutoUpdate());
|
||||
});
|
||||
|
||||
if (["win32", "darwin"].includes(process.platform)) {
|
||||
autoUpdater.on("update-available", () => {
|
||||
console.log("Update available");
|
||||
});
|
||||
autoUpdater.on("update-not-available", () => {
|
||||
console.log("Update not available");
|
||||
});
|
||||
autoUpdater.on("update-downloaded", () => {
|
||||
console.log("Update downloaded");
|
||||
app.store.dispatch(doAutoUpdate());
|
||||
});
|
||||
}
|
||||
app.store.dispatch(doUpdateIsNightAsync());
|
||||
app.store.dispatch(doDownloadLanguages());
|
||||
|
||||
|
|
11
src/renderer/modal/modalAutoUpdateConfirm/index.js
Normal file
11
src/renderer/modal/modalAutoUpdateConfirm/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doCloseModal, doAutoUpdateDeclined } from "redux/actions/app";
|
||||
import ModalAutoUpdateConfirm from "./view";
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
declineAutoUpdate: () => dispatch(doAutoUpdateDeclined()),
|
||||
});
|
||||
|
||||
export default connect(null, perform)(ModalAutoUpdateConfirm);
|
44
src/renderer/modal/modalAutoUpdateConfirm/view.jsx
Normal file
44
src/renderer/modal/modalAutoUpdateConfirm/view.jsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from "react";
|
||||
import { Modal } from "modal/modal";
|
||||
import { Line } from "rc-progress";
|
||||
import Link from "component/link/index";
|
||||
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
class ModalAutoUpdateConfirm extends React.PureComponent {
|
||||
render() {
|
||||
const { closeModal, declineAutoUpdate } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={true}
|
||||
type="confirm"
|
||||
contentLabel={__("Update Downloaded")}
|
||||
confirmButtonLabel={__("Upgrade")}
|
||||
abortButtonLabel={__("Not now")}
|
||||
onConfirmed={() => {
|
||||
ipcRenderer.send("autoUpdateAccepted");
|
||||
}}
|
||||
onAborted={() => {
|
||||
declineAutoUpdate();
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
<section>
|
||||
<h3 className="text-center">{__("LBRY Update Ready")}</h3>
|
||||
<p>
|
||||
{__(
|
||||
'Your LBRY update is ready. Restart LBRY now to use it!'
|
||||
)}
|
||||
</p>
|
||||
<p className="meta text-center">
|
||||
{__('Want to know what has changed?')} See the{' '}
|
||||
<Link label={__('release notes')} href="https://github.com/lbryio/lbry-app/releases" />.
|
||||
</p>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalAutoUpdateConfirm;
|
11
src/renderer/modal/modalAutoUpdateDownloaded/index.js
Normal file
11
src/renderer/modal/modalAutoUpdateDownloaded/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { doCloseModal, doAutoUpdateDeclined } from "redux/actions/app";
|
||||
import ModalAutoUpdateDownloaded from "./view";
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
declineAutoUpdate: () => dispatch(doAutoUpdateDeclined()),
|
||||
});
|
||||
|
||||
export default connect(null, perform)(ModalAutoUpdateDownloaded);
|
41
src/renderer/modal/modalAutoUpdateDownloaded/view.jsx
Normal file
41
src/renderer/modal/modalAutoUpdateDownloaded/view.jsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
import { Modal } from "modal/modal";
|
||||
import { Line } from "rc-progress";
|
||||
import Link from "component/link/index";
|
||||
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
class ModalAutoUpdateDownloaded extends React.PureComponent {
|
||||
render() {
|
||||
const { closeModal, declineAutoUpdate } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={true}
|
||||
type="confirm"
|
||||
contentLabel={__("Update Downloaded")}
|
||||
confirmButtonLabel={__("Use it Now")}
|
||||
abortButtonLabel={__("Upgrade on Close")}
|
||||
onConfirmed={() => {
|
||||
ipcRenderer.send("autoUpdateAccepted");
|
||||
}}
|
||||
onAborted={() => {
|
||||
declineAutoUpdate();
|
||||
ipcRenderer.send("autoUpdateDeclined");
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
<section>
|
||||
<h3 className="text-center">{__("LBRY Leveled Up")}</h3>
|
||||
<p>
|
||||
{__(
|
||||
'A new version of LBRY has been released, downloaded, and is ready for you to use pending a restart.'
|
||||
)}
|
||||
</p>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalAutoUpdateDownloaded;
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import * as settings from 'constants/settings';
|
||||
import { selectCurrentModal, selectModalProps } from 'redux/selectors/app';
|
||||
import { selectCurrentModal, selectModalProps, selectModalsAllowed } from 'redux/selectors/app';
|
||||
import { selectCurrentPage } from 'redux/selectors/navigation';
|
||||
import { selectCostForCurrentPageUri } from 'redux/selectors/cost_info';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
|
@ -24,6 +24,7 @@ const select = (state, props) => ({
|
|||
),
|
||||
isWelcomeAcknowledged: makeSelectClientSetting(settings.NEW_USER_ACKNOWLEDGED)(state),
|
||||
user: selectUser(state),
|
||||
modalsAllowed: selectModalsAllowed(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
|
|
|
@ -2,6 +2,8 @@ import React from 'react';
|
|||
import ModalError from 'modal/modalError';
|
||||
import ModalAuthFailure from 'modal/modalAuthFailure';
|
||||
import ModalDownloading from 'modal/modalDownloading';
|
||||
import ModalAutoUpdateDownloaded from 'modal/modalAutoUpdateDownloaded';
|
||||
import ModalAutoUpdateConfirm from 'modal/modalAutoUpdateConfirm';
|
||||
import ModalUpgrade from 'modal/modalUpgrade';
|
||||
import ModalWelcome from 'modal/modalWelcome';
|
||||
import ModalFirstReward from 'modal/modalFirstReward';
|
||||
|
@ -96,13 +98,17 @@ class ModalRouter extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { modal, modalProps } = this.props;
|
||||
const { modal, modalsAllowed, modalProps } = this.props;
|
||||
|
||||
switch (modal) {
|
||||
case modals.UPGRADE:
|
||||
return <ModalUpgrade {...modalProps} />;
|
||||
case modals.DOWNLOADING:
|
||||
return <ModalDownloading {...modalProps} />;
|
||||
case modals.AUTO_UPDATE_DOWNLOADED:
|
||||
return <ModalAutoUpdateDownloaded {...modalProps} />;
|
||||
case modals.AUTO_UPDATE_CONFIRM:
|
||||
return <ModalAutoUpdateConfirm {...modalProps} />;
|
||||
case modals.ERROR:
|
||||
return <ModalError {...modalProps} />;
|
||||
case modals.FILE_TIMEOUT:
|
||||
|
|
|
@ -10,6 +10,8 @@ import { doAuthNavigate } from 'redux/actions/navigation';
|
|||
import { doFetchDaemonSettings } from 'redux/actions/settings';
|
||||
import { doAuthenticate } from 'redux/actions/user';
|
||||
import { doBalanceSubscribe } from 'redux/actions/wallet';
|
||||
import { doPause } from "redux/actions/media";
|
||||
|
||||
import {
|
||||
selectCurrentModal,
|
||||
selectIsUpgradeSkipped,
|
||||
|
@ -18,8 +20,10 @@ import {
|
|||
selectUpgradeDownloadItem,
|
||||
selectUpgradeDownloadPath,
|
||||
selectUpgradeFilename,
|
||||
selectAutoUpdateDeclined,
|
||||
} from 'redux/selectors/app';
|
||||
|
||||
const { autoUpdater } = remote.require('electron-updater');
|
||||
const { download } = remote.require('electron-dl');
|
||||
const Fs = remote.require('fs');
|
||||
const { lbrySettings: config } = require('package.json');
|
||||
|
@ -66,6 +70,41 @@ export function doStartUpgrade() {
|
|||
};
|
||||
}
|
||||
|
||||
export function doDownloadUpgradeRequested() {
|
||||
// This means the user requested an upgrade by clicking the "upgrade" button in the navbar.
|
||||
// If on Mac and Windows, we do some new behavior for the auto-update system.
|
||||
// This will probably be reorganized once we get auto-update going on Linux and remove
|
||||
// the old logic.
|
||||
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
||||
// Pause video if needed
|
||||
dispatch(doPause());
|
||||
|
||||
const autoUpdateDeclined = selectAutoUpdateDeclined(state);
|
||||
|
||||
if (['win32', 'darwin'].includes(process.platform)) { // electron-updater behavior
|
||||
if (autoUpdateDeclined) {
|
||||
// The user declined an update before, so show the "confirm" dialog
|
||||
dispatch({
|
||||
type: ACTIONS.OPEN_MODAL,
|
||||
data: { modal: MODALS.AUTO_UPDATE_CONFIRM },
|
||||
});
|
||||
} else {
|
||||
// The user was never shown the original update dialog (e.g. because they were
|
||||
// watching a video). So show the inital "update downloaded" dialog.
|
||||
dispatch({
|
||||
type: ACTIONS.OPEN_MODAL,
|
||||
data: { modal: MODALS.AUTO_UPDATE_DOWNLOADED },
|
||||
});
|
||||
}
|
||||
} else { // Old behavior for Linux
|
||||
dispatch(doDownloadUpgrade());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doDownloadUpgrade() {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
@ -105,6 +144,29 @@ export function doDownloadUpgrade() {
|
|||
};
|
||||
}
|
||||
|
||||
export function doAutoUpdate() {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState();
|
||||
dispatch({
|
||||
type: ACTIONS.AUTO_UPDATE_DOWNLOADED,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.OPEN_MODAL,
|
||||
data: { modal: MODALS.AUTO_UPDATE_DOWNLOADED },
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function doAutoUpdateDeclined() {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState();
|
||||
dispatch({
|
||||
type: ACTIONS.AUTO_UPDATE_DECLINED,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function doCancelUpgrade() {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
@ -135,6 +197,17 @@ export function doCheckUpgradeAvailable() {
|
|||
type: ACTIONS.CHECK_UPGRADE_START,
|
||||
});
|
||||
|
||||
if (["win32", "darwin"].includes(process.platform)) {
|
||||
// On Windows and Mac, updates happen silently through
|
||||
// electron-updater.
|
||||
const autoUpdateDeclined = selectAutoUpdateDeclined(state);
|
||||
|
||||
if (!autoUpdateDeclined) {
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const success = ({ remoteVersion, upgradeAvailable }) => {
|
||||
dispatch({
|
||||
type: ACTIONS.CHECK_UPGRADE_SUCCESS,
|
||||
|
|
|
@ -26,6 +26,8 @@ export type AppState = {
|
|||
hasSignature: boolean,
|
||||
badgeNumber: number,
|
||||
volume: number,
|
||||
autoUpdateDeclined: boolean,
|
||||
modalsAllowed: boolean,
|
||||
downloadProgress: ?number,
|
||||
upgradeDownloading: ?boolean,
|
||||
upgradeDownloadComplete: ?boolean,
|
||||
|
@ -46,6 +48,9 @@ const defaultState: AppState = {
|
|||
hasSignature: false,
|
||||
badgeNumber: 0,
|
||||
volume: Number(sessionStorage.getItem('volume')) || 1,
|
||||
autoUpdateDownloaded: false,
|
||||
autoUpdateDeclined: false,
|
||||
modalsAllowed: true,
|
||||
|
||||
downloadProgress: undefined,
|
||||
upgradeDownloading: undefined,
|
||||
|
@ -79,6 +84,17 @@ reducers[ACTIONS.UPGRADE_CANCELLED] = state =>
|
|||
modal: null,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.AUTO_UPDATE_DOWNLOADED] = state =>
|
||||
Object.assign({}, state, {
|
||||
autoUpdateDownloaded: true,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.AUTO_UPDATE_DECLINED] = state => {
|
||||
return Object.assign({}, state, {
|
||||
autoUpdateDeclined: true,
|
||||
});
|
||||
}
|
||||
|
||||
reducers[ACTIONS.UPGRADE_DOWNLOAD_COMPLETED] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
downloadPath: action.data.path,
|
||||
|
@ -91,6 +107,11 @@ reducers[ACTIONS.UPGRADE_DOWNLOAD_STARTED] = state =>
|
|||
upgradeDownloading: true,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.CHANGE_MODALS_ALLOWED] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
modalsAllowed: action.data.modalsAllowed,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SKIP_UPGRADE] = state => {
|
||||
sessionStorage.setItem('upgradeSkipped', 'true');
|
||||
|
||||
|
@ -100,6 +121,28 @@ reducers[ACTIONS.SKIP_UPGRADE] = state => {
|
|||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.MEDIA_PLAY] = state => {
|
||||
return Object.assign({}, state, {
|
||||
modalsAllowed: false,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.MEDIA_PAUSE] = state => {
|
||||
return Object.assign({}, state, {
|
||||
modalsAllowed: true,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.SET_PLAYING_URI] = (state, action) => {
|
||||
if (action.data.uri === null) {
|
||||
return Object.assign({}, state, {
|
||||
modalsAllowed: true,
|
||||
});
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
reducers[ACTIONS.UPDATE_VERSION] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
version: action.data.version,
|
||||
|
@ -116,12 +159,16 @@ reducers[ACTIONS.CHECK_UPGRADE_SUBSCRIBE] = (state, action) =>
|
|||
checkUpgradeTimer: action.data.checkUpgradeTimer,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.OPEN_MODAL] = (state, action) =>
|
||||
Object.assign({}, state, {
|
||||
modal: action.data.modal,
|
||||
modalProps: action.data.modalProps || {},
|
||||
});
|
||||
|
||||
reducers[ACTIONS.OPEN_MODAL] = (state, action) => {
|
||||
if (!state.modalsAllowed) {
|
||||
return state;
|
||||
} else {
|
||||
return Object.assign({}, state, {
|
||||
modal: action.data.modal,
|
||||
modalProps: action.data.modalProps || {},
|
||||
});
|
||||
}
|
||||
};
|
||||
reducers[ACTIONS.CLOSE_MODAL] = state =>
|
||||
Object.assign({}, state, {
|
||||
modal: undefined,
|
||||
|
|
|
@ -56,6 +56,12 @@ export const selectUpgradeDownloadPath = createSelector(selectState, state => st
|
|||
|
||||
export const selectUpgradeDownloadItem = createSelector(selectState, state => state.downloadItem);
|
||||
|
||||
export const selectAutoUpdateDownloaded = createSelector(selectState, state => state.autoUpdateDownloaded);
|
||||
|
||||
export const selectAutoUpdateDeclined = createSelector(selectState, state => state.autoUpdateDeclined);
|
||||
|
||||
export const selectModalsAllowed = createSelector(selectState, state => state.modalsAllowed);
|
||||
|
||||
export const selectModalProps = createSelector(selectState, state => state.modalProps);
|
||||
|
||||
export const selectDaemonVersionMatched = createSelector(
|
||||
|
|
Loading…
Reference in a new issue