From 7bacff7b86ea01080cdf94976a4c344f47cbf3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Darko=20Miji=C4=87?= Date: Thu, 24 Sep 2020 09:13:07 +0100 Subject: [PATCH 1/3] [DDW-395] Update WBE and Node dependencies (#2187) * [DDW-395] Update WBE and Node dependencies to test saturation fix * cardano-address/cardano-cli: use correct attribute * [DDW-395] Update readme with instructions how to update cardano-node dependency * [DDW-395] Show saturation * [DDW-395] Fix delegation wizard selected pool styling * [DDW-395] Update saturation colors Co-authored-by: Samuel Leathers Co-authored-by: Nikola Glumac --- CHANGELOG.md | 1 + README.md | 1 + default.nix | 4 ++-- nix/sources.json | 14 +++++++------- source/main/environment.js | 2 +- .../components/staking/widgets/ThumbPoolContent.js | 12 ++++++------ .../staking/widgets/ThumbSelectedPool.scss | 2 +- .../app/components/staking/widgets/TooltipPool.js | 5 ++--- source/renderer/app/config/stakingConfig.js | 2 ++ source/renderer/app/utils/colors.js | 6 +++--- 10 files changed, 26 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3afd826c57..683f161c0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Changelog ### Chores +- Updated `cardano-wallet` to revision `ffeca1d9` which includes `cardano-node` 1.20.0 ([PR 2187](https://github.com/input-output-hk/daedalus/pull/2187)) - Set "Delegation center" as default staking screen ([PR 2185](https://github.com/input-output-hk/daedalus/pull/2185)) - Changed the ordering of wallet addresses on the "Receive" screen so that the oldest one are on top and the newest one on the bottom of the list ([PR 2176](https://github.com/input-output-hk/daedalus/pull/2176)) - Fixed UI issues ([PR 2152](https://github.com/input-output-hk/daedalus/pull/2152)) diff --git a/README.md b/README.md index 8851e6ca27..bc7d2e8fcc 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ Daedalus - Cryptocurrency Wallet Dependencies are updated with the follow nix commands: - Update to the latest master: `nix-shell -A devops --arg nivOnly true --run "niv update cardano-wallet"` - Update to a specific revision: `nix-shell -A devops --arg nivOnly true --run "niv update cardano-wallet -a rev=91db88f9195de49d4fb4299c68fc3f6de09856ab"` +- Update node to a specific tag: `nix-shell -A devops --arg nivOnly true --run "niv update cardano-node -b tags/1.20.0"` #### Notes diff --git a/default.nix b/default.nix index 9d456177a2..c37a0a5051 100644 --- a/default.nix +++ b/default.nix @@ -78,9 +78,9 @@ let }; cardano-wallet = import self.sources.cardano-wallet { inherit system; gitrev = self.sources.cardano-wallet.rev; crossSystem = crossSystem walletPkgs.lib; }; cardano-wallet-native = import self.sources.cardano-wallet { inherit system; gitrev = self.sources.cardano-wallet.rev; }; - cardano-address = (import self.sources.cardano-wallet { inherit system; gitrev = self.sources.cardano-wallet.rev; crossSystem = crossSystem walletPkgs.lib; }).haskellPackages.cardano-addresses.components.exes.cardano-address; + cardano-address = (import self.sources.cardano-wallet { inherit system; gitrev = self.sources.cardano-wallet.rev; crossSystem = crossSystem walletPkgs.lib; }).cardano-address; cardano-shell = import self.sources.cardano-shell { inherit system; crossSystem = crossSystem shellPkgs.lib; }; - cardano-cli = (import self.sources.cardano-node { inherit system; crossSystem = crossSystem nodePkgs.lib; }).haskellPackages.cardano-cli.components.exes.cardano-cli; + cardano-cli = (import self.sources.cardano-node { inherit system; crossSystem = crossSystem nodePkgs.lib; }).cardano-cli; cardano-node-cluster = let # Test wallets with known mnemonics walletTestGenesisYaml = (self.sources.cardano-wallet + "/lib/shelley/test/data/cardano-node-shelley/genesis.yaml"); diff --git a/nix/sources.json b/nix/sources.json index 921295b8e1..7aa96865ce 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -1,14 +1,14 @@ { "cardano-node": { - "branch": "master", + "branch": "tags/1.20.0", "description": null, "homepage": null, "owner": "input-output-hk", "repo": "cardano-node", - "rev": "9e06d6f9a293a9656bf6e31b7e5d0a9f498163d1", - "sha256": "1kh2rnis2r7g7a40pffnnkzj5mra25581a57b2fgq21jk0g42cby", + "rev": "1f2f51164b53b9b775c03ac9f1e23e7b70c74b05", + "sha256": "0ql57bhisnhs2dr2vdk8s4rwnff818lzvqw3l2780q16f20yypdg", "type": "tarball", - "url": "https://github.com/input-output-hk/cardano-node/archive/9e06d6f9a293a9656bf6e31b7e5d0a9f498163d1.tar.gz", + "url": "https://github.com/input-output-hk/cardano-node/archive/1f2f51164b53b9b775c03ac9f1e23e7b70c74b05.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "cardano-shell": { @@ -29,10 +29,10 @@ "homepage": null, "owner": "input-output-hk", "repo": "cardano-wallet", - "rev": "d7994e9c83ed00d798cf24f3101e50f0383ddc17", - "sha256": "00wzahmqm44hkxrcns1ig5zann057328y24xbv34jwjgllldmzhb", + "rev": "ffeca1d9e0f302111588c7890785051b6cc6b0da", + "sha256": "0hmlv9sfqxhwf0syrfkvqcn6zndkk0s7z47fixfji62llzvhk9zl", "type": "tarball", - "url": "https://github.com/input-output-hk/cardano-wallet/archive/d7994e9c83ed00d798cf24f3101e50f0383ddc17.tar.gz", + "url": "https://github.com/input-output-hk/cardano-wallet/archive/ffeca1d9e0f302111588c7890785051b6cc6b0da.tar.gz", "url_template": "https://github.com///archive/.tar.gz", "version": "v2020-04-28" }, diff --git a/source/main/environment.js b/source/main/environment.js index 3eef91bf80..231103e34e 100644 --- a/source/main/environment.js +++ b/source/main/environment.js @@ -60,7 +60,7 @@ const isIncentivizedTestnetSelfnode = checkIsIncentivizedTestnetSelfnode( const isDevelopment = checkIsDevelopment(NETWORK); const isWatchMode = process.env.IS_WATCH_MODE; const API_VERSION = process.env.API_VERSION || 'dev'; -const NODE_VERSION = '1.19.0'; // TODO: pick up this value from process.env +const NODE_VERSION = '1.20.0'; // TODO: pick up this value from process.env const mainProcessID = get(process, 'ppid', '-'); const rendererProcessID = process.pid; const PLATFORM = os.platform(); diff --git a/source/renderer/app/components/staking/widgets/ThumbPoolContent.js b/source/renderer/app/components/staking/widgets/ThumbPoolContent.js index 0badc358fd..44a05fcd86 100644 --- a/source/renderer/app/components/staking/widgets/ThumbPoolContent.js +++ b/source/renderer/app/components/staking/widgets/ThumbPoolContent.js @@ -8,7 +8,10 @@ import noDataDashBigImage from '../../../assets/images/no-data-dash-big.inline.s import styles from './ThumbPoolContent.scss'; import { getColorFromRange, getSaturationColor } from '../../../utils/colors'; import StakePool from '../../../domains/StakePool'; -import { IS_RANKING_DATA_AVAILABLE } from '../../../config/stakingConfig'; +import { + IS_RANKING_DATA_AVAILABLE, + IS_SATURATION_DATA_AVAILABLE, +} from '../../../config/stakingConfig'; type Props = { stakePool: StakePool, @@ -19,15 +22,12 @@ type Props = { export default class ThumbPoolContent extends Component { render() { const { stakePool, numberOfStakePools } = this.props; - const { ranking, ticker, retiring, saturation } = stakePool; const color = getColorFromRange(ranking, numberOfStakePools); - const showSaturation = false; - const componentClassnames = classnames([ styles.component, - !showSaturation ? styles.hideSaturation : null, + !IS_SATURATION_DATA_AVAILABLE ? styles.hideSaturation : null, ]); const saturationClassnames = classnames([ @@ -43,7 +43,7 @@ export default class ThumbPoolContent extends Component {
{ranking}
- {showSaturation && ( + {IS_SATURATION_DATA_AVAILABLE && (
{ showWithSelectButton, numberOfStakePools, } = this.props; - const showSaturation = false; const { componentStyle, arrowStyle, @@ -520,7 +519,7 @@ export default class TooltipPool extends Component { />
- {showSaturation && ( + {IS_SATURATION_DATA_AVAILABLE && ( <>
{intl.formatMessage(messages.saturation)} diff --git a/source/renderer/app/config/stakingConfig.js b/source/renderer/app/config/stakingConfig.js index b345a2b92a..3f904477ea 100644 --- a/source/renderer/app/config/stakingConfig.js +++ b/source/renderer/app/config/stakingConfig.js @@ -51,4 +51,6 @@ export const REDEEM_ITN_REWARDS_STEPS: { export const IS_RANKING_DATA_AVAILABLE = true; +export const IS_SATURATION_DATA_AVAILABLE = true; + export const EPOCH_COUNTDOWN_INTERVAL = 1 * 1000; // 1 second | unit: milliseconds; diff --git a/source/renderer/app/utils/colors.js b/source/renderer/app/utils/colors.js index b14eb40e7c..3808c42818 100644 --- a/source/renderer/app/utils/colors.js +++ b/source/renderer/app/utils/colors.js @@ -58,11 +58,11 @@ export const getColorFromRange = ( export const getSaturationColor = (saturation: number): string => { let color; - if (saturation >= 100) { + if (saturation > 110) { color = 'red'; - } else if (saturation >= 90) { + } else if (saturation > 105) { color = 'orange'; - } else if (saturation >= 80) { + } else if (saturation > 100) { color = 'yellow'; } else { color = 'green'; From 79d185474a31ecacf20113d2f475f3900c6c1948 Mon Sep 17 00:00:00 2001 From: Danilo Prates Date: Thu, 24 Sep 2020 14:27:34 +0000 Subject: [PATCH 2/3] [DDW-269] Automatic Daedalus updates (#2056) * [DDW-269] init * [DDW-269] CHANGELOG * [DDW-269] NewsFeedIcon * [DDW-269] NewsFeed item - init * [DDW-269] Update container - init * [DDW-269] Progress * [DDW-269] Download progress working * [DDW-269] Download progress - adjustments * [DDW-269] Update management - progress * [DDW-269] Progress * [DDW-269] Progress * [DDW-269] Progress * [DDW-269] Progress * [DDW-269] Progress * [DDW-269] Progress * [DDW-269] Progress * [DDW-269] Progress * [DDW-269] Rename DownloadData into DownloadInfo * [DDW-269] Rename DownloadProgress into DownloadData * [DDW-269] Download progress information in the Overlay * [DDW-269] Flow issues * [DDW-269] Triggering App Update * [DDW-269] Persistent Automatic Update Failed flag * [DDW-269] Progress * [DDW-269] Remove unused files * [DDW-269] Unifying Manual Update into the new App Update * [DDW-269] Colors progress * [DDW-269] Remove unused methods * [DDW-269] Progress * [DDW-269] New screens for manual update scenarios * [DDW-269] Progress - finish update process * [DDW-269] Improve security when opening the installer file * Revert "[DDW-269] Improve security when opening the installer file" This reverts commit 512cad8570476bed945a3ee29f76059f3ee89fec. * [DDW-269] Preparation for QA testing - init * [DDW-269] Preparation for QA testing - progress * [DDW-269] Preparation for QA testing - progress * [DDW-269] Different versions for testing purposes * [DDW-269] Fix storybook styling * [DDW-269] Preparing to copy theme variables * [DDW-269] Color adjustments * [DDW-269] Build for test - SHOULD UPDATE * [DDW-269] Build for test - IS UP TO DATE * [DDW-269] Adjustments * [DDW-269] Adjustments and missing translation * [DDW-269] Styling adjustments * [DDW-269] Styling adjustments * [DDW-269] Temporary testing bits * [DDW-269] Styling adjustment * [DDW-269] Temporary testing bits * [DDW-269] Temporary testing bits * [DDW-269] Testing version * fix yarn.lock * [DDW-269] TEST - Should Update * [DDW-269] Remove react-syntax-highlighter from yark.lock * [DDW-269] TEST - SHOULD UPDATE * [DDW-269] yark lock things * [DDW-269] Acceptance tests * fix yarn.lock * [DDW-269] Fixed shasum version * [DDW-269] yarn.lock * [DDW-269] Fix yarn.lock * [DDW-269] Improve error when the Installer is missing * [DDW-269] After installing * [DDW-269] Translation manager * [DDW-269] Fix switched installers * [DDW-269] Handle app closing * [DDW-269] Minor adjustment * [DDW-269] Move scroll into the news items * [DDW-269] Color variable and close newsfeed after download update * [DDW-269] Handle app closing - fixing * [DDW-269] Adjustments and remove consoles * [DDW-269] Fix total download size not being displayed * [DDW-269] Missing JP translation * [DDW-269] Inconsistent colors for the white theme * [DDW-269] Remove update overlay when Daedalus is quitting * [DDW-269] Fix localstorage theme key declaration * [DDW-269] Handle file deleted while downloading * [DDW-269] Checks if the file exists on app loading * [DDW-269] Correct green dot color * [DDW-269] Prevent close button on error scenario * [DDW-269] Prevent error message to be improperly shown * [DDW-269] Fix japanese link * [DDW-269] Different action for Linux * [DDW-269] Logging errors * [DDW-269] Supports link inside the markdown content * [DDW-269] Handles error when changing to Japanese * [DDW-269] Handle multiple download requests * [DDW-269] Allow installation postpone * [DDW-269] Linux/postpone copy/translation * [DDW-269] Linux installation * [DDW-269] Logging errors * [DDW-269] Logging errors - 2 * [DDW-269] Test without update-runner * [DDW-269] Test without update-runner - chmod * [DDW-269] Test without update-runner - quit after install * [DDW-269] Revert logging * [DDW-269] Test Update Runner * [DDW-269] Test 2 Update Runner * [DDW-269] Test 3 Update Runner * [DDW-269] Test 4 Update Runner - synchronous * [DDW-269] [DDW-269] Test 4 Update Runner - asynchronous messages * [DDW-269] Progress * [DDW-269] spawn env * [DDW-269] [DDW-269] spawn env 2 * [DDW-269] [DDW-269] spawn env 2 * Revert "[DDW-269] [DDW-269] spawn env 2" This reverts commit 6a2dc0dcec158689dfbc435c489235054aedf212. * [DDW-269] Checkbox checked when installing * [DDW-269] Adjustments * [DDW-269] Fix Update description not changing locale * [DDW-269] Postpone/Installing update link adjustments * dont treat stderr as fatal * fix the missing username * fix the on function and the global unhandledRejection handler * [DDW-269] Styling adjustments * clean up arx temp files after execution report progress for daedalus to render * [DDW-269] Fix issue when deleting the temporaty file * [DDW-269] Fix issue when deleting the temporaty file * [DDW-269] Linux installation progress * [DDW-269] Small adjustment * [DDW-269] Styling adjustments * [DDW-269] Remove typos and console log * [DDW-269] Remove dummy data * [DDW-269] Cleanup Co-authored-by: Michael Bishop Co-authored-by: Nikola Glumac --- CHANGELOG.md | 4 + default.nix | 7 +- installers/nix/nix-installer.nix | 31 +- nix/launcher-config.nix | 1 + nix/sources.json | 12 + package.json | 1 + source/common/config/appupdateConfig.js | 12 + source/common/config/downloadManagerConfig.js | 6 +- source/common/config/electron-store.config.js | 2 + source/common/ipc/api.js | 61 ++- source/common/types/app-update.types.js | 3 + source/common/types/downloadManager.types.js | 42 +- source/common/types/electron-store.types.js | 4 +- source/common/types/environment.types.js | 2 + source/common/types/locales.types.js | 7 + source/main/config.js | 1 + source/main/index.js | 3 +- source/main/ipc/downloadManagerChannel.js | 203 +++++-- source/main/ipc/index.js | 2 + source/main/ipc/manageAppUpdateChannel.js | 187 +++++++ source/main/utils/downloadManager.js | 289 ++++++---- source/main/utils/mainErrorHandler.js | 6 +- source/main/utils/mainLocalStorage.js | 61 ++- source/renderer/app/App.js | 24 +- .../app/actions/app-update-actions.js | 9 +- source/renderer/app/api/api.js | 101 +--- source/renderer/app/api/news/types.js | 20 +- .../app/api/nodes/requests/applyAppUpdate.js | 10 - .../api/nodes/requests/getLatestAppVersion.js | 16 - .../api/nodes/requests/getNextAppUpdate.js | 10 - .../api/nodes/requests/postponeAppUpdate.js | 10 - source/renderer/app/api/nodes/types.js | 46 -- source/renderer/app/api/utils/localStorage.js | 24 +- source/renderer/app/api/utils/patchAdaApi.js | 77 --- source/renderer/app/api/utils/request.js | 4 +- .../components/appUpdate/AppUpdateOverlay.js | 355 ++++++++++++ .../appUpdate/AppUpdateOverlay.scss | 245 +++++++++ .../loading/manual-update/ManualUpdate.js | 101 ---- .../loading/manual-update/ManualUpdate.scss | 91 --- .../syncing-connecting/SyncingConnecting.js | 51 +- .../renderer/app/components/news/NewsFeed.js | 52 +- .../app/components/news/NewsFeed.scss | 27 +- .../app/components/news/NewsItem.scss | 2 +- .../app/components/news/UpdateItem.js | 78 +++ .../app/components/news/UpdateItem.scss | 68 +++ .../AutomaticUpdateNotification.js | 121 ---- .../AutomaticUpdateNotification.scss | 101 ---- .../DataLayerMigrationForm.scss | 4 +- .../wallet/settings/WalletSettings.js | 10 +- .../app/components/widgets/NewsFeedIcon.js | 13 +- .../app/components/widgets/NewsFeedIcon.scss | 16 +- .../components/widgets/ProgressBarLarge.js | 31 ++ .../components/widgets/ProgressBarLarge.scss | 36 ++ source/renderer/app/config/timingConfig.js | 1 - source/renderer/app/containers/Root.js | 15 +- .../app/containers/TopBarContainer.js | 14 +- .../appUpdate/AppUpdateContainer.js | 58 ++ .../app/containers/loading/LoadingPage.js | 3 - .../containers/loading/ManualUpdatePage.js | 27 - .../loading/SyncingConnectingPage.js | 36 +- .../app/containers/news/NewsFeedContainer.js | 16 +- .../containers/news/NewsOverlayContainer.js | 5 +- .../AutomaticUpdateNotificationDialog.js | 28 - source/renderer/app/domains/News.js | 12 +- .../app/i18n/locales/defaultMessages.json | 437 ++++++++------- source/renderer/app/i18n/locales/en-US.json | 26 +- source/renderer/app/i18n/locales/ja-JP.json | 26 +- .../app/ipc/downloadManagerChannel.js | 40 +- .../app/ipc/manageAppUpdateChannel.js | 13 + source/renderer/app/stores/AppUpdateStore.js | 517 +++++++++++------- .../renderer/app/stores/NetworkStatusStore.js | 1 - source/renderer/app/stores/NewsFeedStore.js | 1 + source/renderer/app/stores/ProfileStore.js | 5 +- .../renderer/app/themes/daedalus/cardano.js | 60 +- .../renderer/app/themes/daedalus/dark-blue.js | 60 +- .../app/themes/daedalus/dark-cardano.js | 58 +- .../app/themes/daedalus/flight-candidate.js | 58 +- .../themes/daedalus/incentivized-testnet.js | 58 +- .../app/themes/daedalus/light-blue.js | 59 +- .../app/themes/daedalus/shelley-testnet.js | 59 +- source/renderer/app/themes/daedalus/white.js | 59 +- source/renderer/app/themes/daedalus/yellow.js | 65 ++- .../renderer/app/themes/utils/createTheme.js | 59 +- source/renderer/app/utils/formatters.js | 44 ++ source/renderer/app/utils/storesUtils.js | 9 +- source/renderer/app/utils/waitFor.js | 9 + storybook/stories/_support/StoryLayout.js | 3 +- storybook/stories/common/Widgets.stories.js | 7 +- storybook/stories/index.js | 1 + .../stories/news/AlertsOverlay.stories.js | 4 +- .../stories/news/AppUpdateOverlay.stories.js | 87 +++ .../stories/news/IncidentOverlay.stories.js | 6 +- storybook/stories/news/NewsFeed.stories.js | 193 ++----- .../stories/news/_utils/fakeDataNewsFeed.js | 31 ++ .../stories/news/_utils/fakeDataUpdate.js | 112 ++++ .../environment/TopBarEnvironment.stories.js | 9 +- .../syncing/SyncingConnecting.stories.js | 12 +- .../nodes/updates/AutomaticUpdate.stories.js | 22 - .../nodes/updates/ManualUpdate.stories.js | 12 - .../stories/nodes/updates/Updates.stories.js | 8 - .../settings/WalletSettingsScreen.stories.js | 3 +- tests/app/e2e/features/app-update.feature | 83 +++ .../features/node-update-notification.feature | 35 -- tests/app/e2e/steps/app-version-difference.js | 24 +- .../app/e2e/steps/node-update-notification.js | 78 --- yarn.lock | 15 +- 106 files changed, 3350 insertions(+), 2033 deletions(-) create mode 100644 source/common/config/appupdateConfig.js create mode 100644 source/common/types/app-update.types.js create mode 100644 source/main/ipc/manageAppUpdateChannel.js delete mode 100644 source/renderer/app/api/nodes/requests/applyAppUpdate.js delete mode 100644 source/renderer/app/api/nodes/requests/getLatestAppVersion.js delete mode 100644 source/renderer/app/api/nodes/requests/getNextAppUpdate.js delete mode 100644 source/renderer/app/api/nodes/requests/postponeAppUpdate.js delete mode 100644 source/renderer/app/api/nodes/types.js create mode 100644 source/renderer/app/components/appUpdate/AppUpdateOverlay.js create mode 100644 source/renderer/app/components/appUpdate/AppUpdateOverlay.scss delete mode 100644 source/renderer/app/components/loading/manual-update/ManualUpdate.js delete mode 100644 source/renderer/app/components/loading/manual-update/ManualUpdate.scss create mode 100644 source/renderer/app/components/news/UpdateItem.js create mode 100644 source/renderer/app/components/news/UpdateItem.scss delete mode 100644 source/renderer/app/components/notifications/AutomaticUpdateNotification.js delete mode 100644 source/renderer/app/components/notifications/AutomaticUpdateNotification.scss create mode 100644 source/renderer/app/components/widgets/ProgressBarLarge.js create mode 100644 source/renderer/app/components/widgets/ProgressBarLarge.scss create mode 100644 source/renderer/app/containers/appUpdate/AppUpdateContainer.js delete mode 100644 source/renderer/app/containers/loading/ManualUpdatePage.js delete mode 100644 source/renderer/app/containers/notifications/AutomaticUpdateNotificationDialog.js create mode 100644 source/renderer/app/ipc/manageAppUpdateChannel.js create mode 100644 source/renderer/app/utils/waitFor.js create mode 100644 storybook/stories/news/AppUpdateOverlay.stories.js create mode 100644 storybook/stories/news/_utils/fakeDataNewsFeed.js create mode 100644 storybook/stories/news/_utils/fakeDataUpdate.js delete mode 100644 storybook/stories/nodes/updates/AutomaticUpdate.stories.js delete mode 100644 storybook/stories/nodes/updates/ManualUpdate.stories.js create mode 100644 tests/app/e2e/features/app-update.feature delete mode 100644 tests/app/e2e/features/node-update-notification.feature delete mode 100644 tests/app/e2e/steps/node-update-notification.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 683f161c0a..e0aaf02a15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,10 @@ Changelog ## 2.0.0-RC1 +### Features + +- Implemented Daedalus app automatic update ([PR 2056](https://github.com/input-output-hk/daedalus/pull/2056)) + ### Fixes - Fixed the epoch duration copy ([PR 2107](https://github.com/input-output-hk/daedalus/pull/2107)) diff --git a/default.nix b/default.nix index c37a0a5051..a5d543d9b4 100644 --- a/default.nix +++ b/default.nix @@ -333,12 +333,7 @@ let runLint = self.callPackage ./tests/lint.nix {}; runShellcheck = self.callPackage ./tests/shellcheck.nix { src = ./.;}; }; - nix-bundle = import (pkgs.fetchFromGitHub { - owner = "matthewbauer"; - repo = "nix-bundle"; - rev = "7f12322399fd87d937355d0fc263d37d798496fc"; - sha256 = "07wnmdadchf73p03wk51abzgd3zm2xz5khwadz1ypbvv3cqlzp5m"; - }) { nixpkgs = pkgs; }; + nix-bundle = import sources.nix-bundle { nixpkgs = pkgs; }; iconPath = self.launcherConfigs.installerConfig.iconPath; # used for name of profile, binary and the desktop shortcut linuxClusterBinName = cluster; diff --git a/installers/nix/nix-installer.nix b/installers/nix/nix-installer.nix index 6b76ed0e2a..543db47d65 100644 --- a/installers/nix/nix-installer.nix +++ b/installers/nix/nix-installer.nix @@ -8,6 +8,8 @@ let target = "${installer}"; run = "/bin/installer"; nixUserChrootFlags = "-c -m /home:/home -p HOME"; + rmStyle = "-rm_"; + shared = false; }; utils = pkgs.writeText "utils.sh" '' function rmrf { @@ -29,24 +31,38 @@ let pwd id UNPACK=$(mktemp -d) - cd $UNPACK - echo "$@" - bash "$1" --extract - ls -ltrh dat/nix/store/*-tarball/tarball/tarball.tar.xz - UNPACK2=$(mktemp -d) - tar --delay-directory-restore -C $UNPACK2 -xf dat/nix/store/*-tarball/tarball/tarball.tar.xz - cd + pushd $UNPACK + echo "$@" + echo STATUS decompressing + echo PROG 1/6 + bash "$1" --extract + ls -ltrh dat/nix/store/*-tarball/tarball/tarball.tar.xz + UNPACK2=$(mktemp -d) + echo STATUS unpacking + echo PROG 2/6 + tar --delay-directory-restore -C $UNPACK2 -xf dat/nix/store/*-tarball/tarball/tarball.tar.xz + popd + echo STATUS removing temp files + echo PROG 3/6 rmrf $UNPACK ls -ltrh $UNPACK2 + echo STATUS copying files + echo PROG 3/6 NIX_REMOTE=local?root=$UNPACK2 nix-store --load-db < $UNPACK2/nix-path-registration NIX_REMOTE=local?root=$UNPACK2 nix-store --verify --check-contents nix copy --no-check-sigs --from local?root=$UNPACK2 $(readlink $UNPACK2/firstGeneration) export NIX_PROFILE=/nix/var/nix/profiles/profile nix-env --set $(readlink $UNPACK2/firstGeneration) nix-env -p /nix/var/nix/profiles/profile-${linuxClusterBinName} --set $(readlink $UNPACK2/firstGeneration) + echo STATUS cleaning up + echo PROG 4/6 rmrf $UNPACK2 + echo STATUS post-install + echo PROG 5/6 post-install || true + echo STATUS done + echo PROG 6/6 ''; enter = pkgs.writeScriptBin "enter-chroot" '' #!/usr/bin/env bash @@ -86,6 +102,7 @@ let ln -svf ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt /etc/ssl/certs/ca-certificates.crt ln -svf ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt /etc/ssl/certs/ca-bundle.crt unset NIX_SSL_CERT_FILE + export USER=daedalus if [ -z "$@" ]; then exec bash diff --git a/nix/launcher-config.nix b/nix/launcher-config.nix index a23f15b7d3..071e08ff0d 100644 --- a/nix/launcher-config.nix +++ b/nix/launcher-config.nix @@ -151,6 +151,7 @@ let inherit logsPrefix launcherLogsPrefix tlsConfig; walletLogging = false; daedalusBin = mkBinPath "frontend"; + updateRunnerBin = mkBinPath "update-runner"; # TODO: set when update system is complete updaterArgs = []; updaterPath = ""; diff --git a/nix/sources.json b/nix/sources.json index 7aa96865ce..ce8d89e70f 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -96,6 +96,18 @@ "url": "https://github.com/input-output-hk/niv/archive/4229fbcf62997467c34283a2f353702359e78e5a.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, + "nix-bundle": { + "branch": "rm-style", + "description": "Bundle Nix derivations to run anywhere!", + "homepage": "", + "owner": "input-output-hk", + "repo": "nix-bundle", + "rev": "acf3b142c1fa332d4f9050e7cdfb3c3f8091e78b", + "sha256": "0pcc8bh1x3shr6k2h8zw05n0apm5ppx6nk07xp89ljldah4mmc6c", + "type": "tarball", + "url": "https://github.com/input-output-hk/nix-bundle/archive/acf3b142c1fa332d4f9050e7cdfb3c3f8091e78b.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, "nixpkgs-nsis": { "branch": "nsis", "description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to", diff --git a/package.json b/package.json index f1999b6846..2153a4bcd9 100644 --- a/package.json +++ b/package.json @@ -229,6 +229,7 @@ "rust-cardano-crypto": "0.2.0", "safe-buffer": "5.2.0", "semver": "7.1.3", + "shasum": "1.0.2", "source-map-support": "0.5.16", "spectron-fake-dialog": "0.0.1", "unorm": "1.6.0", diff --git a/source/common/config/appupdateConfig.js b/source/common/config/appupdateConfig.js new file mode 100644 index 0000000000..b7c140d4a3 --- /dev/null +++ b/source/common/config/appupdateConfig.js @@ -0,0 +1,12 @@ +// @flow +import type { UpdateInstallationStatus } from '../types/app-update.types'; + +export const APP_UPDATE_DOWNLOAD_ID = 'appUpdate'; + +export const UPDATE_INSTALLATION_STATUSES: { + [key: string]: UpdateInstallationStatus, +} = { + PROGRESS: 'progress', + ERROR: 'error', + SUCCESS: 'success', +}; diff --git a/source/common/config/downloadManagerConfig.js b/source/common/config/downloadManagerConfig.js index 60fc7bfa80..45137528f6 100644 --- a/source/common/config/downloadManagerConfig.js +++ b/source/common/config/downloadManagerConfig.js @@ -38,6 +38,8 @@ export const DOWNLOAD_EVENT_TYPES: { PROGRESS: 'progress', END: 'end', TIMEOUT: 'timeout', + STOP: 'stop', + PAUSE: 'pause', ERROR: 'error', }; @@ -49,7 +51,7 @@ export const TEMPORARY_FILENAME = { extension: 'crdownload', }; -export const DOWNLOAD_PROGRESS_DEFAULT = { +export const DOWNLOAD_DATA_DEFAULT = { state: DOWNLOAD_STATES.IDLE, remainingSize: 0, serverFileSize: 0, @@ -60,3 +62,5 @@ export const DOWNLOAD_PROGRESS_DEFAULT = { incomplete: false, isResumed: false, }; + +export const ERROR_TIME_AFTER_NO_END_EVENT = 10 * 1000; // 10 seconds | unit: milliseconds diff --git a/source/common/config/electron-store.config.js b/source/common/config/electron-store.config.js index 3bd5a9523b..e499dbb107 100644 --- a/source/common/config/electron-store.config.js +++ b/source/common/config/electron-store.config.js @@ -26,4 +26,6 @@ export const STORAGE_KEYS: { WALLETS: 'WALLETS', WALLET_MIGRATION_STATUS: 'WALLET-MIGRATION-STATUS', DOWNLOAD_MANAGER: 'DOWNLOAD-MANAGER', + APP_AUTOMATIC_UPDATE_FAILED: 'APP-AUTOMATIC-UPDATE-FAILED', + APP_UPDATE_COMPLETED: 'APP-UPDATE-COMPLETED', }; diff --git a/source/common/ipc/api.js b/source/common/ipc/api.js index 6bd6d82d8d..791483fec7 100644 --- a/source/common/ipc/api.js +++ b/source/common/ipc/api.js @@ -36,6 +36,11 @@ import type { DownloadResponse, ResumeDownloadRequest, ResumeDownloadResponse, + ClearDownloadLocalDataRequest, + ClearDownloadLocalDataResponse, + DeleteDownloadedFileRequest, + DeleteDownloadedFileResponse, + CheckFileExistsRequest, } from '../types/downloadManager.types'; import type { StoreMessage } from '../types/electron-store.types'; import type { @@ -297,6 +302,27 @@ export type ShowSaveDialogMainResponse = SaveFileDialogResponseParams; export const ELECTRON_STORE_CHANNEL = 'ELECTRON_STORE_CHANNEL'; export type ElectronStoreMessage = StoreMessage; +/** + * Channel for requesting a new download + */ +export const REQUEST_DOWNLOAD = 'REQUEST_DOWNLOAD'; +export type DownloadRendererRequest = DownloadRequest; +export type DownloadMainResponse = DownloadResponse; + +/** + * Channel for resuming an existing download + */ +export const RESUME_DOWNLOAD = 'RESUME_DOWNLOAD'; +export type ResumeDownloadRendererRequest = ResumeDownloadRequest; +export type ResumeDownloadMainResponse = ResumeDownloadResponse | void; + +/** + * Channel for resuming an existing download + */ +export const DELETE_DOWNLOADED_FILE = 'DELETE_DOWNLOADED_FILE'; +export type DeleteDownloadedFileRendererRequest = DeleteDownloadedFileRequest; +export type DeleteDownloadedFileMainResponse = DeleteDownloadedFileResponse | void; + /** * Channel for initiating the download manager */ @@ -312,18 +338,37 @@ export type DownloadsLocalDataRendererRequest = DownloadsLocalDataRequest | void export type DownloadsLocalDataMainResponse = DownloadsLocalDataResponse | void; /** - * Channel for requesting a new download + * Channel for initiating the download manager */ -export const REQUEST_DOWNLOAD = 'REQUEST_DOWNLOAD'; -export type DownloadRendererRequest = DownloadRequest; -export type DownloadMainResponse = DownloadResponse; +export const CLEAR_DOWNLOAD_LOCAL_DATA = 'CLEAR_DOWNLOAD_LOCAL_DATA'; +export type ClearDownloadLocalDataRendererRequest = ClearDownloadLocalDataRequest; +export type ClearDownloadLocalDataMainResponse = ClearDownloadLocalDataResponse; /** - * Channel for requesting a new download + * Channel for checking if the downloaded file still exists */ -export const RESUME_DOWNLOAD = 'RESUME_DOWNLOAD'; -export type ResumeDownloadRendererRequest = ResumeDownloadRequest; -export type ResumeDownloadMainResponse = ResumeDownloadResponse | void; +export const CHECK_FILE_EXISTS = 'CHECK_FILE_EXISTS'; +export type CheckFileExistsRendererRequest = CheckFileExistsRequest; +export type CheckFileExistsMainResponse = boolean; + +/** + * Channel for quitting Daedalus and installing update + */ +export const MANAGE_APP_UPDATE = 'MANAGE_APP_UPDATE'; +export type ManageAppUpdateRendererRequest = { + filePath: string, + hash: string, +}; +export type ManageAppUpdateMainResponse = { + status: 'progress' | 'success' | 'error', + data: { + message?: string, + progress?: number, + code?: number, + error?: Error, + info?: Object, + }, +}; /** * Channel for introspecting an address diff --git a/source/common/types/app-update.types.js b/source/common/types/app-update.types.js new file mode 100644 index 0000000000..183f5e718a --- /dev/null +++ b/source/common/types/app-update.types.js @@ -0,0 +1,3 @@ +// @flow + +export type UpdateInstallationStatus = 'progress' | 'error' | 'success'; diff --git a/source/common/types/downloadManager.types.js b/source/common/types/downloadManager.types.js index 55ee73029e..4d63d37ff3 100644 --- a/source/common/types/downloadManager.types.js +++ b/source/common/types/downloadManager.types.js @@ -34,6 +34,8 @@ export type DownloadRequestOptions = { override?: boolean, // if true it will override the file, otherwise will append '(number)' to the end of file httpRequestOptions?: Object, // Override the http request options httpsRequestOptions?: Object, // Override the https request options, ex: to add SSL Certs + progressIsThrottled?: boolean, // by default, the progress is sent every second. if `false` it will be sent every milisecond + persistLocalData?: boolean, // by default, the localdata information is deleted after the end of the download }; // https://www.npmjs.com/package/node-downloader-helper @@ -55,15 +57,18 @@ export type DownloadEventType = | 'progress' | 'end' | 'timeout' + | 'stop' + | 'pause' | 'error'; export type DownloadResponse = { eventType: DownloadEventType, + info: DownloadInfo, data: DownloadData, - progress: DownloadProgress, + error?: string, }; -export type DownloadData = { +export type DownloadInfo = { downloadId: string, fileUrl: string, originalFilename: string, @@ -71,10 +76,9 @@ export type DownloadData = { destinationDirectoryName: AllowedDownloadDirectories, destinationPath: string, options: DownloadRequestOptions, - persistLocalData?: boolean, }; -export type DownloadProgress = { +export type DownloadData = { state: DownloadState, remainingSize: number, serverFileSize: number, @@ -87,7 +91,7 @@ export type DownloadProgress = { error?: string, }; -export type DownloadProgressUpdate = { +export type DownloadDataUpdate = { state?: DownloadState, remainingSize?: number, serverFileSize?: number, @@ -104,7 +108,7 @@ export type DownloadProgressUpdate = { * * Each event has a different response * which is formatted and so the Main IPC - * response has always the DownloadProgress shape + * response has always the DownloadData shape * */ @@ -133,31 +137,37 @@ export type DownloadInfoEnd = { }; export type DownloadInfoError = { message: string, // Error message - status: string, // Http status response if available - body: string, // Http body response if available + status?: string, // Http status response if available + body?: string, // Http body response if available }; export type DownloadLocalDataRequest = { fileName?: string, id?: string, }; - export type DownloadLocalDataResponse = { - data: DownloadData, - progress: DownloadProgress, + info?: DownloadInfo, + data?: DownloadData, }; export type DownloadsLocalDataRequest = { state: DownloadState, }; - export type DownloadsLocalDataResponse = { - [key: string]: DownloadsLocalDataResponse, + [key: string]: DownloadLocalDataResponse, }; -export type ResumeDownloadRequest = { - fileName?: string, +export type ResumeDownloadRequest = DownloadLocalDataRequest; +export type ResumeDownloadResponse = DownloadResponse | void; + +export type ClearDownloadLocalDataRequest = DownloadLocalDataRequest; +export type ClearDownloadLocalDataResponse = void; + +export type DeleteDownloadedFileRequest = { id?: string, }; +export type DeleteDownloadedFileResponse = void; -export type ResumeDownloadResponse = DownloadResponse | void; +export type CheckFileExistsRequest = { + id?: string, +}; diff --git a/source/common/types/electron-store.types.js b/source/common/types/electron-store.types.js index 6e2678e357..249aeb9b27 100644 --- a/source/common/types/electron-store.types.js +++ b/source/common/types/electron-store.types.js @@ -15,7 +15,9 @@ export type StorageKey = | 'READ-NEWS' | 'WALLETS' | 'WALLET-MIGRATION-STATUS' - | 'DOWNLOAD-MANAGER'; + | 'DOWNLOAD-MANAGER' + | 'APP-AUTOMATIC-UPDATE-FAILED' + | 'APP-UPDATE-COMPLETED'; export type StoreMessage = { type: StorageType, diff --git a/source/common/types/environment.types.js b/source/common/types/environment.types.js index 3ce017900a..a03281de96 100644 --- a/source/common/types/environment.types.js +++ b/source/common/types/environment.types.js @@ -67,6 +67,8 @@ export const OS_NAMES = { [LINUX]: 'Linux', }; +export type Platform = 'darwin' | 'win32' | 'linux'; + export type Network = | 'mainnet' | 'mainnet_flight' diff --git a/source/common/types/locales.types.js b/source/common/types/locales.types.js index 97f43ca569..b695c9be1e 100644 --- a/source/common/types/locales.types.js +++ b/source/common/types/locales.types.js @@ -13,3 +13,10 @@ export const humanizedDurationLanguages = { 'en-US': 'en', 'ja-JP': 'ja', }; + +export const momentLocales: { + [key: Locale]: string, +} = { + 'en-US': 'en-us', + 'ja-JP': 'ja', +}; diff --git a/source/main/config.js b/source/main/config.js index bf595ea134..e23e8ae960 100644 --- a/source/main/config.js +++ b/source/main/config.js @@ -76,6 +76,7 @@ export type LauncherConfig = { isFlight: boolean, isStaging: boolean, smashUrl?: string, + updateRunnerBin: string, }; type WindowOptionsType = { diff --git a/source/main/index.js b/source/main/index.js index 6f2829c51f..c38f565a2d 100644 --- a/source/main/index.js +++ b/source/main/index.js @@ -37,6 +37,7 @@ import type { CheckDiskSpaceResponse } from '../common/types/no-disk-space.types import { logUsedVersion } from './utils/logUsedVersion'; import { setStateSnapshotLogChannel } from './ipc/set-log-state-snapshot'; import { generateWalletMigrationReportChannel } from './ipc/generateWalletMigrationReportChannel'; +import { pauseActiveDownloads } from './ipc/downloadManagerChannel'; /* eslint-disable consistent-return */ @@ -61,8 +62,8 @@ if (isBlankScreenFixActive) { } app.allowRendererProcessReuse = true; - const safeExit = async () => { + pauseActiveDownloads(); if (!cardanoNode || cardanoNode.state === CardanoNodeStates.STOPPED) { logger.info('Daedalus:safeExit: exiting Daedalus with code 0', { code: 0 }); return safeExitWithCode(0); diff --git a/source/main/ipc/downloadManagerChannel.js b/source/main/ipc/downloadManagerChannel.js index 0991214691..f3e90ba886 100644 --- a/source/main/ipc/downloadManagerChannel.js +++ b/source/main/ipc/downloadManagerChannel.js @@ -1,37 +1,51 @@ // @flow import { DownloaderHelper } from 'node-downloader-helper'; +import fs from 'fs'; +import { forEach, omit } from 'lodash'; import type { BrowserWindow } from 'electron'; import { MainIpcChannel } from './lib/MainIpcChannel'; +import { logger } from '../utils/logging'; import { getOriginalFilename, getPathFromDirectoryName, getEventActions, getIdFromFileName, + downloads, } from '../utils/downloadManager'; import { - GET_DOWNLOAD_LOCAL_DATA, - GET_DOWNLOADS_LOCAL_DATA, REQUEST_DOWNLOAD, RESUME_DOWNLOAD, + DELETE_DOWNLOADED_FILE, + GET_DOWNLOAD_LOCAL_DATA, + GET_DOWNLOADS_LOCAL_DATA, + CLEAR_DOWNLOAD_LOCAL_DATA, + CHECK_FILE_EXISTS, } from '../../common/ipc/api'; import { DEFAULT_DIRECTORY_NAME, TEMPORARY_FILENAME, + DOWNLOAD_STATES, } from '../../common/config/downloadManagerConfig'; import { generateFileNameWithTimestamp } from '../../common/utils/files.js'; import { downloadManagerLocalStorage as localStorage } from '../utils/mainLocalStorage'; import type { DownloadRendererRequest, DownloadMainResponse, + ResumeDownloadRendererRequest, + ResumeDownloadMainResponse, + DeleteDownloadedFileRendererRequest, + DeleteDownloadedFileMainResponse, DownloadLocalDataRendererRequest, DownloadLocalDataMainResponse, DownloadsLocalDataRendererRequest, DownloadsLocalDataMainResponse, - ResumeDownloadRendererRequest, - ResumeDownloadMainResponse, + ClearDownloadLocalDataRendererRequest, + ClearDownloadLocalDataMainResponse, + CheckFileExistsMainResponse, + CheckFileExistsRendererRequest, } from '../../common/ipc/api'; -localStorage.setAllStopped(); +localStorage.setAllPaused(); const requestDownload = async ( downloadRequestPayload: DownloadRendererRequest, @@ -40,12 +54,10 @@ const requestDownload = async ( const { fileUrl, destinationDirectoryName = DEFAULT_DIRECTORY_NAME, - // options, options: _options, id, resumeDownload, } = downloadRequestPayload; - const temporaryFilename = resumeDownload ? resumeDownload.temporaryFilename : generateFileNameWithTimestamp(TEMPORARY_FILENAME); @@ -56,7 +68,7 @@ const requestDownload = async ( fileName: temporaryFilename, }; const downloadId = getIdFromFileName(id || originalFilename); - const data = { + const info = { downloadId, fileUrl, destinationPath, @@ -65,45 +77,53 @@ const requestDownload = async ( originalFilename, options, }; + if (downloads[downloadId]) { + logger.info( + `DownloadManager: Preventing download "${downloadId}" duplicity`, + { downloadId } + ); + return false; + } const eventActions = await getEventActions( - data, + info, window, requestDownloadChannel ); const download = new DownloaderHelper(fileUrl, destinationPath, options); + downloads[downloadId] = download; if (resumeDownload) { const { total: downloadSize } = await download.getTotalSize(); // get the total size from the server download.__total = downloadSize; - download.__filePath = `${data.destinationPath}/${data.temporaryFilename}`; + download.__filePath = `${info.destinationPath}/${info.temporaryFilename}`; download.__downloaded = download.__getFilesizeInBytes(download.__filePath); download.__isResumable = true; } + let currentDownloadData = 0; + + const progressType = + options.progressIsThrottled === false ? 'progress' : 'progress.throttled'; + download.on('start', eventActions.start); download.on('download', eventActions.download); - download.on('progress.throttled', (...p) => { - eventActions.progress(...p); + download.on(progressType, evt => { + if (!evt || parseInt(evt.progress, 10) === currentDownloadData) return; + currentDownloadData++; + eventActions.progress(evt); }); + download.on('pause', eventActions.pause); download.on('end', eventActions.end); download.on('error', eventActions.error); - if (resumeDownload) download.resume(); - else download.start(); - return download; -}; -const getDownloadLocalData = async ({ - fileName, - id = fileName, -}: DownloadLocalDataRendererRequest): Promise => { - if (!id) throw new Error('Requires `id` or `fileName`'); - const downloadId: string = getIdFromFileName(String(id)); - return localStorage.get(downloadId); + if (resumeDownload) { + download.resume(); + } else { + download.start(); + } + return download; }; -const getDownloadsLocalData = async (): Promise => - localStorage.getAll(); - const requestResumeDownload = async ( resumeDownloadRequestPayload: ResumeDownloadRendererRequest, window: BrowserWindow @@ -111,17 +131,40 @@ const requestResumeDownload = async ( const downloadLocalData = await getDownloadLocalData( resumeDownloadRequestPayload ); - const { temporaryFilename, originalFilename } = downloadLocalData.data; - const { downloadId: id, fileUrl, destinationDirectoryName, options } = - downloadLocalData.data || {}; + const { + temporaryFilename, + originalFilename, + downloadId: id, + fileUrl, + destinationDirectoryName, + destinationPath, + options, + } = downloadLocalData.info || {}; if (!id) throw new Error('Invalid download ID'); - const requestDownloadPayload = { + const filePath = `${destinationPath}/${temporaryFilename}`; + let requestDownloadPayload = { id, fileUrl, destinationDirectoryName, options, - resumeDownload: { temporaryFilename, originalFilename }, }; + // Check if the file to be resumed still exists + if (fs.existsSync(filePath)) { + requestDownloadPayload = { + ...requestDownloadPayload, + resumeDownload: { temporaryFilename, originalFilename }, + }; + } else { + // Otherwise: + // * New download request + // * The previous download data is removed + // * `fileName` is removed from options + requestDownloadPayload = { + ...requestDownloadPayload, + options: omit(options, 'fileName'), + }; + await localStorage.unset(id); + } return requestDownload( { ...requestDownloadPayload, @@ -131,29 +174,91 @@ const requestResumeDownload = async ( ); }; +const deleteDownloadedFile = async ({ + id, +}: DeleteDownloadedFileRendererRequest): Promise => { + const downloadLocalData = await getDownloadLocalData({ id }); + if (!downloadLocalData) throw new Error('Download data not found'); + const { destinationPath, originalFilename, temporaryFilename } = + downloadLocalData.info || {}; + const originalFilePath = `${destinationPath}/${originalFilename}`; + const temporaryFilePath = `${destinationPath}/${temporaryFilename}`; + if (fs.existsSync(originalFilePath)) fs.unlinkSync(originalFilePath); + if (fs.existsSync(temporaryFilePath)) fs.unlinkSync(temporaryFilePath); +}; + +const getDownloadLocalData = async ({ + fileName, + id = fileName, +}: DownloadLocalDataRendererRequest): Promise => { + if (!id) throw new Error('Requires `id` or `fileName`'); + const downloadId: string = getIdFromFileName(String(id)); + return localStorage.get(downloadId); +}; + +const getDownloadsLocalData = async (): Promise => + localStorage.getAll(); + +const clearDownloadLocalData = async ({ + fileName, + id = fileName, +}: ClearDownloadLocalDataRendererRequest): Promise => { + if (!id) throw new Error('Requires `id` or `fileName`'); + const downloadId: string = getIdFromFileName(String(id)); + return localStorage.unset(downloadId); +}; + +const checkFileExists = async ({ + id, +}: CheckFileExistsRendererRequest): Promise => { + const downloadLocalData = await getDownloadLocalData({ id }); + if (!downloadLocalData) throw new Error('Download data not found'); + const { destinationPath, originalFilename, temporaryFilename } = + downloadLocalData.info || {}; + const { state } = downloadLocalData.data || {}; + const fileName = + state === DOWNLOAD_STATES.FINISHED ? originalFilename : temporaryFilename; + const filePath = `${destinationPath}/${fileName}`; + return fs.existsSync(filePath); +}; + const requestDownloadChannel: // IpcChannel MainIpcChannel< DownloadRendererRequest, DownloadMainResponse > = new MainIpcChannel(REQUEST_DOWNLOAD); +const requestResumeDownloadChannel: // IpcChannel +MainIpcChannel< + ResumeDownloadRendererRequest, + ResumeDownloadMainResponse +> = new MainIpcChannel(RESUME_DOWNLOAD); + +const deleteDownloadedFileChannel: // IpcChannel +MainIpcChannel< + DeleteDownloadedFileRendererRequest, + DeleteDownloadedFileMainResponse +> = new MainIpcChannel(DELETE_DOWNLOADED_FILE); const getDownloadLocalDataChannel: // IpcChannel MainIpcChannel< DownloadLocalDataRendererRequest, DownloadLocalDataMainResponse > = new MainIpcChannel(GET_DOWNLOAD_LOCAL_DATA); - const getDownloadsLocalDataChannel: // IpcChannel MainIpcChannel< DownloadsLocalDataRendererRequest, DownloadsLocalDataMainResponse > = new MainIpcChannel(GET_DOWNLOADS_LOCAL_DATA); - -const requestResumeDownloadChannel: // IpcChannel +const clearDownloadLocalDataChannel: // IpcChannel MainIpcChannel< - ResumeDownloadRendererRequest, - ResumeDownloadMainResponse -> = new MainIpcChannel(RESUME_DOWNLOAD); + ClearDownloadLocalDataRendererRequest, + ClearDownloadLocalDataMainResponse +> = new MainIpcChannel(CLEAR_DOWNLOAD_LOCAL_DATA); +const checkFileExistsChannel: // IpcChannel +MainIpcChannel< + CheckFileExistsRendererRequest, + CheckFileExistsMainResponse +> = new MainIpcChannel(CHECK_FILE_EXISTS); export const downloadManagerChannel = (window: BrowserWindow) => { requestDownloadChannel.onRequest( @@ -164,6 +269,30 @@ export const downloadManagerChannel = (window: BrowserWindow) => { (resumeDownloadRequestPayload: ResumeDownloadRendererRequest) => requestResumeDownload(resumeDownloadRequestPayload, window) ); + deleteDownloadedFileChannel.onRequest(deleteDownloadedFile); + getDownloadLocalDataChannel.onRequest(getDownloadLocalData); getDownloadsLocalDataChannel.onRequest(getDownloadsLocalData); + clearDownloadLocalDataChannel.onRequest(clearDownloadLocalData); + checkFileExistsChannel.onRequest(checkFileExists); +}; + +export const pauseActiveDownloads = () => { + forEach(downloads, (download, downloadId) => { + try { + if (download && download.state === DOWNLOAD_STATES.DOWNLOADING) + download.pause(); + logger.info( + `DownloadManager:PauseDownloads download "${downloadId}" was paused`, + { downloadId } + ); + } catch (error) { + logger.error( + `DownloadManager:PauseDownloads download "${downloadId}" could not be paused`, + { + error, + } + ); + } + }); }; diff --git a/source/main/ipc/index.js b/source/main/ipc/index.js index ae9aa29662..1e1c39bcba 100644 --- a/source/main/ipc/index.js +++ b/source/main/ipc/index.js @@ -16,6 +16,7 @@ import { handleAddressPDFRequests } from './generateAddressPDFChannel'; import { handleRewardsCsvRequests } from './generateRewardsCsvChannel'; import { handleFileDialogRequests } from './show-file-dialog-channels'; import { handleAddressIntrospectionRequests } from './introspect-address'; +import { handleManageAppUpdateRequests } from './manageAppUpdateChannel'; import { openExternalUrlChannel } from './open-external-url'; import { openLocalDirectoryChannel } from './open-local-directory'; @@ -33,6 +34,7 @@ export default (window: BrowserWindow) => { handleRewardsCsvRequests(); handleFileDialogRequests(window); handleAddressIntrospectionRequests(); + handleManageAppUpdateRequests(window); // eslint-disable-next-line no-unused-expressions openExternalUrlChannel; // eslint-disable-next-line no-unused-expressions diff --git a/source/main/ipc/manageAppUpdateChannel.js b/source/main/ipc/manageAppUpdateChannel.js new file mode 100644 index 0000000000..9c32910a19 --- /dev/null +++ b/source/main/ipc/manageAppUpdateChannel.js @@ -0,0 +1,187 @@ +// @flow +import { app, shell } from 'electron'; +import fs from 'fs'; +import shasum from 'shasum'; +import { spawn } from 'child_process'; +import type { BrowserWindow } from 'electron'; +import { MainIpcChannel } from './lib/MainIpcChannel'; +import { MANAGE_APP_UPDATE } from '../../common/ipc/api'; +import type { + ManageAppUpdateRendererRequest as Request, + ManageAppUpdateMainResponse as Response, +} from '../../common/ipc/api'; +import { UPDATE_INSTALLATION_STATUSES as statuses } from '../../common/config/appUpdateConfig'; +import { environment } from '../environment'; +import { safeExitWithCode } from '../utils/safeExitWithCode'; +import { logger } from '../utils/logging'; +import { launcherConfig } from '../config'; + +// IpcChannel + +const manageAppUpdateChannel: MainIpcChannel< + Request, + Response +> = new MainIpcChannel(MANAGE_APP_UPDATE); + +const logPrefix = 'appUpdateInstall'; + +const getMessage = (functionPrefix: string, message?: string): string => { + let formattedMessage = `${logPrefix}:${functionPrefix}`; + if (message) formattedMessage += `: ${message}`; + return formattedMessage; +}; + +export const handleManageAppUpdateRequests = (window: BrowserWindow) => { + const response = ( + success: ?boolean, + functionPrefix: string, + messageText?: string = '', + _data?: Object + ): Response => { + let status = statuses.PROGRESS; + if (success === true) status = statuses.SUCCESS; + else if (success === false) status = statuses.ERROR; + const log = success === false ? logger.error : logger.info; + const message = getMessage(functionPrefix, messageText); + log(getMessage(functionPrefix, message)); + const data = { + ..._data, + message, + }; + manageAppUpdateChannel.send( + { + status, + message, + data, + }, + window.webContents + ); + return { + status, + message, + data, + }; + }; + + const checkInstallerHash = (filePath, expectedHash): boolean => { + const { name: functionPrefix } = checkInstallerHash; + const fileBuffer = fs.readFileSync(filePath); + if (!fileBuffer) { + logger.error(getMessage(functionPrefix, 'Unable to read the installer:')); + return false; + } + const fileHash = shasum(fileBuffer, 'sha256'); + if (fileHash !== expectedHash) { + logger.error(getMessage(functionPrefix, 'Hash does not match'), { + filePath, + }); + return false; + } + return true; + }; + + const installUpdate = async filePath => { + return new Promise((resolve, reject) => { + const { name: functionPrefix } = installUpdate; + response(null, functionPrefix, 'installation begin.'); + const { updateRunnerBin } = launcherConfig; + fs.chmodSync(filePath, 0o777); + const updater = spawn(updateRunnerBin, [filePath]); + let success = true; + updater.stdout.on('data', progressData => { + const info = progressData.toString().split(/\n/); + const progress = info.reduce((prog, infoItem) => { + const [, progressStr] = infoItem.split('PROG '); + if (progressStr) { + const [item, total] = `${progressStr}`.trim().split('/'); + return parseInt( + (parseInt(item, 10) * 100) / parseInt(total, 10), + 10 + ); + } + return prog; + }, 0); + response(null, functionPrefix, 'installation progress.', { + info, + progress, + }); + }); + updater.stderr.on('data', progressData => { + response(null, functionPrefix, 'installation progress.', { + info: progressData.toString(), + }); + }); + updater.on('close', code => { + if (code !== 0) { + success = false; + logger.info(`updater closed with ${code}`); + reject( + response( + false, + functionPrefix, + `updater closed with code ${code}`, + { code } + ) + ); + } + response(null, functionPrefix, 'installation progress.', { + info: 'stdio closed', + }); + }); + updater.on('error', error => { + success = false; + logger.error(`on error with ${error}`); + reject( + response(false, functionPrefix, 'installation failed', { error }) + ); + }); + updater.on('exit', code => { + if (code !== 0) { + success = false; + logger.info(`updater exited with ${code}`); + reject( + response( + false, + functionPrefix, + `updater exited with code ${code}`, + { code } + ) + ); + } + if (!success) { + logger.error('exit without success'); + return reject(); + } + safeExitWithCode(20); + return resolve(response(true, functionPrefix)); + }); + }); + }; + + manageAppUpdateChannel.onRequest(async ({ filePath, hash: expectedHash }) => { + const functionPrefix = 'onRequest'; + + const fileExists = fs.existsSync(filePath); + if (!fileExists) + return response(false, functionPrefix, 'Installer not found:', { + info: { filePath }, + }); + + const installerHash = checkInstallerHash(filePath, expectedHash); + if (!installerHash) return response(false, functionPrefix); + + // For linux we execute the installer file + if (environment.isLinux) return installUpdate(filePath); + + // For other OS we launch the installer file + const openInstaller: boolean = shell.openItem(filePath); + if (!openInstaller) + return response( + false, + functionPrefix, + 'Not able to launch the installer' + ); + app.quit(); + return response(true, functionPrefix); + }); +}; diff --git a/source/main/utils/downloadManager.js b/source/main/utils/downloadManager.js index e36a72b739..fbd3287395 100644 --- a/source/main/utils/downloadManager.js +++ b/source/main/utils/downloadManager.js @@ -3,12 +3,13 @@ import { app } from 'electron'; import fs from 'fs'; import type { BrowserWindow } from 'electron'; import { MainIpcChannel } from '../ipc/lib/MainIpcChannel'; - +import { logger } from './logging'; import { ALLOWED_DOWNLOAD_DIRECTORIES, - DOWNLOAD_PROGRESS_DEFAULT, + DOWNLOAD_DATA_DEFAULT, DOWNLOAD_EVENT_TYPES as types, DOWNLOAD_STATES as states, + ERROR_TIME_AFTER_NO_END_EVENT, } from '../../common/config/downloadManagerConfig'; import { extractFileNameFromPath } from '../../common/utils/files'; import { downloadManagerLocalStorage as localStorage } from './mainLocalStorage'; @@ -22,11 +23,13 @@ import type { DownloadInfoProgress, DownloadInfoEnd, DownloadInfoError, - DownloadData, - DownloadProgressUpdate, + DownloadInfo, + DownloadDataUpdate, } from '../../common/types/downloadManager.types'; import { stateDirectoryPath } from '../config'; +export const downloads = {}; + export const getIdFromFileName = (fileName: string): string => fileName.replace(/\./g, '-'); @@ -58,130 +61,174 @@ export const getOriginalFilename = ({ return name; }; +const getPath = ( + info: DownloadInfo +): { temporaryPath: string, newPath: string } => { + const { destinationPath, temporaryFilename, originalFilename } = info; + const temporaryPath = `${destinationPath}/${temporaryFilename}`; + const newPath = `${destinationPath}/${originalFilename}`; + return { + temporaryPath, + newPath, + }; +}; + export const getEventActions = async ( - data: DownloadData, + info: DownloadInfo, window: BrowserWindow, requestDownloadChannel: MainIpcChannel< DownloadRendererRequest, DownloadMainResponse > ): Promise => { - const { downloadId } = data; - await localStorage.setData(data, downloadId); + const { downloadId } = info; + await localStorage.setInfo(info, downloadId); + let serverFileSize; + let checkNoEndEvent: TimeoutID; + + const startEvent = async () => { + logger.info('DownloadManager:startEvent.'); + const eventType = types.START; + const data = DOWNLOAD_DATA_DEFAULT; + requestDownloadChannel.send( + { + eventType, + info, + data, + }, + window.webContents + ); + }; + const downloadEvent = async ({ + totalSize, + downloadedSize: diskFileSize, + }: DownloadInfoInit) => { + logger.info('DownloadManager:downloadEvent.'); + serverFileSize = totalSize; + const rawData: DownloadDataUpdate = { + ...{ + serverFileSize, + diskFileSize, + remainingSize: serverFileSize, + }, + state: states.DOWNLOADING, + }; + const data = await localStorage.setData(rawData, downloadId); + requestDownloadChannel.send( + { + eventType: types.DOWNLOAD, + info, + data, + }, + window.webContents + ); + }; + const progressEvent = async ({ + total, + downloaded: downloadSize, + progress, + speed, + }: DownloadInfoProgress) => { + const rawData: DownloadDataUpdate = { + ...{ + remainingSize: total - downloadSize, + serverFileSize, + downloadSize, + progress, + speed, + }, + state: states.DOWNLOADING, + }; + const formattedData = await localStorage.setData(rawData, downloadId); + requestDownloadChannel.send( + { + eventType: types.PROGRESS, + info, + data: formattedData, + }, + window.webContents + ); + if (progress === 100) { + // Checks if the file was delete while the download was in progress + checkNoEndEvent = setTimeout(() => { + const { temporaryPath, newPath } = getPath(info); + if (!fs.existsSync(temporaryPath) || !fs.existsSync(newPath)) { + errorEvent({ message: 'The download file was manually deleted' }); + } + }, ERROR_TIME_AFTER_NO_END_EVENT); + } + }; + const endEvent = async ({ + totalSize: downloadSize, + onDiskSize: diskFileSize, + incomplete, + }: DownloadInfoEnd) => { + clearTimeout(checkNoEndEvent); + delete downloads[downloadId]; + logger.info('DownloadManager:endEvent.'); + const rawData: DownloadDataUpdate = { + ...{ + downloadSize, + diskFileSize, + incomplete, + }, + state: states.FINISHED, + }; + const formattedData = await localStorage.setData(rawData, downloadId); + const { temporaryPath, newPath } = getPath(info); + fs.renameSync(temporaryPath, newPath); + requestDownloadChannel.send( + { + eventType: types.END, + info, + data: formattedData, + }, + window.webContents + ); + const { persistLocalData } = info.options; + if (!persistLocalData) await localStorage.unset(downloadId); + }; + const pauseEvent = async () => { + logger.info('DownloadManager:pauseEvent.'); + const newState: DownloadDataUpdate = { + state: states.PAUSED, + }; + const formattedData = await localStorage.setData(newState, downloadId); + requestDownloadChannel.send( + { + eventType: types.PAUSE, + info, + data: formattedData, + }, + window.webContents + ); + }; + const errorEvent = async ({ message }: DownloadInfoError) => { + logger.error('DownloadManager:errorEvent', { error: message }); + const rawData: DownloadDataUpdate = { + ...{ + message, + }, + state: states.FAILED, + }; + const formattedData = await localStorage.setData(rawData, downloadId); + requestDownloadChannel.send( + { + eventType: types.ERROR, + info, + data: formattedData, + error: message, + }, + window.webContents + ); + }; + return { - start: async () => { - const eventType = types.START; - const progress = DOWNLOAD_PROGRESS_DEFAULT; - requestDownloadChannel.send( - { - eventType, - data, - progress, - }, - window.webContents - ); - }, - download: async ({ - totalSize: serverFileSize, - downloadedSize: diskFileSize, - }: DownloadInfoInit) => { - const rawProgress: DownloadProgressUpdate = { - ...{ - serverFileSize, - diskFileSize, - remainingSize: serverFileSize, - }, - state: states.DOWNLOADING, - }; - const progress = await localStorage.setProgress(rawProgress, downloadId); - requestDownloadChannel.send( - { - eventType: types.DOWNLOAD, - data, - progress, - }, - window.webContents - ); - }, - progress: async ({ - total, - downloaded: downloadSize, - progress, - speed, - }: DownloadInfoProgress) => { - const rawProgress: DownloadProgressUpdate = { - ...{ - remainingSize: total - downloadSize, - downloadSize, - progress, - speed, - }, - state: states.DOWNLOADING, - }; - const formattedProgress = await localStorage.setProgress( - rawProgress, - downloadId - ); - requestDownloadChannel.send( - { - eventType: types.PROGRESS, - data, - progress: formattedProgress, - }, - window.webContents - ); - }, - end: async ({ - totalSize: downloadSize, - onDiskSize: diskFileSize, - incomplete, - }: DownloadInfoEnd) => { - const rawProgress: DownloadProgressUpdate = { - ...{ - downloadSize, - diskFileSize, - incomplete, - }, - state: states.FINISHED, - }; - const formattedProgress = await localStorage.setProgress( - rawProgress, - downloadId - ); - const { destinationPath, temporaryFilename, originalFilename } = data; - const temporaryPath = `${destinationPath}/${temporaryFilename}`; - const newPath = `${destinationPath}/${originalFilename}`; - fs.renameSync(temporaryPath, newPath); - requestDownloadChannel.send( - { - eventType: types.END, - data, - progress: formattedProgress, - }, - window.webContents - ); - const { persistLocalData } = data; - if (!persistLocalData) await localStorage.unset(downloadId); - }, - error: async ({ message }: DownloadInfoError) => { - const rawProgress: DownloadProgressUpdate = { - ...{ - message, - }, - state: states.FAILED, - }; - const formattedProgress = await localStorage.setProgress( - rawProgress, - downloadId - ); - requestDownloadChannel.send( - { - eventType: types.ERROR, - data, - progress: formattedProgress, - }, - window.webContents - ); - }, + start: startEvent, + download: downloadEvent, + progress: progressEvent, + end: endEvent, + pause: pauseEvent, + error: errorEvent, }; }; diff --git a/source/main/utils/mainErrorHandler.js b/source/main/utils/mainErrorHandler.js index 77f44a3229..8d0a2c5030 100644 --- a/source/main/utils/mainErrorHandler.js +++ b/source/main/utils/mainErrorHandler.js @@ -17,7 +17,11 @@ export default (onError?: Function) => { }); process.on('unhandledRejection', (error: any) => { - handleError('unhandledRejection', error); + handleError('unhandledRejection', { + error, + stack: error.stack, + message: error.message, + }); }); app.on('gpu-process-crashed', (event: any, killed: boolean) => { diff --git a/source/main/utils/mainLocalStorage.js b/source/main/utils/mainLocalStorage.js index c7aeeb3c51..8a55f06dca 100644 --- a/source/main/utils/mainLocalStorage.js +++ b/source/main/utils/mainLocalStorage.js @@ -5,53 +5,49 @@ import { STORAGE_KEYS, } from '../../common/config/electron-store.config'; import type { + DownloadInfo, DownloadData, - DownloadProgress, - DownloadProgressUpdate, + DownloadDataUpdate, DownloadLocalDataResponse, } from '../../common/types/downloadManager.types'; import { - DOWNLOAD_PROGRESS_DEFAULT, + DOWNLOAD_DATA_DEFAULT, DOWNLOAD_STATES, } from '../../common/config/downloadManagerConfig'; import { requestElectronStore } from '../ipc/electronStoreConversation'; export const downloadManagerLocalStorage = { get: async (id: string): Promise => { - const { data, progress } = + const { info, data } = (await requestElectronStore({ type: STORAGE_TYPES.GET, key: STORAGE_KEYS.DOWNLOAD_MANAGER, id, })) || {}; - if (!data || !progress) throw new Error('Invalid download Id'); - return { data, progress }; + return { info, data }; }, getAll: async () => { - const data = await requestElectronStore({ + const info = await requestElectronStore({ type: STORAGE_TYPES.GET, key: STORAGE_KEYS.DOWNLOAD_MANAGER, }); - return data || []; + return info || []; }, - setData: async (data: DownloadData, id: string) => { - const progress: DownloadProgress = DOWNLOAD_PROGRESS_DEFAULT; + setInfo: async (info: DownloadInfo, id: string) => { + const data: DownloadData = DOWNLOAD_DATA_DEFAULT; requestElectronStore({ type: STORAGE_TYPES.SET, key: STORAGE_KEYS.DOWNLOAD_MANAGER, - data: { data, progress }, + data: { info, data }, id, }); }, - setProgress: async ( - newProgres: DownloadProgressUpdate, + setData: async ( + newData: DownloadDataUpdate, id: string - ): Promise => { - const { - progress: oldProgress, - data, - } = await downloadManagerLocalStorage.get(id); - const progress = mergeWith(oldProgress, newProgres, (o, n, key) => { + ): Promise => { + const { data: oldData, info } = await downloadManagerLocalStorage.get(id); + const data = mergeWith(oldData, newData, (o, n, key) => { if (typeof n === 'number' && key !== 'remainingSize') return Math.max(o, n); return n; @@ -59,21 +55,26 @@ export const downloadManagerLocalStorage = { await requestElectronStore({ type: STORAGE_TYPES.SET, key: STORAGE_KEYS.DOWNLOAD_MANAGER, - data: { data, progress }, + data: { info, data }, id, }); - return progress; + return data; }, - setAllStopped: async () => { + setAllPaused: async () => { const downloads = await downloadManagerLocalStorage.getAll(); const downloadsArray = Object.keys(downloads); for (let index = 0; index < downloadsArray.length; index++) { - await downloadManagerLocalStorage.setProgress( - { - state: DOWNLOAD_STATES.STOPPED, - }, - downloadsArray[index] - ); + const downloadId = downloadsArray[index]; + const { data } = downloads[downloadId]; + const { state, progress } = data; + if (state === DOWNLOAD_STATES.DOWNLOADING && progress < 100) { + await downloadManagerLocalStorage.setData( + { + state: DOWNLOAD_STATES.PAUSED, + }, + downloadId + ); + } } }, unset: async (id: string) => { @@ -86,5 +87,9 @@ export const downloadManagerLocalStorage = { key: STORAGE_KEYS.DOWNLOAD_MANAGER, data: omit(localDownloadsData, id), }); + await requestElectronStore({ + type: STORAGE_TYPES.DELETE, + key: STORAGE_KEYS.APP_AUTOMATIC_UPDATE_FAILED, + }); }, }; diff --git a/source/renderer/app/App.js b/source/renderer/app/App.js index cea7c76cf0..e099c0c588 100755 --- a/source/renderer/app/App.js +++ b/source/renderer/app/App.js @@ -14,7 +14,6 @@ import ThemeManager from './ThemeManager'; import AboutDialog from './containers/static/AboutDialog'; import DaedalusDiagnosticsDialog from './containers/status/DaedalusDiagnosticsDialog'; import NotificationsContainer from './containers/notifications/NotificationsContainer'; -import AutomaticUpdateNotificationDialog from './containers/notifications/AutomaticUpdateNotificationDialog'; import NewsOverlayContainer from './containers/news/NewsOverlayContainer'; import { DIALOGS } from '../../common/ipc/constants'; import type { StoresMap } from './stores/index'; @@ -34,8 +33,7 @@ export default class App extends Component<{ render() { const { stores, actions, history } = this.props; - const { app, appUpdate, networkStatus } = stores; - const { showManualUpdate, showNextUpdate } = appUpdate; + const { app, networkStatus } = stores; const { isActiveDialog, isSetupPage } = app; const { isNodeStopping, isNodeStopped } = networkStatus; const locale = stores.profile.currentLocale; @@ -46,8 +44,6 @@ export default class App extends Component<{ const canShowNews = !isSetupPage && // Active page is not "Language Selection" or "Terms of Use" - !showNextUpdate && // Automatic update not available - !showManualUpdate && // Manual update not available !isNodeStopping && // Daedalus is not shutting down !isNodeStopped; // Daedalus is not shutting down @@ -68,17 +64,13 @@ export default class App extends Component<{ {mobxDevTools} - {showNextUpdate ? ( - - ) : ( - [ - isActiveDialog(ABOUT) && , - isActiveDialog(DAEDALUS_DIAGNOSTICS) && ( - - ), - , - ] - )} + {[ + isActiveDialog(ABOUT) && , + isActiveDialog(DAEDALUS_DIAGNOSTICS) && ( + + ), + , + ]} {canShowNews && [ , , diff --git a/source/renderer/app/actions/app-update-actions.js b/source/renderer/app/actions/app-update-actions.js index d8ba40e139..19f5dd1737 100644 --- a/source/renderer/app/actions/app-update-actions.js +++ b/source/renderer/app/actions/app-update-actions.js @@ -1,10 +1,11 @@ // @flow import Action from './lib/Action'; -// ======= NODE UPDATE ACTIONS ======= +// ======= APP UPDATE ACTIONS ======= export default class AppUpdateActions { - acceptAppUpdate: Action = new Action(); - postponeAppUpdate: Action = new Action(); - getLatestAvailableAppVersion: Action = new Action(); + installUpdate: Action = new Action(); + postponeUpdate: Action = new Action(); + openAppUpdateOverlay: Action = new Action(); + closeAppUpdateOverlay: Action = new Action(); } diff --git a/source/renderer/app/api/api.js b/source/renderer/app/api/api.js index 8f019d8d75..f75a7732ce 100644 --- a/source/renderer/app/api/api.js +++ b/source/renderer/app/api/api.js @@ -27,12 +27,6 @@ import { getNetworkInfo } from './network/requests/getNetworkInfo'; import { getNetworkClock } from './network/requests/getNetworkClock'; import { getNetworkParameters } from './network/requests/getNetworkParameters'; -// App update requests -import { applyAppUpdate } from './nodes/requests/applyAppUpdate'; -// import { getNextAppUpdate } from './nodes/requests/getNextAppUpdate'; -import { postponeAppUpdate } from './nodes/requests/postponeAppUpdate'; -import { getLatestAppVersion } from './nodes/requests/getLatestAppVersion'; - // Transactions requests import { getTransactionFee } from './transactions/requests/getTransactionFee'; import { getByronWalletTransactionFee } from './transactions/requests/getByronWalletTransactionFee'; @@ -80,10 +74,7 @@ import { joinStakePool } from './staking/requests/joinStakePool'; import { quitStakePool } from './staking/requests/quitStakePool'; // Utility functions -import { - awaitUpdateChannel, - cardanoFaultInjectionChannel, -} from '../ipc/cardano.ipc'; +import { cardanoFaultInjectionChannel } from '../ipc/cardano.ipc'; import patchAdaApi from './utils/patchAdaApi'; import { getLegacyWalletId, utcStringToDate } from './utils'; import { logger } from '../utils/logging'; @@ -123,13 +114,6 @@ import type { GetNetworkParametersApiResponse, } from './network/types'; -// Nodes Types -import type { - LatestAppVersionInfoResponse, - AppInfo, - GetLatestAppVersionResponse, -} from './nodes/types'; - // Transactions Types import type { Transaction, @@ -1258,49 +1242,6 @@ export default class AdaApi { } }; - nextUpdate = async (): Promise => { - logger.debug('AdaApi::nextUpdate called'); - - /* TODO: Re-enable when API is available - try { - const appUpdate = await getNextAppUpdate(this.config); - if (appUpdate && appUpdate.version) { - logger.debug('AdaApi::nextUpdate success', { appUpdate }); - return appUpdate; - } - logger.debug('AdaApi::nextUpdate success: No Update Available'); - } catch (error) { - logger.error('AdaApi::nextUpdate error', { error }); - throw new GenericApiError(error); - } - */ - - return null; - }; - - postponeUpdate = async (): Promise => { - logger.debug('AdaApi::postponeUpdate called'); - try { - const response: Promise = await postponeAppUpdate(this.config); - logger.debug('AdaApi::postponeUpdate success', { response }); - } catch (error) { - logger.error('AdaApi::postponeUpdate error', { error }); - throw new ApiError(error); - } - }; - - applyUpdate = async (): Promise => { - logger.debug('AdaApi::applyUpdate called'); - try { - await awaitUpdateChannel.send(); - const response: Promise = await applyAppUpdate(this.config); - logger.debug('AdaApi::applyUpdate success', { response }); - } catch (error) { - logger.error('AdaApi::applyUpdate error', { error }); - throw new ApiError(error); - } - }; - updateWallet = async (request: UpdateWalletRequest): Promise => { logger.debug('AdaApi::updateWallet called', { parameters: filterLogData(request), @@ -1731,43 +1672,6 @@ export default class AdaApi { } }; - getLatestAppVersion = async (): Promise => { - logger.debug('AdaApi::getLatestAppVersion called'); - try { - const { isWindows, platform } = global.environment; - const latestAppVersionInfo: LatestAppVersionInfoResponse = await getLatestAppVersion(); - - const latestAppVersionPath = `platforms.${ - isWindows ? 'windows' : platform - }.version`; - - const applicationVersionPath = `platforms.${ - isWindows ? 'windows' : platform - }.applicationVersion`; - - const latestAppVersion = get( - latestAppVersionInfo, - latestAppVersionPath, - null - ); - - const applicationVersion = get( - latestAppVersionInfo, - applicationVersionPath, - null - ); - logger.debug('AdaApi::getLatestAppVersion success', { - latestAppVersion, - latestAppVersionInfo, - applicationVersion, - }); - return { latestAppVersion, applicationVersion }; - } catch (error) { - logger.error('AdaApi::getLatestAppVersion error', { error }); - throw new ApiError(error); - } - }; - getNews = async (): Promise => { logger.debug('AdaApi::getNews called'); @@ -1857,9 +1761,6 @@ export default class AdaApi { // No implementation here but can be overwritten setLocalTimeDifference: Function; setSyncProgress: Function; - setNextUpdate: Function; - setLatestAppVersion: Function; - setApplicationVersion: Function; setFaultyNodeSettingsApi: boolean; resetTestOverrides: Function; diff --git a/source/renderer/app/api/news/types.js b/source/renderer/app/api/news/types.js index 804906c81d..238b82a4df 100644 --- a/source/renderer/app/api/news/types.js +++ b/source/renderer/app/api/news/types.js @@ -1,5 +1,7 @@ // @flow +import type { Platform } from '../../../../common/types/environment.types'; + export type NewsTranslations = { 'en-US': string, 'ja-JP': string, @@ -17,7 +19,22 @@ export type NewsTarget = { platform: string, }; -export type NewsType = 'incident' | 'alert' | 'announcement' | 'info'; +export type NewsType = + | 'incident' + | 'alert' + | 'announcement' + | 'info' + | 'software-update'; + +export type SoftwareUpdateInfo = { + version: string, + hash: string, + url: string, +}; + +export type SoftwareUpdate = { + [key: Platform]: SoftwareUpdateInfo, +}; export type NewsTimestamp = number; @@ -28,6 +45,7 @@ export type NewsItem = { action: NewsAction, date: NewsTimestamp, type: NewsType, + softwareUpdate?: SoftwareUpdate, }; export type GetNewsResponse = { diff --git a/source/renderer/app/api/nodes/requests/applyAppUpdate.js b/source/renderer/app/api/nodes/requests/applyAppUpdate.js deleted file mode 100644 index 0e60e2c7df..0000000000 --- a/source/renderer/app/api/nodes/requests/applyAppUpdate.js +++ /dev/null @@ -1,10 +0,0 @@ -// @flow -import type { RequestConfig } from '../../common/types'; -import { request } from '../../utils/request'; - -export const applyAppUpdate = (config: RequestConfig): Promise => - request({ - method: 'POST', - path: '/api/internal/apply-update', - ...config, - }); diff --git a/source/renderer/app/api/nodes/requests/getLatestAppVersion.js b/source/renderer/app/api/nodes/requests/getLatestAppVersion.js deleted file mode 100644 index 5eb3f72be5..0000000000 --- a/source/renderer/app/api/nodes/requests/getLatestAppVersion.js +++ /dev/null @@ -1,16 +0,0 @@ -// @flow -import type { DaedalusLatestVersionResponse } from '../types'; -import { externalRequest } from '../../utils/externalRequest'; -import { getLatestVersionInfoUrl } from '../../../utils/network'; - -const { isStaging, isTestnet, network } = global.environment; -const hostname = getLatestVersionInfoUrl(network); -const path = isStaging || isTestnet ? '' : '/update.cardano-mainnet.iohk.io'; - -export const getLatestAppVersion = (): Promise => - externalRequest({ - hostname, - path: `${path}/daedalus-latest-version.json`, - method: 'GET', - protocol: 'https', - }); diff --git a/source/renderer/app/api/nodes/requests/getNextAppUpdate.js b/source/renderer/app/api/nodes/requests/getNextAppUpdate.js deleted file mode 100644 index 6955d3f37f..0000000000 --- a/source/renderer/app/api/nodes/requests/getNextAppUpdate.js +++ /dev/null @@ -1,10 +0,0 @@ -// @flow -import type { RequestConfig } from '../../common/types'; -import { request } from '../../utils/request'; - -export const getNextAppUpdate = (config: RequestConfig): Promise => - request({ - method: 'GET', - path: '/api/internal/next-update', - ...config, - }); diff --git a/source/renderer/app/api/nodes/requests/postponeAppUpdate.js b/source/renderer/app/api/nodes/requests/postponeAppUpdate.js deleted file mode 100644 index ebcf463e4f..0000000000 --- a/source/renderer/app/api/nodes/requests/postponeAppUpdate.js +++ /dev/null @@ -1,10 +0,0 @@ -// @flow -import type { RequestConfig } from '../../common/types'; -import { request } from '../../utils/request'; - -export const postponeAppUpdate = (config: RequestConfig): Promise => - request({ - method: 'POST', - path: '/api/internal/postpone-update', - ...config, - }); diff --git a/source/renderer/app/api/nodes/types.js b/source/renderer/app/api/nodes/types.js deleted file mode 100644 index 645ece3882..0000000000 --- a/source/renderer/app/api/nodes/types.js +++ /dev/null @@ -1,46 +0,0 @@ -// @flow -export type AppInfo = { - applicationName: string, - version: number, -}; - -// req/res Node Types - -export type GetLatestAppVersionResponse = { - latestAppVersion: ?string, - applicationVersion: ?number, -}; - -export type Platform = { - hash: string, - SHA256: string, - signature: string, - version: string, - URL: string, -}; - -export type Platforms = { - ['windows' | 'darwin' | 'linux']: Platform, -}; - -export type DaedalusLatestVersionResponse = { - linux: string, - linuxHash: string, - linuxSHA256: string, - linuxSignature: string, - linuxURL: string, - macos: string, - macosHash: string, - macosSHA256: string, - macosSignature: string, - macosURL: string, - platforms: Platforms, - release_notes: ?string, - win64: string, - win64Hash: string, - win64SHA256: string, - win64Signature: string, - win64URL: string, -}; - -export type LatestAppVersionInfoResponse = DaedalusLatestVersionResponse; diff --git a/source/renderer/app/api/utils/localStorage.js b/source/renderer/app/api/utils/localStorage.js index afba6887a2..4a99047f61 100644 --- a/source/renderer/app/api/utils/localStorage.js +++ b/source/renderer/app/api/utils/localStorage.js @@ -123,12 +123,12 @@ export default class LocalStorageApi { unsetTermsOfUseAcceptance = (): Promise => LocalStorageApi.unset(keys.TERMS_OF_USE_ACCEPTANCE); - getUserTheme = (): Promise => LocalStorageApi.get(keys.USER_THEME); + getUserTheme = (): Promise => LocalStorageApi.get(keys.THEME); setUserTheme = (theme: string): Promise => - LocalStorageApi.set(keys.USER_THEME, theme); + LocalStorageApi.set(keys.THEME, theme); - unsetUserTheme = (): Promise => LocalStorageApi.unset(keys.USER_THEME); + unsetUserTheme = (): Promise => LocalStorageApi.unset(keys.THEME); getDataLayerMigrationAcceptance = (): Promise => LocalStorageApi.get(keys.DATA_LAYER_MIGRATION_ACCEPTANCE, false); @@ -215,6 +215,24 @@ export default class LocalStorageApi { unsetWalletMigrationStatus = (): Promise => LocalStorageApi.unset(keys.WALLET_MIGRATION_STATUS); + getAppAutomaticUpdateFailed = (): Promise => + LocalStorageApi.get(keys.APP_AUTOMATIC_UPDATE_FAILED, false); + + setAppAutomaticUpdateFailed = (): Promise => + LocalStorageApi.set(keys.APP_AUTOMATIC_UPDATE_FAILED, true); + + unsetAppAutomaticUpdateFailed = (): Promise => + LocalStorageApi.unset(keys.APP_AUTOMATIC_UPDATE_FAILED); + + getAppUpdateCompleted = (): Promise => + LocalStorageApi.get(keys.APP_UPDATE_COMPLETED, false); + + setAppUpdateCompleted = (verstion: string): Promise => + LocalStorageApi.set(keys.APP_UPDATE_COMPLETED, verstion, ''); + + unsetAppUpdateCompleted = (): Promise => + LocalStorageApi.unset(keys.APP_UPDATE_COMPLETED); + reset = async () => { await LocalStorageApi.reset(); }; diff --git a/source/renderer/app/api/utils/patchAdaApi.js b/source/renderer/app/api/utils/patchAdaApi.js index 26581f494e..111f17f382 100644 --- a/source/renderer/app/api/utils/patchAdaApi.js +++ b/source/renderer/app/api/utils/patchAdaApi.js @@ -4,7 +4,6 @@ import { action } from 'mobx'; import BigNumber from 'bignumber.js/bignumber'; import AdaApi from '../api'; import { getNetworkInfo } from '../network/requests/getNetworkInfo'; -import { getLatestAppVersion } from '../nodes/requests/getLatestAppVersion'; import { logger } from '../../utils/logging'; import packageJson from '../../../../../package.json'; import ApiError from '../../domains/ApiError'; @@ -17,17 +16,10 @@ import type { GetNetworkInfoResponse, NetworkInfoResponse, } from '../network/types'; -import type { - LatestAppVersionInfoResponse, - GetLatestAppVersionResponse, -} from '../nodes/types'; import type { GetNewsResponse } from '../news/types'; -let LATEST_APP_VERSION = null; let LOCAL_TIME_DIFFERENCE = 0; let SYNC_PROGRESS = null; -let NEXT_ADA_UPDATE = null; -let APPLICATION_VERSION = null; let TESTING_NEWSFEED_JSON: ?GetNewsResponse; let TESTING_WALLETS_DATA: Object = {}; @@ -78,72 +70,6 @@ export default (api: AdaApi) => { SYNC_PROGRESS = syncProgress; }; - api.nextUpdate = async (): Promise => { - let appUpdate = null; - - if (NEXT_ADA_UPDATE) { - appUpdate = { - version: NEXT_ADA_UPDATE, - }; - } - - logger.debug('AdaApi::nextUpdate success', { appUpdate }); - return Promise.resolve(appUpdate); - }; - - api.setNextUpdate = async nextUpdate => { - NEXT_ADA_UPDATE = nextUpdate; - }; - - api.getLatestAppVersion = async (): Promise => { - logger.debug('AdaApi::getLatestAppVersion (PATCHED) called'); - try { - const { isWindows, platform } = global.environment; - const latestAppVersionInfo: LatestAppVersionInfoResponse = await getLatestAppVersion(); - const latestAppVersionPath = `platforms.${ - isWindows ? 'windows' : platform - }.version`; - - const applicationVersionPath = `platforms.${ - isWindows ? 'windows' : platform - }.applicationVersion`; - - const latestAppVersion = get( - latestAppVersionInfo, - latestAppVersionPath, - null - ); - - const applicationVersion = get( - latestAppVersionInfo, - applicationVersionPath, - null - ); - - logger.debug('AdaApi::getLatestAppVersion success', { - latestAppVersion, - latestAppVersionInfo, - applicationVersion, - }); - - return { - latestAppVersion: LATEST_APP_VERSION || latestAppVersion, - applicationVersion: APPLICATION_VERSION || applicationVersion, - }; - } catch (error) { - logger.error('AdaApi::getLatestAppVersion (PATCHED) error', { error }); - throw new ApiError(); - } - }; - - api.setLatestAppVersion = async (latestAppVersion: ?string) => { - LATEST_APP_VERSION = latestAppVersion; - }; - - api.setApplicationVersion = async (applicationVersion: number) => { - APPLICATION_VERSION = applicationVersion; - }; - api.setTestingNewsFeed = (testingNewsFeedData: ?GetNewsResponse) => { const { version: packageJsonVersion } = packageJson; if (!testingNewsFeedData) { @@ -240,9 +166,6 @@ export default (api: AdaApi) => { api.resetTestOverrides = () => { TESTING_WALLETS_DATA = {}; SYNC_PROGRESS = null; - LATEST_APP_VERSION = null; - NEXT_ADA_UPDATE = null; - APPLICATION_VERSION = null; LOCAL_TIME_DIFFERENCE = 0; }; }; diff --git a/source/renderer/app/api/utils/request.js b/source/renderer/app/api/utils/request.js index 1f72196fe9..8ef37cceff 100644 --- a/source/renderer/app/api/utils/request.js +++ b/source/renderer/app/api/utils/request.js @@ -17,9 +17,7 @@ export type RequestOptions = { }, }; -const ALLOWED_ERROR_EXCEPTION_PATHS = [ - '/api/internal/next-update', // when nextAdaUpdate receives a 404, it isn't an error -]; +const ALLOWED_ERROR_EXCEPTION_PATHS = []; const { isIncentivizedTestnet } = global.environment; diff --git a/source/renderer/app/components/appUpdate/AppUpdateOverlay.js b/source/renderer/app/components/appUpdate/AppUpdateOverlay.js new file mode 100644 index 0000000000..d8eada6d54 --- /dev/null +++ b/source/renderer/app/components/appUpdate/AppUpdateOverlay.js @@ -0,0 +1,355 @@ +// @flow +import React, { Component } from 'react'; +import { observer } from 'mobx-react'; +import SVGInline from 'react-svg-inline'; +import classnames from 'classnames'; +import { get } from 'lodash'; +import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; +import { Button } from 'react-polymorph/lib/components/Button'; +import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; +import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin'; +import { Link } from 'react-polymorph/lib/components/Link'; +import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; +import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; +import { ButtonSpinnerSkin } from 'react-polymorph/lib/skins/simple/ButtonSpinnerSkin'; +import ReactMarkdown from 'react-markdown'; +import News from '../../domains/News'; +import styles from './AppUpdateOverlay.scss'; +import DialogCloseButton from '../widgets/DialogCloseButton'; +import ProgressBarLarge from '../widgets/ProgressBarLarge'; +import externalLinkIcon from '../../assets/images/link-ic.inline.svg'; + +const messages = defineMessages({ + title: { + id: 'appUpdate.overlay.title', + defaultMessage: '!!!Software update available!', + description: '"title" for the App Update Overlay', + }, + subtitle: { + id: 'appUpdate.overlay.subtitle', + defaultMessage: + '!!!You are currently running Daedalus version {currentAppVersion}.
Daedalus version {availableAppVersion} is now available to download.', + description: '"subtitle" for the App Update Overlay', + }, + checkboxLabel: { + id: 'appUpdate.overlay.checkboxLabel', + defaultMessage: + '!!!I understand that I need to complete the installation before starting Daedalus.', + description: '"checkboxLabel" for the App Update Overlay', + }, + buttonLaunchInstallerLabel: { + id: 'appUpdate.overlay.button.launchInstaller.label', + defaultMessage: '!!!Quit Daedalus and start the installation', + description: '"buttonLaunchInstallerLabel" for the App Update Overlay', + }, + buttonInstallUpdateLabel: { + id: 'appUpdate.overlay.button.installUpdate.label', + defaultMessage: '!!!Install the update and restart Daedalus', + description: '"buttonInstallUpdateLabel" for the App Update Overlay', + }, + postponeInstallLinkLabel: { + id: 'appUpdate.overlay.postponeInstall.link.label', + defaultMessage: '!!!Postpone the update', + description: '"manualUpdateLinkLabel" for the App Update Overlay', + }, + installingUpdateLabel: { + id: 'appUpdate.overlay.installingUpdate.link.label', + defaultMessage: '!!!Installing update...', + description: '"installingUpdateLabel" for the App Update Overlay', + }, + downloadProgressLabel: { + id: 'appUpdate.overlay.downloadProgressLabel', + defaultMessage: '!!!Download in progress', + description: '"downloadProgressLabel" for the App Update Overlay', + }, + downloadTimeLeft: { + id: 'appUpdate.overlay.downloadTimeLeft', + defaultMessage: '!!!{downloadTimeLeft} left', + description: '"downloadTimeLeft" for the App Update Overlay', + }, + downloadProgressData: { + id: 'appUpdate.overlay.downloadProgressData', + defaultMessage: '!!!({totalDownloaded} of {totalDownloadSize} downloaded)', + description: '"downloadProgressData" for the App Update Overlay', + }, + manualUpdateDescriptionError: { + id: 'appUpdate.overlay.manualUpdate.description.error', + defaultMessage: + '!!!We were unable to launch the update installer automatically.', + description: '"manualUpdateDescriptionError" for the App Update Overlay', + }, + manualUpdateDescriptionErrorLinux: { + id: 'appUpdate.overlay.manualUpdate.description.errorLinux', + defaultMessage: '!!!We were unable to install the update.', + description: + '"manualUpdateDescriptionErrorLinux" for the App Update Overlay', + }, + manualUpdateDescriptionAction: { + id: 'appUpdate.overlay.manualUpdate.description.action', + defaultMessage: '!!!Please manually update Daedalus to its latest version.', + description: '"manualUpdateDescriptionAction" for the App Update Overlay', + }, + manualUpdateButtonLabel: { + id: 'appUpdate.overlay.manualUpdate.button.label', + defaultMessage: '!!!Follow instructions and manually update', + description: '"manualUpdateButtonLabel" for the App Update Overlay', + }, + manualUpdateButtonUrl: { + id: 'appUpdate.overlay.manualUpdate.button.url', + defaultMessage: '!!!https://daedaluswallet.io/en/download/', + description: '"manualUpdateButtonUrl" for the App Update Overlay', + }, +}); + +type Props = { + update: News.News, + onClose: Function, + downloadTimeLeft: string, + totalDownloaded: string, + totalDownloadSize: string, + availableAppVersion: string, + currentAppVersion: string, + downloadProgress: number, + isUpdateDownloaded: boolean, + isAutomaticUpdateFailed: boolean, + isWaitingToQuitDaedalus: boolean, + onInstallUpdate: Function, + onExternalLinkClick: Function, + onPostponeUpdate: Function, + installationProgress: number, + isLinux: boolean, +}; + +type State = { + areTermsOfUseAccepted: boolean, +}; + +@observer +export default class AppUpdateOverlay extends Component { + static contextTypes = { + intl: intlShape.isRequired, + }; + + state = { + areTermsOfUseAccepted: false, + }; + + toggleAcceptance = () => { + this.setState(prevState => ({ + areTermsOfUseAccepted: !prevState.areTermsOfUseAccepted, + })); + }; + + contentClickHandler(event: SyntheticMouseEvent) { + const linkUrl = get(event, ['target', 'href']); + if (linkUrl) { + event.preventDefault(); + event.stopPropagation(); + this.props.onExternalLinkClick(linkUrl); + } + } + + progressActions = () => { + const { intl } = this.context; + const { + downloadTimeLeft, + totalDownloaded, + totalDownloadSize, + downloadProgress, + } = this.props; + return ( +
+
+

+ {intl.formatMessage(messages.downloadProgressLabel)} +

+

+ + {intl.formatMessage(messages.downloadTimeLeft, { + downloadTimeLeft, + })} + {' '} + {intl.formatMessage(messages.downloadProgressData, { + totalDownloaded, + totalDownloadSize, + })} +

+
+ +
+ ); + }; + + openInstallerAction = () => { + const { intl } = this.context; + const { + onInstallUpdate, + onPostponeUpdate, + isWaitingToQuitDaedalus, + isLinux, + installationProgress, + } = this.props; + const { areTermsOfUseAccepted } = this.state; + const isCheckboxDisabled = isWaitingToQuitDaedalus; + const checkboxStyles = classnames([ + styles.checkbox, + isCheckboxDisabled ? styles.disabled : null, + ]); + const isButtonDisabled = !areTermsOfUseAccepted || isWaitingToQuitDaedalus; + const buttonStyles = classnames([ + styles.button, + isButtonDisabled ? styles.disabled : null, + isWaitingToQuitDaedalus ? styles.installing : null, + ]); + const buttonLabel = isLinux + ? messages.buttonInstallUpdateLabel + : messages.buttonLaunchInstallerLabel; + const postponeLinkStyles = classnames([ + styles.postponeLink, + !isLinux && isWaitingToQuitDaedalus ? styles.disabled : null, + isLinux && isWaitingToQuitDaedalus ? styles.noLink : null, + ]); + const postponeLabel = + isLinux && isWaitingToQuitDaedalus + ? messages.installingUpdateLabel + : messages.postponeInstallLinkLabel; + const postponeAction = !isWaitingToQuitDaedalus + ? onPostponeUpdate + : () => {}; + const actionsStyles = classnames([ + styles.actions, + isLinux && isWaitingToQuitDaedalus ? styles.progressBar : null, + ]); + return ( +
+ {!(isLinux && isWaitingToQuitDaedalus) && ( + <> + +
+ ); + }; + + manualUpdateAction = () => { + const { intl } = this.context; + const { onExternalLinkClick, onPostponeUpdate, isLinux } = this.props; + const errorMessage = isLinux + ? messages.manualUpdateDescriptionErrorLinux + : messages.manualUpdateDescriptionError; + return ( +
+
+ {intl.formatMessage(errorMessage)} + {intl.formatMessage(messages.manualUpdateDescriptionAction)} +
+
+ ); + }; + + render() { + const { intl } = this.context; + const { + update, + onClose, + isUpdateDownloaded, + availableAppVersion, + currentAppVersion, + isAutomaticUpdateFailed, + } = this.props; + const { content } = update; + let actions; + if (isAutomaticUpdateFailed) actions = this.manualUpdateAction(); + else if (!isUpdateDownloaded) actions = this.progressActions(); + else actions = this.openInstallerAction(); + + return ( +
{}} + > + {!isUpdateDownloaded && !isAutomaticUpdateFailed && ( + + )} +

{intl.formatMessage(messages.title)}

+ + + + +
+ +
+ {actions} +
+ ); + } +} diff --git a/source/renderer/app/components/appUpdate/AppUpdateOverlay.scss b/source/renderer/app/components/appUpdate/AppUpdateOverlay.scss new file mode 100644 index 0000000000..d4e40149ff --- /dev/null +++ b/source/renderer/app/components/appUpdate/AppUpdateOverlay.scss @@ -0,0 +1,245 @@ +@import '../../themes/mixins/link'; + +.component { + align-items: center; + background-color: var(--theme-app-update-overlay-background-color); + color: var(--theme-app-update-overlay-text-color); + display: flex; + flex-direction: column; + font-family: var(--font-regular); + height: 100vh; + justify-content: center; + position: fixed; + top: 0; + width: 100vw; + z-index: 22; + + .content { + background-color: var(--theme-news-overlay-update-content-background-color); + font-size: 16px; + line-height: 1.2; + margin-bottom: 20px; + max-height: 464px; + opacity: 0.8; + overflow-y: scroll; + padding: 12px 20px; + width: 608px; + word-break: break-word; + &::-webkit-scrollbar-thumb { + background-color: var( + --theme-news-overlay-update-content-scroll-background-color + ); + outline: none; + width: 4px; + + &:hover { + background-color: var( + --theme-news-overlay-update-content-scroll-hover-background-color + ); + } + } + + h1, + h2 { + font-family: var(--font-medium); + margin-bottom: 6px; + } + + h1 { + font-size: 18px; + } + + h2 { + font-size: 16px; + } + + * + h2 { + margin-top: 16px; + } + + ol, + ul { + list-style: disc; + margin-left: 20px; + } + + ol { + list-style-type: decimal; + } + + p, + li { + font-family: var(--font-regular); + font-size: 14px; + line-height: 1.5; + margin-bottom: 6px; + + strong { + color: var(--theme-app-update-overlay-text-color); + font-weight: 500; + } + } + + a { + border-bottom: 1px solid var(--theme-app-update-overlay-text-color); + color: var(--theme-app-update-overlay-text-color); + text-decoration: none; + } + + em { + font-style: italic; + } + } + + .title { + font-size: 20px; + line-height: 1.2; + margin-bottom: 16px; + margin-top: 22.5px; + } + + .subtitle { + color: var(--theme-app-update-overlay-opacity-text-color); + font-family: var(--font-medium); + line-height: 1.38; + margin-bottom: 16px; + text-align: center; + b { + color: var(--theme-app-update-overlay-text-color); + } + } + + .downloadProgress { + width: 608px; + } + .downloadProgressContent { + display: flex; + justify-content: space-between; + margin-bottom: 4px; + p { + font-size: 14px; + } + } + .downloadProgressData { + color: var(--theme-app-update-overlay-opacity-text-color); + b { + font-family: var(--font-bold); + } + } + + .actions { + text-align: center; + width: 540px; + &.progressBar { + width: 608px; + } + .checkbox { + margin-bottom: 30px; + &.disabled { + opacity: 0.4; + } + :global { + .SimpleCheckbox_check { + border: 2px solid var(--theme-news-overlay-update-button-text-color); + } + .SimpleCheckbox_checked { + background: var(--theme-news-overlay-update-button-text-color); + &:after { + border-color: var(--theme-news-overlay-update-background-color); + margin: -3px 0 0; + } + border: none; + } + .SimpleCheckbox_label { + color: var(--theme-app-update-overlay-text-color); + font-family: var(--font-regular); + text-align: left; + } + } + } + .button { + background-color: var( + --theme-news-overlay-update-button-background-color + ); + border: 1px solid var(--theme-news-overlay-update-button-text-color); + color: var(--theme-news-overlay-update-button-text-color); + cursor: pointer; + display: block; + margin: 0 auto 20px; + &:not(.disabled):hover { + background-color: var(--theme-news-overlay-update-button-text-color); + color: var(--theme-news-overlay-update-button-hover-text-color); + .externalLinkIcon { + svg path { + stroke: var(--theme-news-overlay-update-button-hover-text-color); + } + } + } + &.disabled { + cursor: default; + opacity: 0.4; + text-decoration: none; + } + .externalLinkIcon { + @include link(--theme-news-overlay-update-button-text-color); + border-bottom: none; + margin-right: 10px; + vertical-align: baseline; + width: 15px; + svg { + margin-left: 0; + vertical-align: sub; + width: 15px; + g { + stroke-width: 2px; + } + } + } + } + .postponeLink { + border-bottom-color: var(--theme-app-update-overlay-text-color); + color: var(--theme-app-update-overlay-text-color); + &.disabled { + border-bottom-color: transparent; + cursor: default; + opacity: 0.4; + } + &.noLink { + border-bottom-color: transparent; + cursor: default; + } + } + :global { + .SimpleLoadingSpinner_root { + border-color: var(--theme-news-overlay-update-button-text-color) + var(--theme-news-overlay-update-button-text-color) transparent + transparent; + } + } + } + + .manualUpdateDescription { + color: var(--theme-app-update-overlay-manual-update-text-color); + font-family: var(--font-medium); + line-height: 1.38; + margin-bottom: 30px; + // margin-top: -10px; + text-align: center; + } + + .closeButton { + border-radius: 50%; + height: 44px; + opacity: 0.5; + right: 10px; + top: 10px; + width: 44px; + z-index: 999; + &:hover { + background-color: var( + --theme-news-overlay-update-content-background-color + ); + opacity: 0.8; + } + } +} diff --git a/source/renderer/app/components/loading/manual-update/ManualUpdate.js b/source/renderer/app/components/loading/manual-update/ManualUpdate.js deleted file mode 100644 index ce84266027..0000000000 --- a/source/renderer/app/components/loading/manual-update/ManualUpdate.js +++ /dev/null @@ -1,101 +0,0 @@ -// @flow -import React, { Component } from 'react'; -import { observer } from 'mobx-react'; -import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import ReactModal from 'react-modal'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import styles from './ManualUpdate.scss'; -import ButtonLink from '../../widgets/ButtonLink'; - -const messages = defineMessages({ - title: { - id: 'manualUpdate.title', - defaultMessage: '!!!Software update is available', - description: 'Title for "Manual update" overlay', - }, - descriptionLine1: { - id: 'manualUpdate.description1', - defaultMessage: - '!!!You are experiencing network connection issues, and you are not running the latest Daedalus version. Automatic updates are unavailable while Daedalus is not connected to Cardano network.', - description: 'Description line 1 of "Manual update" overlay', - }, - descriptionLine2: { - id: 'manualUpdate.description2', - defaultMessage: - '!!!You are currently running {currentAppVersion} version of Daedalus, and {availableAppVersion} version is available. Please manually update to that version since it may resolve your connecting issues.', - description: 'Description line 2 of "Manual update" overlay', - }, - actionButtonLabel: { - id: 'manualUpdate.button.label', - defaultMessage: '!!!Follow instructions and manually update', - description: - 'Label for "Follow instructions and manually update" action button on "Manual update" overlay', - }, - manualUpdateButtonUrl: { - id: 'manualUpdate.button.url', - defaultMessage: - '!!!https://iohk.zendesk.com/hc/en-us/articles/360023850634', - description: - 'Follow instructions and manually update link on "Manual update" overlay', - }, -}); - -type Props = { - availableAppVersion: ?string, - currentAppVersion: string, - onExternalLinkClick: Function, -}; - -@observer -export default class ManualUpdate extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - - render() { - const { - availableAppVersion, - currentAppVersion, - onExternalLinkClick, - } = this.props; - const { formatMessage } = this.context.intl; - - return ( - -
-

{formatMessage(messages.title)}

-
-

{formatMessage(messages.descriptionLine1)}

-

- -

-
- - - onExternalLinkClick(formatMessage(messages.manualUpdateButtonUrl)) - } - skin={ButtonSkin} - label={formatMessage(messages.actionButtonLabel)} - linkProps={{ - className: styles.btnLabel, - }} - /> -
-
- ); - } -} diff --git a/source/renderer/app/components/loading/manual-update/ManualUpdate.scss b/source/renderer/app/components/loading/manual-update/ManualUpdate.scss deleted file mode 100644 index fd692198a3..0000000000 --- a/source/renderer/app/components/loading/manual-update/ManualUpdate.scss +++ /dev/null @@ -1,91 +0,0 @@ -@import '../../../themes/mixins/link'; - -.overlay { - bottom: 0; - display: flex; - justify-content: center; - left: 0; - position: fixed; - right: 0; - top: 0; - z-index: 20; - - .dialog { - height: 100%; - margin: auto 0; - overflow: hidden; - width: 100%; - - &:focus { - outline: none; - } - .content { - background-color: var(--theme-manual-update-overlay-background-color); - display: flex; - flex-direction: column; - font-family: var(--font-regular); - height: 100%; - justify-content: center; - text-align: center; - - h1 { - color: var(--theme-manual-update-overlay-title-text-color); - font-size: 20px; - line-height: 1.2; - } - - .description { - margin: 16px auto; - p { - color: var(--theme-manual-update-overlay-text-color); - font-size: 16px; - line-height: 1.38; - margin-bottom: 14px; - max-width: 610px; - b { - color: var(--theme-manual-update-overlay-text-highlight-color); - font-family: var(--font-medium); - } - } - } - - .actionButton { - background-color: var( - --theme-manual-update-overlay-button-background-color - ); - border: solid 1px var(--theme-manual-update-overlay-button-border-color); - font-weight: 500; - line-height: 1.36; - margin: 0 auto; - .btnLabel { - color: var(--theme-manual-update-overlay-button-label-color); - &:before { - background-color: var( - --theme-manual-update-overlay-button-label-color - ); - } - } - - &:not(.disabled):hover { - background-color: var( - --theme-manual-update-overlay-button-background-color-hover - ); - color: var(--theme-manual-update-overlay-button-text-color-hover); - - .btnLabel { - color: var(--theme-manual-update-overlay-button-label-color-hover); - &:before { - background-color: var( - --theme-manual-update-overlay-button-label-color-hover - ); - } - } - } - } - } - } - - @media (max-width: 950px), (max-height: 678px) { - overflow-y: auto; - } -} diff --git a/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.js b/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.js index 1a66b0d2d8..2339a43250 100644 --- a/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.js +++ b/source/renderer/app/components/loading/syncing-connecting/SyncingConnecting.js @@ -36,14 +36,12 @@ type Props = { isTlsCertInvalid: boolean, hasLoadedCurrentLocale: boolean, hasLoadedCurrentTheme: boolean, - hasUnreadNews: boolean, + hasNotification: boolean, + hasUpdate: boolean, isCheckingSystemTime: boolean, isNodeResponding: boolean, isNodeSyncing: boolean, isNodeTimeCorrect: boolean, - isNewAppVersionAvailable: boolean, - isNewAppVersionLoading: boolean, - isNewAppVersionLoaded: boolean, disableDownloadLogs: boolean, showNewsFeedIcon: boolean, isIncentivizedTestnet: boolean, @@ -51,7 +49,6 @@ type Props = { onIssueClick: Function, onOpenExternalLink: Function, onDownloadLogs: Function, - onGetAvailableVersions: Function, onStatusIconClick: Function, onToggleNewsFeedIconClick: Function, }; @@ -68,18 +65,7 @@ export default class SyncingConnecting extends Component { } componentDidUpdate() { - const { connectingTime } = this.state; - const { - isConnected, - cardanoNodeState, - isSyncProgressStalling, - onGetAvailableVersions, - isNewAppVersionLoading, - isNewAppVersionLoaded, - isIncentivizedTestnet, - isFlight, - isVerifyingBlockchain, - } = this.props; + const { isConnected, isVerifyingBlockchain } = this.props; const canResetConnecting = this._connectingTimerShouldStop( isConnected, isVerifyingBlockchain @@ -89,23 +75,6 @@ export default class SyncingConnecting extends Component { if (canResetConnecting) { this._resetConnectingTime(); } - const isAppLoadingStuck = - !isVerifyingBlockchain && - (isSyncProgressStalling || - (!isConnected && - (connectingTime >= REPORT_ISSUE_TIME_TRIGGER || - cardanoNodeState === CardanoNodeStates.UNRECOVERABLE))); - // If app loading is stuck, check if a newer version is available and set flag (state) - if ( - isAppLoadingStuck && - !isNewAppVersionLoaded && - !isNewAppVersionLoading && - !isIncentivizedTestnet && - !global.isShelleyTestnet && - !isFlight - ) { - onGetAvailableVersions(); - } } componentWillUnmount() { @@ -157,8 +126,6 @@ export default class SyncingConnecting extends Component { isConnected, isSyncProgressStalling, cardanoNodeState, - isNewAppVersionLoaded, - isNewAppVersionAvailable, isIncentivizedTestnet, forceConnectivityIssue, isVerifyingBlockchain, @@ -175,11 +142,7 @@ export default class SyncingConnecting extends Component { if (isFlight || isIncentivizedTestnet || global.isShelleyTestnet) { return canReportConnectingIssue; } - return ( - isNewAppVersionLoaded && - !isNewAppVersionAvailable && - canReportConnectingIssue - ); + return canReportConnectingIssue; } render() { @@ -191,7 +154,8 @@ export default class SyncingConnecting extends Component { isSyncing, hasLoadedCurrentLocale, hasLoadedCurrentTheme, - hasUnreadNews, + hasNotification, + hasUpdate, onIssueClick, onOpenExternalLink, onDownloadLogs, @@ -238,7 +202,8 @@ export default class SyncingConnecting extends Component { )} diff --git a/source/renderer/app/components/news/NewsFeed.js b/source/renderer/app/components/news/NewsFeed.js index be6370fb57..a63f6ec8ed 100644 --- a/source/renderer/app/components/news/NewsFeed.js +++ b/source/renderer/app/components/news/NewsFeed.js @@ -9,6 +9,7 @@ import closeCrossThin from '../../assets/images/close-cross-thin.inline.svg'; import styles from './NewsFeed.scss'; import News from '../../domains/News'; import NewsItem from './NewsItem'; +import UpdateItem from './UpdateItem'; import LoadingSpinner from '../widgets/LoadingSpinner'; const messages = defineMessages({ @@ -40,6 +41,10 @@ type Props = { currentDateFormat: string, onOpenExternalLink: Function, onProceedNewsAction: Function, + onOpenAppUpdate: Function, + updateDownloadProgress?: number, + displayAppUpdateNewsItem?: boolean, + isUpdatePostponed: boolean, }; type State = { @@ -109,11 +114,18 @@ export default class NewsFeed extends Component { onProceedNewsAction, onOpenExternalLink, currentDateFormat, + onOpenAppUpdate, + updateDownloadProgress = 0, + isUpdatePostponed, + displayAppUpdateNewsItem, } = this.props; const { hasShadow } = this.state; - const totalNewsItems = get(news, 'all', []).length; - const totalUnreadNewsItems = get(news, 'unread', []).length; + const items = get(news, 'all', []); + const update = get(news, 'update'); + const totalUnreadNewsItems = get(items, 'unread', []).length; + const hasUpdateItem = displayAppUpdateNewsItem && update; + const componentClasses = classNames([ styles.component, isNewsFeedOpen ? styles.show : null, @@ -125,6 +137,11 @@ export default class NewsFeed extends Component { hasShadow ? styles.hasShadow : null, ]); + const newsFeedListStyles = classNames([ + styles.newsFeedList, + hasUpdateItem ? styles.hasUpdate : null, + ]); + return (
@@ -140,10 +157,29 @@ export default class NewsFeed extends Component {
-
- {news && totalNewsItems > 0 && ( -
- {news.all.map(newsItem => ( +
+ {hasUpdateItem && ( + <> + { + + } +
+ + )} + {items.length > 0 && ( +
+ {items.map(newsItem => ( { ))}
)} - {news && totalNewsItems === 0 && !isLoadingNews && ( + {news && items.length === 0 && !isLoadingNews && (

{intl.formatMessage(messages.newsFeedEmpty)}

)} - {(!news || totalNewsItems === 0) && isLoadingNews && ( + {(!news || items.length === 0) && isLoadingNews && (

{intl.formatMessage(messages.newsFeedNoFetch)} diff --git a/source/renderer/app/components/news/NewsFeed.scss b/source/renderer/app/components/news/NewsFeed.scss index ee054fa24b..2ed3f224cc 100644 --- a/source/renderer/app/components/news/NewsFeed.scss +++ b/source/renderer/app/components/news/NewsFeed.scss @@ -89,11 +89,10 @@ } } - .newsFeedList { + .newsFeedContainer { background: var(--theme-news-feed-background-color); - height: calc(100% - 84px); - overflow-x: hidden; - overflow-y: overlay; + font-family: var(--font-regular); + overflow: hidden; padding: 20px; &::-webkit-scrollbar-track { @@ -120,14 +119,26 @@ text-align: center; } } - - .newsFeedItemsContainer { - font-family: var(--font-regular); - overflow: hidden; + .newsFeedList { + height: calc(100% - 104px); + overflow-x: hidden; + overflow-y: overlay; + padding: 0 20px 20px 0; + position: absolute; + width: calc(100% - 20px); + &.hasUpdate { + height: calc(100% - 220px); + } } } &.show { margin-right: 0; } + + .separator { + border: 1px solid var(--theme-news-item-title-color); + margin: 15px 0; + opacity: 0.2; + } } diff --git a/source/renderer/app/components/news/NewsItem.scss b/source/renderer/app/components/news/NewsItem.scss index 4f2e7ef783..960ffaf831 100644 --- a/source/renderer/app/components/news/NewsItem.scss +++ b/source/renderer/app/components/news/NewsItem.scss @@ -37,7 +37,7 @@ word-break: break-all; .newsItemBadge { - background-color: var(--theme-news-item-badge-color); + background-color: var(--theme-news-item-badge-red-color); border-radius: 12.5px; display: inline-block; height: 8px; diff --git a/source/renderer/app/components/news/UpdateItem.js b/source/renderer/app/components/news/UpdateItem.js new file mode 100644 index 0000000000..77ab095ae2 --- /dev/null +++ b/source/renderer/app/components/news/UpdateItem.js @@ -0,0 +1,78 @@ +// @flow +import React, { Component } from 'react'; +import { observer } from 'mobx-react'; +import classNames from 'classnames'; +import moment from 'moment'; +import News /* , { NewsTypes } */ from '../../domains/News'; +import styles from './UpdateItem.scss'; + +type Props = { + updateItem: News.News, + onOpenAppUpdate: Function, + currentDateFormat: string, + downloadProgress: number, + isUpdatePostponed: boolean, +}; + +@observer +export default class UpdateItem extends Component { + static defaultProps = { + onupdateItemActionClick: null, + expandWithoutTransition: false, + }; + + generateTitleWithBadge = (title: string) => { + const wordsArray = title.split(' '); + const lastWordIndex = wordsArray.length - 1; + const lastWord = wordsArray[lastWordIndex]; + + // Remove last word from array + wordsArray.splice(lastWordIndex, 1); + // Join words without last one + const firstSentencePart = wordsArray.join(' '); + + return ( +

+ {firstSentencePart ? `${firstSentencePart} ` : null} + + {lastWord}  + {} + +

+ ); + }; + + render() { + const { + updateItem, + currentDateFormat, + onOpenAppUpdate, + downloadProgress, + isUpdatePostponed, + } = this.props; + const componentClasses = classNames([ + styles.component, + updateItem.type ? styles[updateItem.type] : null, + ]); + const title = this.generateTitleWithBadge(updateItem.title); + + return ( +
+ {title} +
+ {moment(updateItem.date).format(currentDateFormat)} +
+ {!isUpdatePostponed && ( +
+ + +
+ )} +
+ ); + } +} diff --git a/source/renderer/app/components/news/UpdateItem.scss b/source/renderer/app/components/news/UpdateItem.scss new file mode 100644 index 0000000000..0c96460892 --- /dev/null +++ b/source/renderer/app/components/news/UpdateItem.scss @@ -0,0 +1,68 @@ +.component, +.isRead, +.info { + background-color: rgba(0, 0, 0, 0.1); + border-radius: 5px; + cursor: pointer; + padding: 20px; + position: relative; + width: 100%; + word-break: break-word; + + & + .component { + margin-top: 10px; + } + + .title { + color: var(--theme-news-item-title-color); + font-family: var(--font-regular); + font-size: 16px; + line-height: 1.33; + margin-bottom: 2px; + + .lastWordWrapper { + display: inline-block; + word-break: break-all; + + .badge { + background-color: var(--theme-news-item-badge-green-color); + border-radius: 12.5px; + display: inline-block; + height: 8px; + margin: 3px 8px 3px 4px; + width: 8px; + } + } + } + + .date { + color: var(--theme-news-item-title-color); + font-family: var(--font-light); + font-size: 14px; + font-weight: 300; + line-height: 1.36; + margin-bottom: 6px; + opacity: 0.7; + } + + .downloadProgress { + height: 2px; + margin-bottom: -6px; + position: relative; + span { + background: var(--theme-news-item-title-color); + display: block; + height: 2px; + transition: width 0.24s; + } + em { + background: var(--theme-news-item-title-color); + bottom: 0; + height: 2px; + opacity: 0.5; + position: absolute; + width: 100%; + z-index: 0; + } + } +} diff --git a/source/renderer/app/components/notifications/AutomaticUpdateNotification.js b/source/renderer/app/components/notifications/AutomaticUpdateNotification.js deleted file mode 100644 index c3295af020..0000000000 --- a/source/renderer/app/components/notifications/AutomaticUpdateNotification.js +++ /dev/null @@ -1,121 +0,0 @@ -// @flow -import React, { Component } from 'react'; -import { observer } from 'mobx-react'; -import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl'; -import ReactModal from 'react-modal'; -import { Button } from 'react-polymorph/lib/components/Button'; -import { ButtonSkin } from 'react-polymorph/lib/skins/simple/ButtonSkin'; -import styles from './AutomaticUpdateNotification.scss'; - -const messages = defineMessages({ - title: { - id: 'automaticUpdate.title', - defaultMessage: '!!!Software update is available', - description: 'Title for "Automatic update" overlay', - }, - descriptionLine1: { - id: 'automaticUpdate.description1', - defaultMessage: - '!!!You are currently running Daedalus {currentAppVersion} and {nextAppVersion} is available.', - description: 'First description line of "Automatic update" overlay', - }, - descriptionLine2: { - id: 'automaticUpdate.description2', - defaultMessage: - '!!!Would you like to install the update? If choose to postpone, the update will be installed automatically on the next Daedalus launch.', - description: 'Second description line of "Automatic update" overlay', - }, - acceptButtonLabel: { - id: 'automaticUpdate.accept.button.label', - defaultMessage: '!!!Restart Daedalus and Update', - description: - 'Label for "Restart Daedalus and Update" action button on "Automatic update" overlay', - }, - postponeButtonLabel: { - id: 'automaticUpdate.postpone.button.label', - defaultMessage: '!!!Postpone until Daedalus restart', - description: - 'Label for "Postpone" action button on "Automatic update" overlay', - }, - newerVersionlabel: { - id: 'automaticUpdate.newerVersion.label', - defaultMessage: '!!!a newer version', - description: 'Label for "newer version" on "Automatic update" overlay', - }, -}); - -type Props = { - currentAppVersion: string, - nextUpdateVersion: ?string, - onAccept: Function, - onPostpone: Function, -}; - -@observer -export default class AutomaticUpdateNotification extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - - render() { - const { - currentAppVersion, - nextUpdateVersion, - onAccept, - onPostpone, - } = this.props; - const { formatMessage } = this.context.intl; - - return ( - -
-

{formatMessage(messages.title)}

-
-

- -

-

{formatMessage(messages.descriptionLine2)}

-
- -
-
-
-
- ); - } -} diff --git a/source/renderer/app/components/notifications/AutomaticUpdateNotification.scss b/source/renderer/app/components/notifications/AutomaticUpdateNotification.scss deleted file mode 100644 index 9fd4454679..0000000000 --- a/source/renderer/app/components/notifications/AutomaticUpdateNotification.scss +++ /dev/null @@ -1,101 +0,0 @@ -@import '../../themes/mixins/link'; - -.overlay { - bottom: 0; - display: flex; - justify-content: center; - left: 0; - position: fixed; - right: 0; - top: 0; - z-index: 101; - - .dialog { - height: 100%; - margin: auto 0; - overflow: hidden; - width: 100%; - - &:focus { - outline: none; - } - .content { - background-color: var(--theme-automatic-update-overlay-background-color); - display: flex; - flex-direction: column; - font-family: var(--font-regular); - height: 100%; - justify-content: center; - text-align: center; - - h1 { - color: var(--theme-automatic-update-overlay-title-text-color); - font-size: 20px; - line-height: 1.2; - } - - .description { - margin: 16px auto 0; - max-width: 620px; - p { - color: var(--theme-automatic-update-overlay-text-color); - font-size: 16px; - line-height: 1.38; - margin-bottom: 14px; - b { - color: var(--theme-automatic-update-overlay-text-highlight-color); - font-family: var(--font-medium); - } - } - } - - .acceptButton { - background-color: var( - --theme-automatic-update-overlay-button-background-color - ); - border: solid 1px - var(--theme-automatic-update-overlay-button-border-color); - display: block; - font-weight: 500; - line-height: 1.36; - margin: 30px auto 0; - - .btnLabel { - color: var(--theme-automatic-update-overlay-button-label-color); - } - - &:hover { - background-color: var( - --theme-automatic-update-overlay-button-background-color-hover - ); - border: none; - color: var(--theme-automatic-update-overlay-button-text-color-hover); - - .btnLabel { - color: var( - --theme-automatic-update-overlay-button-label-color-hover - ); - } - } - } - - .postponeButton { - @include link( - --theme-automatic-update-overlay-button-label-color-light - ); - display: inline-block; - font-size: 14px; - margin: 20px auto 0; - &:hover { - border-bottom: 1px solid - var(--theme-automatic-update-overlay-button-label-color); - color: var(--theme-automatic-update-overlay-button-label-color); - } - } - } - } - - @media (max-width: 950px), (max-height: 678px) { - overflow-y: auto; - } -} diff --git a/source/renderer/app/components/profile/data-layer-migration/DataLayerMigrationForm.scss b/source/renderer/app/components/profile/data-layer-migration/DataLayerMigrationForm.scss index c91045c883..8aacd21b68 100644 --- a/source/renderer/app/components/profile/data-layer-migration/DataLayerMigrationForm.scss +++ b/source/renderer/app/components/profile/data-layer-migration/DataLayerMigrationForm.scss @@ -54,8 +54,8 @@ .submitButton { background-color: var(--theme-data-migration-layer-button-background-color); - border: solid 1px var(--theme-data-migration-button-border-color) !important; - color: var(--theme-data-migration-button-label-color) !important; + border: solid 1px var(--theme-data-migration-layer-button-border-color) !important; + color: var(--theme-data-migration-layer-button-label-color) !important; line-height: 1.36 !important; margin-bottom: 30px; transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), diff --git a/source/renderer/app/components/wallet/settings/WalletSettings.js b/source/renderer/app/components/wallet/settings/WalletSettings.js index 1749de933d..64134f36eb 100644 --- a/source/renderer/app/components/wallet/settings/WalletSettings.js +++ b/source/renderer/app/components/wallet/settings/WalletSettings.js @@ -15,6 +15,8 @@ import ChangeSpendingPasswordDialog from './ChangeSpendingPasswordDialog'; import globalMessages from '../../../i18n/global-messages'; import styles from './WalletSettings.scss'; import WalletRecoveryPhraseVerificationWidget from './WalletRecoveryPhraseVerificationWidget'; +import { momentLocales } from '../../../../../common/types/locales.types'; +import type { Locale } from '../../../../../common/types/locales.types'; export const messages = defineMessages({ assuranceLevelLabel: { @@ -88,7 +90,7 @@ type Props = { recoveryPhraseVerificationStatus: string, recoveryPhraseVerificationStatusType: string, wordCount: number, - locale: string, + locale: Locale, isSpendingPasswordSet: boolean, }; @@ -166,11 +168,7 @@ export default class WalletSettings extends Component { const { isFormBlocked } = this.state; // Set Japanese locale to moment. Default is en-US - if (locale === 'ja-JP') { - moment.locale('ja'); - } else { - moment.locale('en-us'); - } + moment.locale(momentLocales[locale]); if (isLegacy && isIncentivizedTestnet) { const deleteWalletBoxStyles = classNames([ diff --git a/source/renderer/app/components/widgets/NewsFeedIcon.js b/source/renderer/app/components/widgets/NewsFeedIcon.js index 2e00282597..0b66c33c86 100644 --- a/source/renderer/app/components/widgets/NewsFeedIcon.js +++ b/source/renderer/app/components/widgets/NewsFeedIcon.js @@ -8,15 +8,22 @@ import styles from './NewsFeedIcon.scss'; type Props = { onNewsFeedIconClick: Function, newsFeedIconClass?: string, - showDot: boolean, + hasNotification: boolean, + hasUpdate: boolean, }; export default class NewsFeedIcon extends Component { render() { - const { onNewsFeedIconClick, newsFeedIconClass, showDot } = this.props; + const { + onNewsFeedIconClick, + newsFeedIconClass, + hasNotification, + hasUpdate, + } = this.props; const componentClasses = classNames([ styles.component, - showDot ? styles.withDot : null, + hasNotification && !hasUpdate ? styles.notificationDot : null, + hasUpdate ? styles.updateDot : null, newsFeedIconClass, ]); return ( diff --git a/source/renderer/app/components/widgets/NewsFeedIcon.scss b/source/renderer/app/components/widgets/NewsFeedIcon.scss index ee61b26a65..52b81e1609 100644 --- a/source/renderer/app/components/widgets/NewsFeedIcon.scss +++ b/source/renderer/app/components/widgets/NewsFeedIcon.scss @@ -2,9 +2,11 @@ position: absolute; right: 29px; top: 20px; +} - &.withDot:after { - background: var(--theme-news-feed-icon-dot-background-color); +.dot { + &:after { + background: var(--theme-news-feed-icon-green-dot-background-color); border-radius: 12.5px; content: ''; display: block; @@ -17,6 +19,16 @@ } } +.notificationDot { + @extend .dot; + &:after { + background: var(--theme-news-feed-icon-red-dot-background-color); + } +} +.updateDot { + @extend .dot; +} + .icon { border-radius: 50%; cursor: pointer; diff --git a/source/renderer/app/components/widgets/ProgressBarLarge.js b/source/renderer/app/components/widgets/ProgressBarLarge.js new file mode 100644 index 0000000000..065ee3ec4d --- /dev/null +++ b/source/renderer/app/components/widgets/ProgressBarLarge.js @@ -0,0 +1,31 @@ +// @flow +import React, { Component } from 'react'; +import { observer } from 'mobx-react'; +import styles from './ProgressBarLarge.scss'; + +type Props = { + progress: number, + showProgressLabel?: boolean, +}; + +@observer +export default class ProgressBarLarge extends Component { + static defaultProps = { + progress: 0, + }; + + render() { + const { progress, showProgressLabel } = this.props; + return ( +
+
+
+ {showProgressLabel && ( +
{progress}%
+ )} +
+
+
+ ); + } +} diff --git a/source/renderer/app/components/widgets/ProgressBarLarge.scss b/source/renderer/app/components/widgets/ProgressBarLarge.scss new file mode 100644 index 0000000000..50453f6fd4 --- /dev/null +++ b/source/renderer/app/components/widgets/ProgressBarLarge.scss @@ -0,0 +1,36 @@ +.component { + height: 24px; +} + +.progressBarContainer { + background-color: var(--theme-progress-bar-large-background-color); + border-radius: 5px; + height: 100%; + position: relative; + width: 100%; +} + +.progress { + align-items: center; + background: repeating-linear-gradient( + -63deg, + var(--theme-progress-bar-large-progress-stripe1), + var(--theme-progress-bar-large-progress-stripe1) 10px, + var(--theme-progress-bar-large-progress-stripe2) 10px, + var(--theme-progress-bar-large-progress-stripe2) 20px + ); + border-radius: 5px; + display: flex; + height: 100%; + justify-content: flex-end; + transition: width 0.24s; +} + +.progressLabel { + transform: translateX(calc(100% + 8px)); +} + +.progressLabelWhite { + color: var(--theme-staking-progress-label-light); + transform: translateX(-8px); +} diff --git a/source/renderer/app/config/timingConfig.js b/source/renderer/app/config/timingConfig.js index a0f7c77957..a7c444ec70 100644 --- a/source/renderer/app/config/timingConfig.js +++ b/source/renderer/app/config/timingConfig.js @@ -12,7 +12,6 @@ export const NOTIFICATION_DEFAULT_DURATION = 10 * 1000; // 10 seconds / unit: mi export const ADDRESS_COPY_NOTIFICATION_SMALL_DURATION = 3; // unit: seconds export const DELETE_WALLET_COUNTDOWN = 10; // unit: seconds export const FORM_VALIDATION_DEBOUNCE_WAIT = 250; // unit: milliseconds -export const APP_UPDATE_POLL_INTERVAL = 5000; // unit: milliseconds export const ALLOWED_TIME_DIFFERENCE = 15 * 1000000; // 15 seconds | unit: microseconds export const NETWORK_STATUS_POLL_INTERVAL = 2000; // 2 seconds | unit: milliseconds export const NETWORK_CLOCK_POLL_INTERVAL = 1000; // 1 second | unit: milliseconds diff --git a/source/renderer/app/containers/Root.js b/source/renderer/app/containers/Root.js index 0c806482fb..24e7edc0c3 100644 --- a/source/renderer/app/containers/Root.js +++ b/source/renderer/app/containers/Root.js @@ -5,6 +5,7 @@ import WalletAddPage from './wallet/WalletAddPage'; import LoadingPage from './loading/LoadingPage'; import SplashNetworkPage from './splash/SplashNetworkPage'; import RedeemItnRewardsContainer from './staking/RedeemItnRewardsContainer'; +import AppUpdateContainer from './appUpdate/AppUpdateContainer'; import WalletImportFileDialog from '../components/wallet/wallet-import/WalletImportFileDialog'; import type { InjectedContainerProps } from '../types/injectedPropsType'; @@ -28,7 +29,7 @@ export default class Root extends Component { } = stores; const { isStakingPage, redeemStep } = staking; const { isProfilePage, isSettingsPage } = profile; - const { showManualUpdate } = appUpdate; + const { displayAppUpdateOverlay } = appUpdate; const { hasLoadedWallets, isHardwareWalletRoute } = wallets; const { isConnected, @@ -61,7 +62,11 @@ export default class Root extends Component { } if (redeemStep !== null) { - return ; + return ; + } + + if (!isNodeInStoppingSequence && displayAppUpdateOverlay) { + return ; } // Just render any page that doesn't require wallets to be loaded or node to be connected @@ -69,7 +74,7 @@ export default class Root extends Component { (isPageThatDoesntNeedWallets && !isNodeInStoppingSequence) || (isProfilePage && (isNotEnoughDiskSpace || !isNodeInStoppingSequence)) ) { - return <>{children}; + return children; } if ( @@ -77,7 +82,7 @@ export default class Root extends Component { !hasLoadedWallets || isNotEnoughDiskSpace || !isSystemTimeCorrect || - showManualUpdate + displayAppUpdateOverlay ) { return ; } @@ -86,6 +91,6 @@ export default class Root extends Component { return ; } - return <>{children}; + return children; } } diff --git a/source/renderer/app/containers/TopBarContainer.js b/source/renderer/app/containers/TopBarContainer.js index 518b5e0628..cf254b6b19 100644 --- a/source/renderer/app/containers/TopBarContainer.js +++ b/source/renderer/app/containers/TopBarContainer.js @@ -20,7 +20,14 @@ export default class TopBarContainer extends Component { render() { const { actions, stores } = this.props; - const { sidebar, app, networkStatus, wallets, newsFeed } = stores; + const { + sidebar, + app, + networkStatus, + wallets, + newsFeed, + appUpdate, + } = stores; const { isSynced, syncPercentage } = networkStatus; const { active, isWalletRoute, hasAnyWallets, hasRewardsWallets } = wallets; const { isShelleyActivated } = networkStatus; @@ -55,6 +62,8 @@ export default class TopBarContainer extends Component { }); const { unread } = newsFeed.newsFeedData; + const { displayAppUpdateNewsItem } = appUpdate; + const hasUnreadNews = unread.length > 0; return ( @@ -75,7 +84,8 @@ export default class TopBarContainer extends Component { /> ); diff --git a/source/renderer/app/containers/appUpdate/AppUpdateContainer.js b/source/renderer/app/containers/appUpdate/AppUpdateContainer.js new file mode 100644 index 0000000000..59545ecebd --- /dev/null +++ b/source/renderer/app/containers/appUpdate/AppUpdateContainer.js @@ -0,0 +1,58 @@ +// @flow +import React, { Component } from 'react'; +import { inject, observer } from 'mobx-react'; +import AppUpdateOverlay from '../../components/appUpdate/AppUpdateOverlay'; +import type { InjectedProps } from '../../types/injectedPropsType'; + +@inject('stores', 'actions') +@observer +export default class AppUpdateContainer extends Component { + static defaultProps = { actions: null, stores: null }; + + render() { + const { stores, actions } = this.props; + const { appUpdate } = stores; + const { environment, openExternalLink } = stores.app; + const { version, isLinux } = environment; + const { + downloadProgress, + isUpdateDownloaded, + availableUpdate, + downloadTimeLeft, + totalDownloaded, + totalDownloadSize, + availableUpdateVersion, + isAutomaticUpdateFailed, + isWaitingToQuitDaedalus, + installationProgress, + } = appUpdate; + const { + installUpdate, + closeAppUpdateOverlay, + postponeUpdate, + } = actions.appUpdate; + + if (!availableUpdate) return null; + + return ( + + ); + } +} diff --git a/source/renderer/app/containers/loading/LoadingPage.js b/source/renderer/app/containers/loading/LoadingPage.js index e72eea949c..1e3c6fe832 100644 --- a/source/renderer/app/containers/loading/LoadingPage.js +++ b/source/renderer/app/containers/loading/LoadingPage.js @@ -4,7 +4,6 @@ import { inject, observer } from 'mobx-react'; import CenteredLayout from '../../components/layout/CenteredLayout'; import NoDiskSpaceErrorPage from './NoDiskSpaceErrorPage'; import SystemTimeErrorPage from './SystemTimeErrorPage'; -import ManualUpdatePage from './ManualUpdatePage'; import SyncingConnectingPage from './SyncingConnectingPage'; import type { InjectedProps } from '../../types/injectedPropsType'; @@ -14,9 +13,7 @@ export default class LoadingPage extends Component { static defaultProps = { stores: null, actions: null }; get activeOverlay() { - const { showManualUpdate } = this.props.stores.appUpdate; if (this.isNotEnoughDiskSpace) return ; - if (showManualUpdate) return ; if (this.isSystemTimeError) return ; return null; } diff --git a/source/renderer/app/containers/loading/ManualUpdatePage.js b/source/renderer/app/containers/loading/ManualUpdatePage.js deleted file mode 100644 index 3f554b2002..0000000000 --- a/source/renderer/app/containers/loading/ManualUpdatePage.js +++ /dev/null @@ -1,27 +0,0 @@ -// @flow -import React, { Component } from 'react'; -import { observer, inject } from 'mobx-react'; -import type { InjectedStoresProps } from '../../types/injectedPropsType'; -import ManualUpdate from '../../components/loading/manual-update/ManualUpdate'; - -type Props = InjectedStoresProps; - -@inject('stores') -@observer -export default class ManualUpdatePage extends Component { - static defaultProps = { stores: null }; - - render() { - const { stores } = this.props; - const { availableAppVersion } = stores.appUpdate; - const { environment, openExternalLink } = stores.app; - const { version } = environment; - return ( - - ); - } -} diff --git a/source/renderer/app/containers/loading/SyncingConnectingPage.js b/source/renderer/app/containers/loading/SyncingConnectingPage.js index 23e11a3dd5..3bcaa029f6 100644 --- a/source/renderer/app/containers/loading/SyncingConnectingPage.js +++ b/source/renderer/app/containers/loading/SyncingConnectingPage.js @@ -14,7 +14,13 @@ export default class LoadingSyncingConnectingPage extends Component { render() { const { isIncentivizedTestnet, isFlight } = global; - const { stores } = this.props; + const { + newsFeed, + appUpdate, + networkStatus, + profile, + app, + } = this.props.stores; const { cardanoNodeState, isNodeResponding, @@ -31,16 +37,12 @@ export default class LoadingSyncingConnectingPage extends Component { isTlsCertInvalid, isVerifyingBlockchain, verificationProgress, - } = stores.networkStatus; - const { - isNewAppVersionAvailable, - isNewAppVersionLoading, - isNewAppVersionLoaded, - } = stores.appUpdate; - const { hasLoadedCurrentLocale, hasLoadedCurrentTheme } = stores.profile; + } = networkStatus; + const { displayAppUpdateNewsItem } = appUpdate; + const { hasLoadedCurrentLocale, hasLoadedCurrentTheme } = profile; const { toggleNewsFeed } = this.props.actions.app; - const { unread } = stores.newsFeed.newsFeedData; - const hasUnreadNews = unread.length > 0; + const { unread } = newsFeed.newsFeedData; + const hasNotification = unread.length > 0; return ( { isNodeStopped={isNodeStopped} isNotEnoughDiskSpace={isNotEnoughDiskSpace} isTlsCertInvalid={isTlsCertInvalid} - hasUnreadNews={hasUnreadNews} + hasNotification={hasNotification} + hasUpdate={displayAppUpdateNewsItem} hasLoadedCurrentLocale={hasLoadedCurrentLocale} hasLoadedCurrentTheme={hasLoadedCurrentTheme} isCheckingSystemTime={ @@ -65,17 +68,13 @@ export default class LoadingSyncingConnectingPage extends Component { isNodeResponding={isNodeResponding} isNodeSyncing={isNodeSyncing} isNodeTimeCorrect={isNodeTimeCorrect} - isNewAppVersionAvailable={isNewAppVersionAvailable} - isNewAppVersionLoading={isNewAppVersionLoading} - isNewAppVersionLoaded={isNewAppVersionLoaded} isIncentivizedTestnet={isIncentivizedTestnet} onIssueClick={this.handleIssueClick} onOpenExternalLink={this.handleOpenExternalLink} - onGetAvailableVersions={this.handleGetAvailableVersions} onStatusIconClick={this.openDaedalusDiagnosticsDialog} onDownloadLogs={this.handleDownloadLogs} onToggleNewsFeedIconClick={toggleNewsFeed.trigger} - disableDownloadLogs={stores.app.isDownloadNotificationVisible} + disableDownloadLogs={app.isDownloadNotificationVisible} showNewsFeedIcon={!isNodeStopping && !isNodeStopped} isVerifyingBlockchain={isVerifyingBlockchain} verificationProgress={verificationProgress} @@ -104,11 +103,6 @@ export default class LoadingSyncingConnectingPage extends Component { app.setIsDownloadingLogs.trigger(true); }; - handleGetAvailableVersions = () => { - const { appUpdate } = this.props.actions; - appUpdate.getLatestAvailableAppVersion.trigger(); - }; - openDaedalusDiagnosticsDialog = () => { const { actions: { app }, diff --git a/source/renderer/app/containers/news/NewsFeedContainer.js b/source/renderer/app/containers/news/NewsFeedContainer.js index a8dcb41ff5..319912b21c 100644 --- a/source/renderer/app/containers/news/NewsFeedContainer.js +++ b/source/renderer/app/containers/news/NewsFeedContainer.js @@ -17,8 +17,14 @@ export default class NewsFeedContainer extends Component { render() { const { stores, actions } = this.props; - const { app, profile } = stores; - const { newsFeedData, isLoadingNews, proceedNewsAction } = stores.newsFeed; + const { app, profile, appUpdate, newsFeed } = stores; + const { newsFeedData, isLoadingNews, proceedNewsAction } = newsFeed; + const { openAppUpdateOverlay } = actions.appUpdate; + const { + downloadProgress, + displayAppUpdateNewsItem, + isUpdatePostponed, + } = appUpdate; const { toggleNewsFeed } = actions.app; const { openExternalLink, newsFeedIsOpen } = app; const { currentDateFormat } = profile; @@ -29,12 +35,16 @@ export default class NewsFeedContainer extends Component { isNewsFeedOpen={newsFeedIsOpen} isLoadingNews={isLoadingNews} onClose={toggleNewsFeed.trigger} - onOpenAlert={stores.newsFeed.openAlert} + onOpenAlert={newsFeed.openAlert} onMarkNewsAsRead={this.handleMarkNewsAsRead} openWithoutTransition={stores.networkStatus.environment.isTest} onProceedNewsAction={proceedNewsAction} onOpenExternalLink={openExternalLink} currentDateFormat={currentDateFormat} + updateDownloadProgress={downloadProgress} + displayAppUpdateNewsItem={displayAppUpdateNewsItem} + isUpdatePostponed={isUpdatePostponed} + onOpenAppUpdate={openAppUpdateOverlay.trigger} /> ); } diff --git a/source/renderer/app/containers/news/NewsOverlayContainer.js b/source/renderer/app/containers/news/NewsOverlayContainer.js index ce839312bc..04897dbad3 100644 --- a/source/renderer/app/containers/news/NewsOverlayContainer.js +++ b/source/renderer/app/containers/news/NewsOverlayContainer.js @@ -11,8 +11,9 @@ export default class NewsOverlayContainer extends Component { static defaultProps = { actions: null, stores: null }; render() { - const { app, newsFeed, profile } = this.props.stores; - const { openExternalLink } = app; + const { stores } = this.props; + const { newsFeed, profile } = stores; + const { openExternalLink } = stores.app; const { closeOpenedAlert, markNewsAsRead, diff --git a/source/renderer/app/containers/notifications/AutomaticUpdateNotificationDialog.js b/source/renderer/app/containers/notifications/AutomaticUpdateNotificationDialog.js deleted file mode 100644 index 9532741c9b..0000000000 --- a/source/renderer/app/containers/notifications/AutomaticUpdateNotificationDialog.js +++ /dev/null @@ -1,28 +0,0 @@ -// @flow -import React, { Component } from 'react'; -import { observer, inject } from 'mobx-react'; -import AutomaticUpdateNotification from '../../components/notifications/AutomaticUpdateNotification'; -import type { InjectedProps } from '../../types/injectedPropsType'; - -@inject('stores', 'actions') -@observer -export default class AutomaticUpdateNotificationDialog extends Component { - static defaultProps = { actions: null, stores: null }; - - render() { - const { stores, actions } = this.props; - const { nextUpdateVersion } = stores.appUpdate; - const { environment } = stores.app; - const { version } = environment; - const { acceptAppUpdate, postponeAppUpdate } = actions.appUpdate; - - return ( - - ); - } -} diff --git a/source/renderer/app/domains/News.js b/source/renderer/app/domains/News.js index 18fa2dbe8f..8d2a16841b 100644 --- a/source/renderer/app/domains/News.js +++ b/source/renderer/app/domains/News.js @@ -18,11 +18,13 @@ export const NewsTypes: { ALERT: NewsType, ANNOUNCEMENT: NewsType, INFO: NewsType, + UPDATE: NewsType, } = { INCIDENT: 'incident', ALERT: 'alert', ANNOUNCEMENT: 'announcement', INFO: 'info', + UPDATE: 'software-update', }; export const IncidentColors: { @@ -73,6 +75,7 @@ class News { class NewsCollection { @observable all: Array = []; + @observable update: ?News = null; constructor(data: Array) { // Filter news by platform and versions @@ -83,6 +86,7 @@ class NewsCollection { '' ); const targetPlatforms = get(newsItem, ['target', 'platforms']); + return ( (!availableTargetVersionRange || (availableTargetVersionRange && @@ -90,6 +94,7 @@ class NewsCollection { includePrerelease: true, }))) && (platform === 'browser' || includes(targetPlatforms, platform)) && + newsItem.type !== NewsTypes.UPDATE && newsItem.id && newsItem.title && newsItem.content && @@ -97,11 +102,12 @@ class NewsCollection { newsItem.date ); }); - const orderedNews = orderBy(filteredNews, 'date', 'desc'); + const update = data.filter(item => item.type === NewsTypes.UPDATE)[0]; runInAction(() => { this.all = orderedNews; + this.update = update; }); } @@ -170,6 +176,10 @@ class NewsCollection { // Order read from newest to oldest return orderBy(read, 'date', 'asc'); } + + // @computed get update(): News | null { + // return this.all.filter(item => item.type === NewsTypes.UPDATE)[0]; + // } } export default { diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index 2e8f0b9bf8..cc53e1c15b 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -275,6 +275,221 @@ ], "path": "source/renderer/app/api/errors.json" }, + { + "descriptors": [ + { + "defaultMessage": "!!!Software update available!", + "description": "\"title\" for the App Update Overlay", + "end": { + "column": 3, + "line": 27 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.title", + "start": { + "column": 9, + "line": 23 + } + }, + { + "defaultMessage": "!!!You are currently running Daedalus version {currentAppVersion}.
Daedalus version {availableAppVersion} is now available to download.", + "description": "\"subtitle\" for the App Update Overlay", + "end": { + "column": 3, + "line": 33 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.subtitle", + "start": { + "column": 12, + "line": 28 + } + }, + { + "defaultMessage": "!!!I understand that I need to complete the installation before starting Daedalus.", + "description": "\"checkboxLabel\" for the App Update Overlay", + "end": { + "column": 3, + "line": 39 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.checkboxLabel", + "start": { + "column": 17, + "line": 34 + } + }, + { + "defaultMessage": "!!!Quit Daedalus and start the installation", + "description": "\"buttonLaunchInstallerLabel\" for the App Update Overlay", + "end": { + "column": 3, + "line": 44 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.button.launchInstaller.label", + "start": { + "column": 30, + "line": 40 + } + }, + { + "defaultMessage": "!!!Install the update and restart Daedalus", + "description": "\"buttonInstallUpdateLabel\" for the App Update Overlay", + "end": { + "column": 3, + "line": 49 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.button.installUpdate.label", + "start": { + "column": 28, + "line": 45 + } + }, + { + "defaultMessage": "!!!Postpone the update", + "description": "\"manualUpdateLinkLabel\" for the App Update Overlay", + "end": { + "column": 3, + "line": 54 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.postponeInstall.link.label", + "start": { + "column": 28, + "line": 50 + } + }, + { + "defaultMessage": "!!!Installing update...", + "description": "\"installingUpdateLabel\" for the App Update Overlay", + "end": { + "column": 3, + "line": 59 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.installingUpdate.link.label", + "start": { + "column": 25, + "line": 55 + } + }, + { + "defaultMessage": "!!!Download in progress", + "description": "\"downloadProgressLabel\" for the App Update Overlay", + "end": { + "column": 3, + "line": 64 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.downloadProgressLabel", + "start": { + "column": 25, + "line": 60 + } + }, + { + "defaultMessage": "!!!{downloadTimeLeft} left", + "description": "\"downloadTimeLeft\" for the App Update Overlay", + "end": { + "column": 3, + "line": 69 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.downloadTimeLeft", + "start": { + "column": 20, + "line": 65 + } + }, + { + "defaultMessage": "!!!({totalDownloaded} of {totalDownloadSize} downloaded)", + "description": "\"downloadProgressData\" for the App Update Overlay", + "end": { + "column": 3, + "line": 74 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.downloadProgressData", + "start": { + "column": 24, + "line": 70 + } + }, + { + "defaultMessage": "!!!We were unable to launch the update installer automatically.", + "description": "\"manualUpdateDescriptionError\" for the App Update Overlay", + "end": { + "column": 3, + "line": 80 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.manualUpdate.description.error", + "start": { + "column": 32, + "line": 75 + } + }, + { + "defaultMessage": "!!!We were unable to install the update.", + "description": "\"manualUpdateDescriptionErrorLinux\" for the App Update Overlay", + "end": { + "column": 3, + "line": 86 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.manualUpdate.description.errorLinux", + "start": { + "column": 37, + "line": 81 + } + }, + { + "defaultMessage": "!!!Please manually update Daedalus to its latest version.", + "description": "\"manualUpdateDescriptionAction\" for the App Update Overlay", + "end": { + "column": 3, + "line": 91 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.manualUpdate.description.action", + "start": { + "column": 33, + "line": 87 + } + }, + { + "defaultMessage": "!!!Follow instructions and manually update", + "description": "\"manualUpdateButtonLabel\" for the App Update Overlay", + "end": { + "column": 3, + "line": 96 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.manualUpdate.button.label", + "start": { + "column": 27, + "line": 92 + } + }, + { + "defaultMessage": "!!!https://daedaluswallet.io/en/download/", + "description": "\"manualUpdateButtonUrl\" for the App Update Overlay", + "end": { + "column": 3, + "line": 101 + }, + "file": "source/renderer/app/components/appUpdate/AppUpdateOverlay.js", + "id": "appUpdate.overlay.manualUpdate.button.url", + "start": { + "column": 25, + "line": 97 + } + } + ], + "path": "source/renderer/app/components/appUpdate/AppUpdateOverlay.json" + }, { "descriptors": [ { @@ -397,81 +612,6 @@ ], "path": "source/renderer/app/components/hardware-wallet/settings/ConnectHardwareWallet.json" }, - { - "descriptors": [ - { - "defaultMessage": "!!!Software update is available", - "description": "Title for \"Manual update\" overlay", - "end": { - "column": 3, - "line": 15 - }, - "file": "source/renderer/app/components/loading/manual-update/ManualUpdate.js", - "id": "manualUpdate.title", - "start": { - "column": 9, - "line": 11 - } - }, - { - "defaultMessage": "!!!You are experiencing network connection issues, and you are not running the latest Daedalus version. Automatic updates are unavailable while Daedalus is not connected to Cardano network.", - "description": "Description line 1 of \"Manual update\" overlay", - "end": { - "column": 3, - "line": 21 - }, - "file": "source/renderer/app/components/loading/manual-update/ManualUpdate.js", - "id": "manualUpdate.description1", - "start": { - "column": 20, - "line": 16 - } - }, - { - "defaultMessage": "!!!You are currently running {currentAppVersion} version of Daedalus, and {availableAppVersion} version is available. Please manually update to that version since it may resolve your connecting issues.", - "description": "Description line 2 of \"Manual update\" overlay", - "end": { - "column": 3, - "line": 27 - }, - "file": "source/renderer/app/components/loading/manual-update/ManualUpdate.js", - "id": "manualUpdate.description2", - "start": { - "column": 20, - "line": 22 - } - }, - { - "defaultMessage": "!!!Follow instructions and manually update", - "description": "Label for \"Follow instructions and manually update\" action button on \"Manual update\" overlay", - "end": { - "column": 3, - "line": 33 - }, - "file": "source/renderer/app/components/loading/manual-update/ManualUpdate.js", - "id": "manualUpdate.button.label", - "start": { - "column": 21, - "line": 28 - } - }, - { - "defaultMessage": "!!!https://iohk.zendesk.com/hc/en-us/articles/360023850634", - "description": "Follow instructions and manually update link on \"Manual update\" overlay", - "end": { - "column": 3, - "line": 40 - }, - "file": "source/renderer/app/components/loading/manual-update/ManualUpdate.js", - "id": "manualUpdate.button.url", - "start": { - "column": 25, - "line": 34 - } - } - ], - "path": "source/renderer/app/components/loading/manual-update/ManualUpdate.json" - }, { "descriptors": [ { @@ -1279,13 +1419,13 @@ "description": "Newsfeed is empty", "end": { "column": 3, - "line": 19 + "line": 20 }, "file": "source/renderer/app/components/news/NewsFeed.js", "id": "news.newsfeed.empty", "start": { "column": 17, - "line": 15 + "line": 16 } }, { @@ -1293,13 +1433,13 @@ "description": "Trying to fetch the newsfeed...", "end": { "column": 3, - "line": 24 + "line": 25 }, "file": "source/renderer/app/components/news/NewsFeed.js", "id": "news.newsfeed.noFetch", "start": { "column": 19, - "line": 20 + "line": 21 } }, { @@ -1307,107 +1447,18 @@ "description": "Newsfeed", "end": { "column": 3, - "line": 29 + "line": 30 }, "file": "source/renderer/app/components/news/NewsFeed.js", "id": "news.newsfeed.title", "start": { "column": 17, - "line": 25 + "line": 26 } } ], "path": "source/renderer/app/components/news/NewsFeed.json" }, - { - "descriptors": [ - { - "defaultMessage": "!!!Software update is available", - "description": "Title for \"Automatic update\" overlay", - "end": { - "column": 3, - "line": 15 - }, - "file": "source/renderer/app/components/notifications/AutomaticUpdateNotification.js", - "id": "automaticUpdate.title", - "start": { - "column": 9, - "line": 11 - } - }, - { - "defaultMessage": "!!!You are currently running Daedalus {currentAppVersion} and {nextAppVersion} is available.", - "description": "First description line of \"Automatic update\" overlay", - "end": { - "column": 3, - "line": 21 - }, - "file": "source/renderer/app/components/notifications/AutomaticUpdateNotification.js", - "id": "automaticUpdate.description1", - "start": { - "column": 20, - "line": 16 - } - }, - { - "defaultMessage": "!!!Would you like to install the update? If choose to postpone, the update will be installed automatically on the next Daedalus launch.", - "description": "Second description line of \"Automatic update\" overlay", - "end": { - "column": 3, - "line": 27 - }, - "file": "source/renderer/app/components/notifications/AutomaticUpdateNotification.js", - "id": "automaticUpdate.description2", - "start": { - "column": 20, - "line": 22 - } - }, - { - "defaultMessage": "!!!Restart Daedalus and Update", - "description": "Label for \"Restart Daedalus and Update\" action button on \"Automatic update\" overlay", - "end": { - "column": 3, - "line": 33 - }, - "file": "source/renderer/app/components/notifications/AutomaticUpdateNotification.js", - "id": "automaticUpdate.accept.button.label", - "start": { - "column": 21, - "line": 28 - } - }, - { - "defaultMessage": "!!!Postpone until Daedalus restart", - "description": "Label for \"Postpone\" action button on \"Automatic update\" overlay", - "end": { - "column": 3, - "line": 39 - }, - "file": "source/renderer/app/components/notifications/AutomaticUpdateNotification.js", - "id": "automaticUpdate.postpone.button.label", - "start": { - "column": 23, - "line": 34 - } - }, - { - "defaultMessage": "!!!a newer version", - "description": "Label for \"newer version\" on \"Automatic update\" overlay", - "end": { - "column": 3, - "line": 44 - }, - "file": "source/renderer/app/components/notifications/AutomaticUpdateNotification.js", - "id": "automaticUpdate.newerVersion.label", - "start": { - "column": 21, - "line": 40 - } - } - ], - "path": "source/renderer/app/components/notifications/AutomaticUpdateNotification.json" - }, { "descriptors": [ { @@ -8859,13 +8910,13 @@ "description": "Label for the \"Transaction assurance security level\" dropdown.", "end": { "column": 3, - "line": 25 + "line": 27 }, "file": "source/renderer/app/components/wallet/settings/WalletSettings.js", "id": "wallet.settings.assurance", "start": { "column": 23, - "line": 20 + "line": 22 } }, { @@ -8873,13 +8924,13 @@ "description": "Delete wallet header on the wallet settings page.", "end": { "column": 3, - "line": 30 + "line": 32 }, "file": "source/renderer/app/components/wallet/settings/WalletSettings.js", "id": "wallet.settings.deleteWallet.header", "start": { "column": 22, - "line": 26 + "line": 28 } }, { @@ -8887,13 +8938,13 @@ "description": "Delete wallet warning explaining the consequences.", "end": { "column": 3, - "line": 36 + "line": 38 }, "file": "source/renderer/app/components/wallet/settings/WalletSettings.js", "id": "wallet.settings.deleteWallet.warning1", "start": { "column": 24, - "line": 31 + "line": 33 } }, { @@ -8901,13 +8952,13 @@ "description": "Delete wallet warning explaining the consequences.", "end": { "column": 3, - "line": 42 + "line": 44 }, "file": "source/renderer/app/components/wallet/settings/WalletSettings.js", "id": "wallet.settings.deleteWallet.warning2", "start": { "column": 24, - "line": 37 + "line": 39 } }, { @@ -8915,13 +8966,13 @@ "description": "Label for the \"Name\" text input on the wallet settings page.", "end": { "column": 3, - "line": 47 + "line": 49 }, "file": "source/renderer/app/components/wallet/settings/WalletSettings.js", "id": "wallet.settings.name.label", "start": { "column": 8, - "line": 43 + "line": 45 } }, { @@ -8929,13 +8980,13 @@ "description": "Label for the \"Password\" field.", "end": { "column": 3, - "line": 52 + "line": 54 }, "file": "source/renderer/app/components/wallet/settings/WalletSettings.js", "id": "wallet.settings.password", "start": { "column": 17, - "line": 48 + "line": 50 } }, { @@ -8943,13 +8994,13 @@ "description": "Last updated X time ago message.", "end": { "column": 3, - "line": 57 + "line": 59 }, "file": "source/renderer/app/components/wallet/settings/WalletSettings.js", "id": "wallet.settings.passwordLastUpdated", "start": { "column": 23, - "line": 53 + "line": 55 } }, { @@ -8957,13 +9008,13 @@ "description": "You still don't have password set message.", "end": { "column": 3, - "line": 62 + "line": 64 }, "file": "source/renderer/app/components/wallet/settings/WalletSettings.js", "id": "wallet.settings.passwordNotSet", "start": { "column": 18, - "line": 58 + "line": 60 } } ], @@ -13950,20 +14001,6 @@ "column": 8, "line": 69 } - }, - { - "defaultMessage": "!!!Follow instructions and manually update", - "description": "Follow instructions and manually update description.", - "end": { - "column": 3, - "line": 78 - }, - "file": "storybook/stories/common/Widgets.stories.js", - "id": "manualUpdate.button.label", - "start": { - "column": 22, - "line": 74 - } } ], "path": "storybook/stories/common/Widgets.stories.json" diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json index 8d80b1e99d..60adf949cb 100755 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -17,12 +17,21 @@ "api.errors.invalidAddress": "Please enter a valid address.", "api.errors.nothingToMigrate": "Funds cannot be transferred from this wallet because it contains some unspent transaction outputs (UTXOs), with amounts of ada that are too small to be migrated.", "api.errors.utxoTooSmall": "Invalid transaction.", - "automaticUpdate.accept.button.label": "Restart Daedalus and Update", - "automaticUpdate.description1": "You are currently running Daedalus {currentAppVersion} and {nextUpdateVersion} is available.", - "automaticUpdate.description2": "Would you like to install the update? If choose to postpone, the update will be installed automatically on the next Daedalus launch.", - "automaticUpdate.newerVersion.label": "a newer version", - "automaticUpdate.postpone.button.label": "Postpone until Daedalus restart", - "automaticUpdate.title": "Software update is available", + "appUpdate.overlay.button.installUpdate.label": "Install the update and restart Daedalus", + "appUpdate.overlay.button.launchInstaller.label": "Quit Daedalus and start the installation", + "appUpdate.overlay.checkboxLabel": "I understand that I need to complete the installation before starting Daedalus.", + "appUpdate.overlay.downloadProgressData": "({totalDownloaded} of {totalDownloadSize} downloaded)", + "appUpdate.overlay.downloadProgressLabel": "Download in progress", + "appUpdate.overlay.downloadTimeLeft": "{downloadTimeLeft} left", + "appUpdate.overlay.installingUpdate.link.label": "Installing update...", + "appUpdate.overlay.manualUpdate.button.label": "Follow instructions and manually update", + "appUpdate.overlay.manualUpdate.button.url": "https://daedaluswallet.io/en/download/", + "appUpdate.overlay.manualUpdate.description.action": "Please manually update Daedalus to its latest version.", + "appUpdate.overlay.manualUpdate.description.error": "We were unable to launch the update installer automatically.", + "appUpdate.overlay.manualUpdate.description.errorLinux": "We were unable to launch the update installer automatically.", + "appUpdate.overlay.postponeInstall.link.label": "Postpone the update", + "appUpdate.overlay.subtitle": "You are currently running Daedalus version {currentAppVersion}.
Daedalus version {availableAppVersion} is now available to download.", + "appUpdate.overlay.title": "Software update available!", "backToTopButton.label": "Back to top", "cancel.transaction.confirmation.dialog.button.backLabel": "No, leave transaction pending", "cancel.transaction.confirmation.dialog.button.confirmLabel": "Yes, cancel transaction", @@ -176,11 +185,6 @@ "loading.screen.updatedCardanoMessage": "Cardano node updated", "loading.screen.updatingCardanoMessage": "Updating Cardano node", "loading.screen.verifyingBlockchainMessage": "Verifying the blockchain ({verificationProgress}% complete) ", - "manualUpdate.button.label": "Follow instructions and manually update", - "manualUpdate.button.url": "https://iohk.zendesk.com/hc/en-us/articles/360023850634", - "manualUpdate.description1": "You are experiencing network connection issues, and you are not running the latest Daedalus version. Automatic updates are unavailable while Daedalus is not connected to Cardano network.", - "manualUpdate.description2": "You are currently running {currentAppVersion} version of Daedalus, and {availableAppVersion} version is available. Please manually update to that version since it may resolve your connecting issues.", - "manualUpdate.title": "Software update is available", "news.newsfeed.empty": "Newsfeed is empty", "news.newsfeed.noFetch": "Trying to fetch the newsfeed...", "news.newsfeed.title": "Newsfeed", diff --git a/source/renderer/app/i18n/locales/ja-JP.json b/source/renderer/app/i18n/locales/ja-JP.json index f16ba14fa7..6c70d73572 100755 --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -17,12 +17,21 @@ "api.errors.invalidAddress": "有効なアドレスを入力してください。", "api.errors.nothingToMigrate": "このウォレットに保有されている未使用トランザクションアウトプット(UTXO)の一部に、移行するために十分なADAが入っていないため、このウォレットから資金を移すことはできません。", "api.errors.utxoTooSmall": "無効なトランザクションです。", - "automaticUpdate.accept.button.label": "Daedalusを再起動して更新する", - "automaticUpdate.description1": "現在実行中のDaedalus {currentAppVersion}{nextUpdateVersion}に更新できます。", - "automaticUpdate.description2": "今すぐ最新版をインストールしますか。更新を保留すると、Daedalusを次回起動する時に自動的にインストールされます。", - "automaticUpdate.newerVersion.label": "最新バージョン", - "automaticUpdate.postpone.button.label": "Daedalusの次回起動時まで保留する", - "automaticUpdate.title": "ソフトウェアが更新できます", + "appUpdate.overlay.button.installUpdate.label": "更新をインストールしてDaedalusを再起動する", + "appUpdate.overlay.button.launchInstaller.label": "Daedalusを終了してインストールを開始する", + "appUpdate.overlay.checkboxLabel": "Daedalusを起動する前に、インストールを完了する必要があることを理解しました。", + "appUpdate.overlay.downloadProgressData": "({totalDownloaded}/{totalDownloadSize}をダウンロード済み)", + "appUpdate.overlay.downloadProgressLabel": "ダウンロードしています", + "appUpdate.overlay.downloadTimeLeft": "残り{downloadTimeLeft}", + "appUpdate.overlay.installingUpdate.link.label": "更新をインストールしています…", + "appUpdate.overlay.manualUpdate.button.label": "指示に従い手動で更新してください", + "appUpdate.overlay.manualUpdate.button.url": "https://daedaluswallet.io/ja/download/", + "appUpdate.overlay.manualUpdate.description.action": "手動でDaedalusを最新バージョンに更新してください。", + "appUpdate.overlay.manualUpdate.description.error": "自動で更新インストーラーを起動できませんでした。", + "appUpdate.overlay.manualUpdate.description.errorLinux": "更新をインストールできませんでした。", + "appUpdate.overlay.postponeInstall.link.label": "後で更新する", + "appUpdate.overlay.subtitle": "このDaedalusはバージョン{currentAppVersion}です。
現在Daedalus {availableAppVersion}がダウンロード可能です", + "appUpdate.overlay.title": "ソフトウェアが更新できます!", "backToTopButton.label": "トップに戻る", "cancel.transaction.confirmation.dialog.button.backLabel": "いいえ、トランザクションを保留にしておきます", "cancel.transaction.confirmation.dialog.button.confirmLabel": "はい、トランザクションをキャンセルします", @@ -176,11 +185,6 @@ "loading.screen.updatedCardanoMessage": "Cardanoノードが更新されました", "loading.screen.updatingCardanoMessage": "Cardanoノードを更新しています", "loading.screen.verifyingBlockchainMessage": "ブロックチェーンを検証しています({verificationProgress}%完了)", - "manualUpdate.button.label": "指示に従って手動で更新してください", - "manualUpdate.button.url": "https://iohk.zendesk.com/hc/ja/articles/360023850634", - "manualUpdate.description1": "ネットワークへの接続に不具合があり、Daedalusウォレットの最新バージョンが実行されていません。DaedalusがCardanoネットワークに接続されていない間、自動更新は実行できません。", - "manualUpdate.description2": "ご使用のDaedalusはバージョン{currentAppVersion}です。現在{availableAppVersion}をご利用いただけます。手動で最新版に更新すると、接続の不具合を解消できる場合があります。", - "manualUpdate.title": "ソフトウェアが更新できます", "news.newsfeed.empty": "ニュースフィードは空です", "news.newsfeed.noFetch": "ニュースフィードを読み込んでいます…", "news.newsfeed.title": "ニュースフィード", diff --git a/source/renderer/app/ipc/downloadManagerChannel.js b/source/renderer/app/ipc/downloadManagerChannel.js index a7be0a883d..1630054c4d 100644 --- a/source/renderer/app/ipc/downloadManagerChannel.js +++ b/source/renderer/app/ipc/downloadManagerChannel.js @@ -1,19 +1,28 @@ // @flow import { REQUEST_DOWNLOAD, + RESUME_DOWNLOAD, + DELETE_DOWNLOADED_FILE, GET_DOWNLOAD_LOCAL_DATA, GET_DOWNLOADS_LOCAL_DATA, - RESUME_DOWNLOAD, + CLEAR_DOWNLOAD_LOCAL_DATA, + CHECK_FILE_EXISTS, } from '../../../common/ipc/api'; import type { DownloadRendererRequest, DownloadMainResponse, + ResumeDownloadRendererRequest, + ResumeDownloadMainResponse, + DeleteDownloadedFileRendererRequest, + DeleteDownloadedFileMainResponse, DownloadLocalDataRendererRequest, DownloadLocalDataMainResponse, DownloadsLocalDataRendererRequest, DownloadsLocalDataMainResponse, - ResumeDownloadRendererRequest, - ResumeDownloadMainResponse, + ClearDownloadLocalDataRendererRequest, + ClearDownloadLocalDataMainResponse, + CheckFileExistsRendererRequest, + CheckFileExistsMainResponse, } from '../../../common/ipc/api'; import { RendererIpcChannel } from './lib/RendererIpcChannel'; @@ -22,21 +31,36 @@ RendererIpcChannel< DownloadMainResponse, DownloadRendererRequest > = new RendererIpcChannel(REQUEST_DOWNLOAD); +export const requestResumeDownloadChannel: // IpcChannel +RendererIpcChannel< + ResumeDownloadMainResponse, + ResumeDownloadRendererRequest +> = new RendererIpcChannel(RESUME_DOWNLOAD); + +export const deleteDownloadedFile: // IpcChannel +RendererIpcChannel< + DeleteDownloadedFileMainResponse, + DeleteDownloadedFileRendererRequest +> = new RendererIpcChannel(DELETE_DOWNLOADED_FILE); export const getDownloadLocalDataChannel: // IpcChannel RendererIpcChannel< DownloadLocalDataMainResponse, DownloadLocalDataRendererRequest > = new RendererIpcChannel(GET_DOWNLOAD_LOCAL_DATA); - export const getDownloadsLocalDataChannel: // IpcChannel RendererIpcChannel< DownloadsLocalDataMainResponse, DownloadsLocalDataRendererRequest > = new RendererIpcChannel(GET_DOWNLOADS_LOCAL_DATA); +export const clearDownloadLocalDataChannel: // IpcChannel +RendererIpcChannel< + ClearDownloadLocalDataMainResponse, + ClearDownloadLocalDataRendererRequest +> = new RendererIpcChannel(CLEAR_DOWNLOAD_LOCAL_DATA); -export const requestResumeDownloadChannel: // IpcChannel +export const checkFileExistsChannel: // IpcChannel RendererIpcChannel< - ResumeDownloadMainResponse, - ResumeDownloadRendererRequest -> = new RendererIpcChannel(RESUME_DOWNLOAD); + CheckFileExistsMainResponse, + CheckFileExistsRendererRequest +> = new RendererIpcChannel(CHECK_FILE_EXISTS); diff --git a/source/renderer/app/ipc/manageAppUpdateChannel.js b/source/renderer/app/ipc/manageAppUpdateChannel.js new file mode 100644 index 0000000000..37faf7040f --- /dev/null +++ b/source/renderer/app/ipc/manageAppUpdateChannel.js @@ -0,0 +1,13 @@ +// @flow +import { MANAGE_APP_UPDATE } from '../../../common/ipc/api'; +import type { + ManageAppUpdateRendererRequest, + ManageAppUpdateMainResponse, +} from '../../../common/ipc/api'; +import { RendererIpcChannel } from './lib/RendererIpcChannel'; + +export const manageAppUpdateChannel: // IpcChannel +RendererIpcChannel< + ManageAppUpdateMainResponse, + ManageAppUpdateRendererRequest +> = new RendererIpcChannel(MANAGE_APP_UPDATE); diff --git a/source/renderer/app/stores/AppUpdateStore.js b/source/renderer/app/stores/AppUpdateStore.js index 80903bf878..e349f84b60 100644 --- a/source/renderer/app/stores/AppUpdateStore.js +++ b/source/renderer/app/stores/AppUpdateStore.js @@ -1,98 +1,290 @@ // @flow import { action, computed, observable, runInAction } from 'mobx'; +import { get } from 'lodash'; +import semver from 'semver'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; -import type { AppInfo, GetLatestAppVersionResponse } from '../api/nodes/types'; -import { APP_UPDATE_POLL_INTERVAL } from '../config/timingConfig'; -import { rebuildApplicationMenu } from '../ipc/rebuild-application-menu'; +import NewsDomains from '../domains/News'; +import { logger } from '../utils/logging'; import { requestDownloadChannel, - getDownloadLocalDataChannel, requestResumeDownloadChannel, + deleteDownloadedFile, + getDownloadLocalDataChannel, + clearDownloadLocalDataChannel, + checkFileExistsChannel, } from '../ipc/downloadManagerChannel'; -import type { DownloadMainResponse } from '../../../common/ipc/api'; -import { DOWNLOAD_EVENT_TYPES } from '../../../common/config/downloadManagerConfig'; - -const APP_UPDATE_DOWNLOAD_ID = 'appUpdate'; +import { manageAppUpdateChannel } from '../ipc/manageAppUpdateChannel'; +import type { + DownloadMainResponse, + DownloadLocalDataMainResponse, + CheckFileExistsMainResponse, + ManageAppUpdateMainResponse, +} from '../../../common/ipc/api'; +import { + APP_UPDATE_DOWNLOAD_ID, + UPDATE_INSTALLATION_STATUSES as statuses, +} from '../../../common/config/appUpdateConfig'; +import { formattedDownloadData } from '../utils/formatters.js'; +import { + DOWNLOAD_EVENT_TYPES, + DOWNLOAD_STATES, +} from '../../../common/config/downloadManagerConfig'; +import type { SoftwareUpdateInfo } from '../api/news/types'; +import type { + DownloadInfo, + DownloadData, +} from '../../../common/types/downloadManager.types'; +import type { FormattedDownloadData } from '../utils/formatters.js'; -const { isIncentivizedTestnet, isShelleyTestnet, isFlight } = global; +const { version: currentVersion, platform } = global.environment; +const { News } = NewsDomains; export default class AppUpdateStore extends Store { - @observable isUpdateAvailable = false; - @observable isDownloadingUpdate = false; - @observable isUpdatePostponed = false; - @observable isUpdateInstalled = false; - @observable hasPendingDownload = false; + @observable availableUpdate: ?News = null; + @observable availableUpdateVersion: string = ''; + @observable isUpdateDownloading: boolean = false; + @observable isUpdateDownloaded: boolean = false; + @observable isUpdateProgressOpen: boolean = false; + @observable isAutomaticUpdateFailed: boolean = false; + @observable isUpdatePostponed: boolean = false; + @observable isWaitingToQuitDaedalus: boolean = false; + @observable installationProgress: number = 0; + + @observable downloadInfo: ?DownloadInfo = null; + @observable downloadData: ?DownloadData = null; @observable availableAppVersion: ?string = null; - @observable isNewAppVersionAvailable: boolean = false; - @observable nextUpdateVersion: ?string = null; - @observable applicationVersion: ?number = null; - @observable downloadProgress: ?number = null; - // @observable updateFileUrl: ?string = null; - @observable updateFileUrl: ?string = - 'https://update-cardano-mainnet.iohk.io/daedalus-1.1.0-mainnet-12849.pkg'; - - // REQUESTS - @observable nextUpdateRequest: Request = new Request( - this.api.ada.nextUpdate - ); - @observable postponeUpdateRequest: Request> = new Request( - this.api.ada.postponeUpdate - ); - @observable applyUpdateRequest: Request> = new Request( - this.api.ada.applyUpdate - ); - @observable - getLatestAppVersionRequest: Request = new Request( - this.api.ada.getLatestAppVersion - ); - - nextUpdateInterval: ?IntervalID = null; + + @observable getAppAutomaticUpdateFailedRequest: Request< + Promise + > = new Request(this.api.localStorage.getAppAutomaticUpdateFailed); + @observable setAppAutomaticUpdateFailedRequest: Request< + Promise + > = new Request(this.api.localStorage.setAppAutomaticUpdateFailed); + @observable unsetAppAutomaticUpdateFailedRequest: Request< + Promise + > = new Request(this.api.localStorage.unsetAppAutomaticUpdateFailed); + + @observable getAppUpdateCompletedRequest: Request< + Promise + > = new Request(this.api.localStorage.getAppUpdateCompleted); + @observable setAppUpdateCompletedRequest: Request< + Promise + > = new Request(this.api.localStorage.setAppUpdateCompleted); + @observable unsetAppUpdateCompletedRequest: Request< + Promise + > = new Request(this.api.localStorage.unsetAppUpdateCompleted); setup() { const actions = this.actions.appUpdate; - actions.acceptAppUpdate.listen(this._acceptAppUpdate); - actions.postponeAppUpdate.listen(this._postponeAppUpdate); - actions.getLatestAvailableAppVersion.listen( - this._getLatestAvailableAppVersion - ); + actions.installUpdate.listen(this._installUpdate); + actions.openAppUpdateOverlay.listen(this._openAppUpdateOverlay); + actions.closeAppUpdateOverlay.listen(this._closeAppUpdateOverlay); + actions.postponeUpdate.listen(this._postponeUpdate); + requestDownloadChannel.onReceive(this._manageUpdateResponse); + manageAppUpdateChannel.onReceive(this._manageQuitAndInstallResponse); - if (!isFlight && !isIncentivizedTestnet && !isShelleyTestnet) { - this.nextUpdateInterval = setInterval( - this.refreshNextUpdate, - APP_UPDATE_POLL_INTERVAL - ); - } + // ============== MOBX REACTIONS ============== + this.registerReactions([this._watchForNewsfeedUpdates]); + } + + // ================= REACTIONS ================== + + _watchForNewsfeedUpdates = () => { + const { update } = this.stores.newsFeed.newsFeedData; + if (update) this._checkNewAppUpdate(update); + }; + + // ==================== PUBLIC ================== + + @computed get displayAppUpdateOverlay(): boolean { + return ( + !!this.availableUpdate && + !this.isUpdatePostponed && + (this.isUpdateProgressOpen || + this.isUpdateDownloaded || + this.isAutomaticUpdateFailed) + ); + } + @computed get displayAppUpdateNewsItem(): boolean { + return this.isUpdateDownloading || this.isUpdatePostponed; + } + + @computed get formattedDownloadData(): FormattedDownloadData { + return formattedDownloadData( + this.downloadData, + this.stores.profile.currentLocale + ); + } + + @computed get downloadTimeLeft(): string { + return this.formattedDownloadData.timeLeft; + } + + @computed get totalDownloaded(): string { + return this.formattedDownloadData.downloaded; + } + + @computed get totalDownloadSize(): string { + return this.formattedDownloadData.total; + } + + @computed get downloadProgress(): number { + return this.formattedDownloadData.progress; + } + + @computed get showManualUpdate(): boolean { + return this.isAutomaticUpdateFailed; + } + + @computed get isUpdateAvailable(): boolean { + return this.availableUpdate !== null; } - _managePendingUpdate = async () => { - const downloadLocalData = await getDownloadLocalDataChannel.request({ + getUpdateInfo(update: News): SoftwareUpdateInfo { + const softwareUpdate = get(update, 'softwareUpdate', {}); + const { version, hash, url } = softwareUpdate[platform] || {}; + return { version, hash, url }; + } + + isUpdateInstalled = (update: News) => { + const { version: updateVersion } = this.getUpdateInfo(update); + return !semver.lt(currentVersion, updateVersion); + }; + + // =================== PRIVATE ================== + + _checkNewAppUpdate = async (update: News) => { + const { version } = this.getUpdateInfo(update); + const appUpdateCompleted = await this.getAppUpdateCompletedRequest.execute(); + + /* + * The update was already installed and the installer was already deleted. + * We can't simply compare with the `package.json` version + * otherwise we would trigger the localdata cleaning on every app load + */ + if (appUpdateCompleted === version) return false; + + // Was the update already installed? + if (this.isUpdateInstalled(update)) { + // Sets the `appUpdateCompleted` flag to prevent this whole process every app load + await this.setAppUpdateCompletedRequest.execute(version); + await this.unsetAppAutomaticUpdateFailedRequest.execute(); + await this._removeUpdateFile(); + await this._removeLocalDataInfo(); + return false; + } + + // The Update is valid and needs to be downloaded/installed + runInAction(() => { + this.availableUpdate = update; + this.availableUpdateVersion = version; + }); + + // Cancels if the update download is already in progress + if (this.isUpdateDownloading) return false; + + // Is there an 'Automatic Update Failed' flag? + const isAutomaticUpdateFailed = await this.getAppAutomaticUpdateFailedRequest.execute(); + if (isAutomaticUpdateFailed) { + runInAction(() => { + this.isAutomaticUpdateFailed = true; + }); + return false; + } + + // Is there a pending / resumable download? + const downloadLocalData = await this._getUpdateDownloadLocalData(); + const { info, data } = downloadLocalData; + if (info && data) { + // The user reopened Daedalus without installing the update + if (data.state === DOWNLOAD_STATES.FINISHED && data.progress === 100) { + // Does the file still exist? + const installerFileStillExists = await this._checkFileExists(); + if (!installerFileStillExists) { + logger.error( + 'AppUpdateStore:_setAppAutomaticUpdateFailed: Failed to find the installer file' + ); + this._setAppAutomaticUpdateFailed(); + return false; + } + + runInAction(() => { + this.downloadInfo = info; + this.downloadData = data; + this.isUpdateDownloaded = true; + }); + return false; + } + + // Resumes the update download + this._requestResumeUpdateDownload(); + return false; + } + + await this._removeLocalDataInfo(); + return this._requestUpdateDownload(update); + }; + + _removeLocalDataInfo = async () => { + clearDownloadLocalDataChannel.request({ + id: APP_UPDATE_DOWNLOAD_ID, + }); + }; + + _removeUpdateFile = () => { + deleteDownloadedFile.request({ id: APP_UPDATE_DOWNLOAD_ID, }); - // eslint-disable-next-line - console.log('downloadLocalData', downloadLocalData); - return downloadLocalData; }; + _getUpdateDownloadLocalData = async (): Promise => + getDownloadLocalDataChannel.request({ + id: APP_UPDATE_DOWNLOAD_ID, + }); + + _checkFileExists = async (): Promise => + checkFileExistsChannel.request({ + id: APP_UPDATE_DOWNLOAD_ID, + }); + _manageUpdateResponse = ({ eventType, - /* data, */ - progress, + info, + data, + error, }: DownloadMainResponse) => { - if (eventType === 'progress') { - // eslint-disable-next-line - console.log( - '%c Download progress: %s%', - 'color: darkOrange', - parseInt(progress.progress, 10) - ); - } runInAction('updates the download information', () => { + if (eventType === DOWNLOAD_EVENT_TYPES.PAUSE) { + this.availableUpdate = null; + this.isUpdateDownloaded = true; + } + if (eventType === DOWNLOAD_EVENT_TYPES.PROGRESS) { + this.downloadInfo = info; + this.downloadData = data; + } if (eventType === DOWNLOAD_EVENT_TYPES.END) { - this.isDownloadingUpdate = false; + this.isUpdateDownloaded = true; + this.actions.app.closeNewsFeed.trigger(); + } + if (eventType === DOWNLOAD_EVENT_TYPES.ERROR) { + logger.error( + 'AppUpdateStore:_setAppAutomaticUpdateFailed: Received an error event from the main process', + { + error, + } + ); + this._setAppAutomaticUpdateFailed(); + } + if ( + eventType === DOWNLOAD_EVENT_TYPES.END || + eventType === DOWNLOAD_EVENT_TYPES.PAUSE || + eventType === DOWNLOAD_EVENT_TYPES.ERROR + ) { + this.isUpdateDownloading = false; } else { - this.isDownloadingUpdate = true; + this.isUpdateDownloading = true; } }); return Promise.resolve({ fileUrl: '' }); @@ -101,149 +293,96 @@ export default class AppUpdateStore extends Store { _requestResumeUpdateDownload = async () => { await requestResumeDownloadChannel.request({ id: APP_UPDATE_DOWNLOAD_ID, + options: { + progressIsThrottled: false, + persistLocalData: true, + }, }); }; - _requestUpdateDownload = async () => { - if (!this.updateFileUrl) return; - await requestDownloadChannel.request({ + _requestUpdateDownload = (update: News) => { + const { url: fileUrl } = this.getUpdateInfo(update); + if (!fileUrl) return null; + return requestDownloadChannel.request({ id: APP_UPDATE_DOWNLOAD_ID, - fileUrl: this.updateFileUrl, + fileUrl, + options: { + progressIsThrottled: false, + persistLocalData: true, + }, }); }; - refreshNextUpdate = async () => { - if (this.stores.networkStatus.isSynced) { - await this.nextUpdateRequest.execute(); - const { result } = this.nextUpdateRequest; - // If nextUpdate is available, fetch additional Daedalus info - if (result) { - await this._getLatestAvailableAppVersion(); - this._activateAutomaticUpdate(result.version); - } + _installUpdate = async () => { + if ( + !this.availableUpdate || + this.isUpdateDownloading || + !this.isUpdateDownloaded || + !this.downloadInfo + ) { + logger.error( + 'AppUpdateStore:_setAppAutomaticUpdateFailed: Unable to install the update', + { + '!this.availableUpdate': !this.availableUpdate, + 'this.isUpdateDownloading': this.isUpdateDownloading, + '!this.isUpdateDownloaded': !this.isUpdateDownloaded, + '!this.downloadInfo': !this.downloadInfo, + } + ); + // this._setAppAutomaticUpdateFailed(); + return false; } + runInAction(() => { + this.isWaitingToQuitDaedalus = true; + }); + if (!this.availableUpdate) return false; + const { destinationPath, originalFilename } = this.downloadInfo || {}; + const { hash } = this.getUpdateInfo(this.availableUpdate); + const filePath = `${destinationPath}/${originalFilename}`; + return manageAppUpdateChannel.request({ + filePath, + hash, + }); }; - @action _activateAutomaticUpdate = async (nextUpdateVersion: ?number) => { - if ( - nextUpdateVersion && - !this.isUpdateAvailable && - !this.isUpdatePostponed && - !this.isUpdateInstalled - ) { - this.isUpdateAvailable = true; - // If next update version matches applicationVersion (fetched from latestAppVersion json) - // then set next update version to latest availableAppVersion - this.nextUpdateVersion = - nextUpdateVersion === this.applicationVersion - ? this.availableAppVersion - : null; - - // Close all active dialogs - this.stores.app._closeActiveDialog(); - this.actions.app.closeAboutDialog.trigger(); - - // Rebuild app menu - await rebuildApplicationMenu.send({ - isUpdateAvailable: this.isUpdateAvailable, + _manageQuitAndInstallResponse = ({ + status, + data, + }: ManageAppUpdateMainResponse) => { + const { message, progress, error } = data; + if (status === statuses.ERROR) { + logger.error(message || '', { error }); + runInAction(() => { + this.isWaitingToQuitDaedalus = false; }); + this._setAppAutomaticUpdateFailed(); + } else if (status === statuses.PROGRESS) { + if (progress) { + runInAction(() => { + this.installationProgress = progress; + }); + } } + return Promise.resolve({ filePath: '', hash: '' }); }; - @action _postponeAppUpdate = async () => { - this.postponeUpdateRequest.execute(); - this.isUpdatePostponed = true; - this.isUpdateAvailable = false; - await rebuildApplicationMenu.send({ - isUpdateAvailable: this.isUpdateAvailable, + _setAppAutomaticUpdateFailed = async () => { + await this.setAppAutomaticUpdateFailedRequest.execute(); + runInAction(() => { + this.isAutomaticUpdateFailed = true; }); }; - @action _acceptAppUpdate = async () => { - this.applyUpdateRequest.execute(); + @action _openAppUpdateOverlay = () => { + this.isUpdateProgressOpen = true; + this.isUpdatePostponed = false; }; - @action hideUpdateDialog = async () => { - this.isUpdateInstalled = true; - this.isUpdateAvailable = false; - }; - - @action _getLatestAvailableAppVersion = async () => { - // Manual update notification is not available for Daedalus Flight and ITN builds - if (isFlight || isIncentivizedTestnet || isShelleyTestnet) { - return; - } - - const { - latestAppVersion, - applicationVersion, - } = await this.getLatestAppVersionRequest.execute().promise; - this.setLatestAvailableAppVersion(latestAppVersion, applicationVersion); + @action _closeAppUpdateOverlay = () => { + this.isUpdateProgressOpen = false; }; - @action setLatestAvailableAppVersion = ( - latestAppVersion: ?string, - applicationVersion: ?number - ) => { - let isNewAppVersionAvailable = false; - - if (latestAppVersion) { - const { version: currentVersion } = this.environment; - const chunkedCurrentVersion = currentVersion.split('.').map(Number); - const chunkedLatestVersion = latestAppVersion.split('.').map(Number); - - // Main version changed - const isMainVersionChanged = - chunkedCurrentVersion[0] < chunkedLatestVersion[0]; - // Middle version changed - const isMiddleVersionChanged = - chunkedCurrentVersion[0] === chunkedLatestVersion[0] && - chunkedCurrentVersion[1] < chunkedLatestVersion[1]; - // Minor version changed - const isMinorVersionChanged = - chunkedCurrentVersion[0] === chunkedLatestVersion[0] && - chunkedCurrentVersion[1] === chunkedLatestVersion[1] && - chunkedCurrentVersion[2] < chunkedLatestVersion[2]; - isNewAppVersionAvailable = - isMainVersionChanged || isMiddleVersionChanged || isMinorVersionChanged; - } - - this.isNewAppVersionAvailable = isNewAppVersionAvailable; - this.availableAppVersion = latestAppVersion; - this.applicationVersion = applicationVersion; + @action _postponeUpdate = () => { + this.isUpdatePostponed = true; }; - - @computed get isNewAppVersionLoading(): boolean { - return this.getLatestAppVersionRequest.isExecuting; - } - - @computed get isNewAppVersionLoaded(): boolean { - return ( - this.getLatestAppVersionRequest.wasExecuted && - (this.getLatestAppVersionRequest.result !== null || - this.getLatestAppVersionRequest.error !== null) - ); - } - - @computed get showNextUpdate(): boolean { - return ( - this.isUpdateAvailable && - !this.isUpdatePostponed && - !this.isUpdateInstalled && - !isIncentivizedTestnet && - !isShelleyTestnet && - !isFlight - ); - } - - @computed get showManualUpdate(): boolean { - return ( - this.isNewAppVersionAvailable && - !this.isUpdatePostponed && - !this.isUpdateAvailable && - !isIncentivizedTestnet && - !isShelleyTestnet && - !isFlight - ); - } } diff --git a/source/renderer/app/stores/NetworkStatusStore.js b/source/renderer/app/stores/NetworkStatusStore.js index 51c072d3bc..750df09dca 100644 --- a/source/renderer/app/stores/NetworkStatusStore.js +++ b/source/renderer/app/stores/NetworkStatusStore.js @@ -314,7 +314,6 @@ export default class NetworkStatusStore extends Store { this.tlsConfig = null; }); this._setDisconnected(wasConnected); - this.stores.appUpdate.hideUpdateDialog(); this.stores.app._closeActiveDialog(); break; default: diff --git a/source/renderer/app/stores/NewsFeedStore.js b/source/renderer/app/stores/NewsFeedStore.js index 60cd39e2e9..9e8f6db75e 100644 --- a/source/renderer/app/stores/NewsFeedStore.js +++ b/source/renderer/app/stores/NewsFeedStore.js @@ -269,6 +269,7 @@ export default class NewsFeedStore extends Store { return newsfeedItem; }); } + return new News.NewsCollection(news); } diff --git a/source/renderer/app/stores/ProfileStore.js b/source/renderer/app/stores/ProfileStore.js index fe5e183b12..d1c6200a83 100644 --- a/source/renderer/app/stores/ProfileStore.js +++ b/source/renderer/app/stores/ProfileStore.js @@ -31,6 +31,7 @@ import { hasLoadedRequest, isRequestSet, requestGetter, + requestGetterLocale, getRequestKeys, } from '../utils/storesUtils'; import { @@ -148,8 +149,8 @@ export default class ProfileStore extends Store { BigNumber.config({ FORMAT }); }; - @computed get currentLocale(): string { - return requestGetter(this.getProfileLocaleRequest, this.systemLocale); + @computed get currentLocale(): Locale { + return requestGetterLocale(this.getProfileLocaleRequest, this.systemLocale); } @computed get hasLoadedCurrentLocale(): boolean { diff --git a/source/renderer/app/themes/daedalus/cardano.js b/source/renderer/app/themes/daedalus/cardano.js index 0670df6830..34b7e489f4 100644 --- a/source/renderer/app/themes/daedalus/cardano.js +++ b/source/renderer/app/themes/daedalus/cardano.js @@ -121,11 +121,11 @@ export const CARDANO_THEME_OUTPUT = { '--theme-data-migration-layer-button-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-data-migration-layer-button-background-color-hover': '#ffffff', + '--theme-data-migration-layer-button-border-color': '#ffffff', + '--theme-data-migration-layer-button-label-color': '#ffffff', '--theme-data-migration-layer-text-color': '#ffffff', '--theme-data-migration-layer-text-color-hover': '#202225', '--theme-data-migration-layer-text-opacity-color': '#ffffff', - '--theme-data-migration-button-border-color': '#ffffff', - '--theme-data-migration-button-label-color': '#ffffff', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#5e6066', @@ -327,22 +327,22 @@ export const CARDANO_THEME_OUTPUT = { '--theme-loading-spinner-color': '#5e6066', '--theme-loading-spinner-medium-color': '#fff', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': 'rgba(32, 34, 37, 0.96)', - '--theme-manual-update-overlay-button-background-color': - 'rgba(0, 0, 0, 0.1)', - '--theme-manual-update-overlay-button-background-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-border-color': '#ffffff', - '--theme-manual-update-overlay-button-icon-color': 'rgba(255, 255, 255, 1)', - '--theme-manual-update-overlay-button-icon-color-hover': - 'rgba(32, 34, 37, 1)', - '--theme-manual-update-overlay-button-label-color-hover': + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': 'rgba(32, 34, 37, 0.96)', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color-hover': '#ffffff', + '--theme-app-update-overlay-button-border-color': '#ffffff', + '--theme-app-update-overlay-button-icon-color': 'rgba(255, 255, 255, 1)', + '--theme-app-update-overlay-button-icon-color-hover': 'rgba(32, 34, 37, 1)', + '--theme-app-update-overlay-button-label-color-hover': 'rgba(32, 34, 37, 1)', - '--theme-manual-update-overlay-button-text-color-hover': '#202225', - '--theme-manual-update-overlay-text-color': 'rgba(255, 255, 255, 0.7)', - '--theme-manual-update-overlay-text-highlight-color': '#ffffff', - '--theme-manual-update-overlay-title-text-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color': '#ffffff', + '--theme-app-update-overlay-button-text-color-hover': '#202225', + '--theme-app-update-overlay-opacity-text-color': 'rgba(255, 255, 255, 0.7)', + '--theme-app-update-overlay-text-highlight-color': '#ffffff', + '--theme-app-update-overlay-text-color': '#ffffff', + '--theme-app-update-overlay-button-label-color': '#ffffff', + '--theme-app-update-overlay-manual-update-text-color': '#ea4c5b', }, mnemonic: { '--theme-backup-mnemonic-background-color': 'rgba(68, 91, 124, 0.05)', @@ -394,7 +394,8 @@ export const CARDANO_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#ffffff', '--theme-news-feed-icon-color-connecting-screen': '#ffffff', '--theme-news-feed-icon-color-syncing-screen': '#5e6066', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#ffffff', @@ -444,7 +445,8 @@ export const CARDANO_THEME_OUTPUT = { '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(31, 193, 195, 0.2)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#ffffff', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#ffffff', @@ -500,6 +502,9 @@ export const CARDANO_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(255, 255, 255, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(255, 255, 255, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#e0e5ea', + '--theme-progress-bar-large-progress-stripe2': '#fafbfc', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': 'transparent', @@ -1047,6 +1052,23 @@ export const CARDANO_THEME_OUTPUT = { '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(36, 62, 98, 0.96)', + '--theme-news-overlay-update-text-color': '#fafbfc', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(255, 255, 255, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(255, 255, 255, 0.5)', + '--theme-news-overlay-update-button-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-button-text-color': '#ffffff', + '--theme-news-overlay-update-button-hover-text-color': '#273d5b', + '--theme-news-overlay-update-button-background-color-hover': '#ffffff', + '--theme-news-overlay-update-button-border-color': '#ffffff', + }, }; const CARDANO_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/dark-blue.js b/source/renderer/app/themes/daedalus/dark-blue.js index b66f94c22e..5af8b0fbe2 100644 --- a/source/renderer/app/themes/daedalus/dark-blue.js +++ b/source/renderer/app/themes/daedalus/dark-blue.js @@ -121,11 +121,11 @@ export const DARK_BLUE_THEME_OUTPUT = { '--theme-data-migration-layer-button-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-data-migration-layer-button-background-color-hover': '#fafbfc', + '--theme-data-migration-layer-button-border-color': '#ffffff', + '--theme-data-migration-layer-button-label-color': '#ffffff', '--theme-data-migration-layer-text-color': '#fafbfc', '--theme-data-migration-layer-text-color-hover': '#263345', '--theme-data-migration-layer-text-opacity-color': '#fafbfc', - '--theme-data-migration-button-border-color': '#ffffff', - '--theme-data-migration-button-label-color': '#ffffff', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#e9f4fe', @@ -331,22 +331,22 @@ export const DARK_BLUE_THEME_OUTPUT = { '--theme-loading-spinner-color': '#e9f4fe', '--theme-loading-spinner-medium-color': '#e9f4fe', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': 'rgba(38, 51, 69, 0.96)', - '--theme-manual-update-overlay-button-background-color': - 'rgba(0, 0, 0, 0.1)', - '--theme-manual-update-overlay-button-background-color-hover': '#fafbfc', - '--theme-manual-update-overlay-button-icon-color': 'rgba(250, 251, 252, 1)', - '--theme-manual-update-overlay-button-icon-color-hover': - 'rgba(38, 51, 69, 1)', - '--theme-manual-update-overlay-button-label-color-hover': + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': 'rgba(38, 51, 69, 0.96)', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color-hover': '#fafbfc', + '--theme-app-update-overlay-button-icon-color': 'rgba(250, 251, 252, 1)', + '--theme-app-update-overlay-button-icon-color-hover': 'rgba(38, 51, 69, 1)', + '--theme-app-update-overlay-button-label-color-hover': 'rgba(38, 51, 69, 1)', - '--theme-manual-update-overlay-button-text-color-hover': '#263345', - '--theme-manual-update-overlay-button-border-color': '#fafbfc', - '--theme-manual-update-overlay-text-color': 'rgba(250, 251, 252, 0.7)', - '--theme-manual-update-overlay-text-highlight-color': '#fafbfc', - '--theme-manual-update-overlay-title-text-color': '#fafbfc', - '--theme-manual-update-overlay-button-label-color': '#fafbfc', + '--theme-app-update-overlay-button-text-color-hover': '#263345', + '--theme-app-update-overlay-button-border-color': '#fafbfc', + '--theme-app-update-overlay-opacity-text-color': 'rgba(250, 251, 252, 0.7)', + '--theme-app-update-overlay-text-highlight-color': '#fafbfc', + '--theme-app-update-overlay-text-color': '#fafbfc', + '--theme-app-update-overlay-button-label-color': '#fafbfc', + '--theme-app-update-overlay-manual-update-text-color': '#ea4c5b', }, mnemonic: { '--theme-backup-mnemonic-background-color': 'rgba(233, 244, 254, 0.05)', @@ -398,7 +398,8 @@ export const DARK_BLUE_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#e9f4fe', '--theme-news-feed-icon-color-connecting-screen': '#e9f4fe', '--theme-news-feed-icon-color-syncing-screen': '#e9f4fe', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#fafbfc', @@ -448,7 +449,8 @@ export const DARK_BLUE_THEME_OUTPUT = { '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(31, 193, 195, 0.2)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#fafbfc', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#fafbfc', @@ -503,6 +505,9 @@ export const DARK_BLUE_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(233, 244, 254, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(233, 244, 254, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#e0e5eb', + '--theme-progress-bar-large-progress-stripe2': '#fafbfc', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': '#fff', @@ -1052,6 +1057,23 @@ export const DARK_BLUE_THEME_OUTPUT = { '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(36, 62, 98, 0.96)', + '--theme-news-overlay-update-text-color': '#fafbfc', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(255, 255, 255, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(255, 255, 255, 0.5)', + '--theme-news-overlay-update-button-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-button-text-color': '#ffffff', + '--theme-news-overlay-update-button-hover-text-color': '#273d5b', + '--theme-news-overlay-update-button-background-color-hover': '#fafbfc', + '--theme-news-overlay-update-button-border-color': '#ffffff', + }, }; const DARK_BLUE_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/dark-cardano.js b/source/renderer/app/themes/daedalus/dark-cardano.js index 6266e60c2c..17cf158419 100644 --- a/source/renderer/app/themes/daedalus/dark-cardano.js +++ b/source/renderer/app/themes/daedalus/dark-cardano.js @@ -113,11 +113,11 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-data-migration-layer-box-shadow-color': '#36374d', '--theme-data-migration-layer-button-background-color': '#36374d', '--theme-data-migration-layer-button-background-color-hover': '#ffffff', + '--theme-data-migration-layer-button-border-color': '#ffffff', + '--theme-data-migration-layer-button-label-color': '#ffffff', '--theme-data-migration-layer-text-color': '#ffffff', '--theme-data-migration-layer-text-color-hover': '#36374d', '--theme-data-migration-layer-text-opacity-color': '#000000', - '--theme-data-migration-button-border-color': '#ffffff', - '--theme-data-migration-button-label-color': '#ffffff', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#ffffff', @@ -313,19 +313,21 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-loading-spinner-color': '#ffffff', '--theme-loading-spinner-medium-color': '#ffffff', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': '#36374df5', - '--theme-manual-update-overlay-button-background-color': '#313245', - '--theme-manual-update-overlay-button-background-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-icon-color': '#ffffff', - '--theme-manual-update-overlay-button-icon-color-hover': '#36374d', - '--theme-manual-update-overlay-button-text-color-hover': '#36374d', - '--theme-manual-update-overlay-button-border-color': '#ffffff', - '--theme-manual-update-overlay-text-color': '#ffffffb3', - '--theme-manual-update-overlay-text-highlight-color': '#ffffff', - '--theme-manual-update-overlay-title-text-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color-hover': '#000000', + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': '#36374df5', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': '#313245', + '--theme-app-update-overlay-button-background-color-hover': '#ffffff', + '--theme-app-update-overlay-button-icon-color': '#ffffff', + '--theme-app-update-overlay-button-icon-color-hover': '#36374d', + '--theme-app-update-overlay-button-text-color-hover': '#36374d', + '--theme-app-update-overlay-button-border-color': '#ffffff', + '--theme-app-update-overlay-opacity-text-color': '#ffffffb3', + '--theme-app-update-overlay-text-highlight-color': '#ffffff', + '--theme-app-update-overlay-text-color': '#ffffff', + '--theme-app-update-overlay-button-label-color': '#ffffff', + '--theme-app-update-overlay-button-label-color-hover': '#000000', + '--theme-app-update-overlay-manual-update-text-color': '#ea4c5b', }, mnemonic: { '--theme-backup-mnemonic-background-color': '#414251', @@ -374,7 +376,8 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#ffffff', '--theme-news-feed-icon-color-connecting-screen': '#ffffff', '--theme-news-feed-icon-color-syncing-screen': '#ffffff', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#fafbfc', @@ -424,7 +427,8 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(31, 193, 195, 0.2)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#ffffff', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#ffffff', @@ -479,6 +483,9 @@ export const DARK_CARDANO_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(255, 255, 255, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(255, 255, 255, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#e0e5eb', + '--theme-progress-bar-large-progress-stripe2': '#fafbfc', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': '#fff', @@ -1031,6 +1038,23 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(36, 62, 98, 0.96)', + '--theme-news-overlay-update-text-color': '#fafbfc', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(255, 255, 255, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(255, 255, 255, 0.5)', + '--theme-news-overlay-update-button-background-color': '#36374d', + '--theme-news-overlay-update-button-text-color': '#ffffff', + '--theme-news-overlay-update-button-hover-text-color': '#273d5b', + '--theme-news-overlay-update-button-background-color-hover': '#ffffff', + '--theme-news-overlay-update-button-border-color': '#ffffff', + }, }; const DARK_CARDANO_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/flight-candidate.js b/source/renderer/app/themes/daedalus/flight-candidate.js index d461bb040b..72eb88383b 100644 --- a/source/renderer/app/themes/daedalus/flight-candidate.js +++ b/source/renderer/app/themes/daedalus/flight-candidate.js @@ -113,11 +113,11 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-data-migration-layer-box-shadow-color': '#36374d', '--theme-data-migration-layer-button-background-color': '#36374d', '--theme-data-migration-layer-button-background-color-hover': '#ffffff', + '--theme-data-migration-layer-button-border-color': '#ffffff', + '--theme-data-migration-layer-button-label-color': '#ffffff', '--theme-data-migration-layer-text-color': '#ffffff', '--theme-data-migration-layer-text-color-hover': '#36374d', '--theme-data-migration-layer-text-opacity-color': '#000000', - '--theme-data-migration-button-border-color': '#ffffff', - '--theme-data-migration-button-label-color': '#ffffff', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#ffffff', @@ -313,19 +313,21 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-loading-spinner-color': '#ffffff', '--theme-loading-spinner-medium-color': '#ffffff', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': '#36374df5', - '--theme-manual-update-overlay-button-background-color': '#313245', - '--theme-manual-update-overlay-button-background-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-icon-color': '#ffffff', - '--theme-manual-update-overlay-button-icon-color-hover': '#36374d', - '--theme-manual-update-overlay-button-text-color-hover': '#36374d', - '--theme-manual-update-overlay-button-border-color': '#ffffff', - '--theme-manual-update-overlay-text-color': '#ffffffb3', - '--theme-manual-update-overlay-text-highlight-color': '#ffffff', - '--theme-manual-update-overlay-title-text-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color-hover': '#000000', + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': '#36374df5', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': '#313245', + '--theme-app-update-overlay-button-background-color-hover': '#ffffff', + '--theme-app-update-overlay-button-icon-color': '#ffffff', + '--theme-app-update-overlay-button-icon-color-hover': '#36374d', + '--theme-app-update-overlay-button-text-color-hover': '#36374d', + '--theme-app-update-overlay-button-border-color': '#ffffff', + '--theme-app-update-overlay-opacity-text-color': '#ffffffb3', + '--theme-app-update-overlay-text-highlight-color': '#ffffff', + '--theme-app-update-overlay-text-color': '#ffffff', + '--theme-app-update-overlay-button-label-color': '#ffffff', + '--theme-app-update-overlay-button-label-color-hover': '#000000', + '--theme-app-update-overlay-manual-update-text-color': '#ea4c5b', }, mnemonic: { '--theme-backup-mnemonic-background-color': '#414251', @@ -374,7 +376,8 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#ffffff', '--theme-news-feed-icon-color-connecting-screen': '#ffffff', '--theme-news-feed-icon-color-syncing-screen': '#ffffff', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#fafbfc', @@ -424,7 +427,8 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(255, 185, 35, 0.2)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#ffffff', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#ffffff', @@ -479,6 +483,9 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(255, 255, 255, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(255, 255, 255, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#e0e5eb', + '--theme-progress-bar-large-progress-stripe2': '#fafbfc', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': '#fff', @@ -1031,6 +1038,23 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(36, 62, 98, 0.96)', + '--theme-news-overlay-update-text-color': '#fafbfc', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(255, 255, 255, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(255, 255, 255, 0.5)', + '--theme-news-overlay-update-button-background-color': '#36374d', + '--theme-news-overlay-update-button-text-color': '#ffffff', + '--theme-news-overlay-update-button-hover-text-color': '#273d5b', + '--theme-news-overlay-update-button-background-color-hover': '#ffffff', + '--theme-news-overlay-update-button-border-color': '#ffffff', + }, }; const FLIGHT_CANDIDATE_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/incentivized-testnet.js b/source/renderer/app/themes/daedalus/incentivized-testnet.js index f81001e35c..5e2f7101d9 100644 --- a/source/renderer/app/themes/daedalus/incentivized-testnet.js +++ b/source/renderer/app/themes/daedalus/incentivized-testnet.js @@ -114,11 +114,11 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--theme-data-migration-layer-box-shadow-color': '#36374d', '--theme-data-migration-layer-button-background-color': '#36374d', '--theme-data-migration-layer-button-background-color-hover': '#ffffff', + '--theme-data-migration-layer-button-border-color': '#ffffff', + '--theme-data-migration-layer-button-label-color': '#ffffff', '--theme-data-migration-layer-text-color': '#ffffff', '--theme-data-migration-layer-text-color-hover': '#36374d', '--theme-data-migration-layer-text-opacity-color': '#000000', - '--theme-data-migration-button-border-color': '#ffffff', - '--theme-data-migration-button-label-color': '#ffffff', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#ffffff', @@ -314,19 +314,21 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--theme-loading-spinner-color': '#ffffff', '--theme-loading-spinner-medium-color': '#ffffff', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': '#36374df5', - '--theme-manual-update-overlay-button-background-color': '#313245', - '--theme-manual-update-overlay-button-background-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-icon-color': '#ffffff', - '--theme-manual-update-overlay-button-icon-color-hover': '#36374d', - '--theme-manual-update-overlay-button-text-color-hover': '#36374d', - '--theme-manual-update-overlay-button-border-color': '#ffffff', - '--theme-manual-update-overlay-text-color': '#ffffffb3', - '--theme-manual-update-overlay-text-highlight-color': '#ffffff', - '--theme-manual-update-overlay-title-text-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color-hover': '#000000', + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': '#36374df5', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': '#313245', + '--theme-app-update-overlay-button-background-color-hover': '#ffffff', + '--theme-app-update-overlay-button-icon-color': '#ffffff', + '--theme-app-update-overlay-button-icon-color-hover': '#36374d', + '--theme-app-update-overlay-button-text-color-hover': '#36374d', + '--theme-app-update-overlay-button-border-color': '#ffffff', + '--theme-app-update-overlay-opacity-text-color': '#ffffffb3', + '--theme-app-update-overlay-text-highlight-color': '#ffffff', + '--theme-app-update-overlay-text-color': '#ffffff', + '--theme-app-update-overlay-button-label-color': '#ffffff', + '--theme-app-update-overlay-button-label-color-hover': '#000000', + '--theme-app-update-overlay-manual-update-text-color': '#eb4a22', }, mnemonic: { '--theme-backup-mnemonic-background-color': '#414251', @@ -375,7 +377,8 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#ffffff', '--theme-news-feed-icon-color-connecting-screen': '#ffffff', '--theme-news-feed-icon-color-syncing-screen': '#ffffff', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#fafbfc', @@ -424,7 +427,8 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--theme-news-item-action-button-color-hover': '#2a2b3c', '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(235, 34, 88, 0.2)', - '--theme-news-item-badge-color': '#eb4a22', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#eb4a22', '--theme-news-item-content-link-color': '#ffffff', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#ffffff', @@ -479,6 +483,9 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(255, 255, 255, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(255, 255, 255, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#e0e5eb', + '--theme-progress-bar-large-progress-stripe2': '#fafbfc', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': '#fff', @@ -1033,6 +1040,23 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(36, 62, 98, 0.96)', + '--theme-news-overlay-update-text-color': '#fafbfc', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(255, 255, 255, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(255, 255, 255, 0.5)', + '--theme-news-overlay-update-button-background-color': '#36374d', + '--theme-news-overlay-update-button-text-color': '#ffffff', + '--theme-news-overlay-update-button-hover-text-color': '#273d5b', + '--theme-news-overlay-update-button-background-color-hover': '#ffffff', + '--theme-news-overlay-update-button-border-color': '#ffffff', + }, }; const INCENTIVIZED_TESTNET_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/light-blue.js b/source/renderer/app/themes/daedalus/light-blue.js index 217ff0ef2e..f26a166f71 100644 --- a/source/renderer/app/themes/daedalus/light-blue.js +++ b/source/renderer/app/themes/daedalus/light-blue.js @@ -119,11 +119,11 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-data-migration-layer-box-shadow-color': 'rgba(0, 0, 0, 0.25)', '--theme-data-migration-layer-button-background-color': '#243E62', '--theme-data-migration-layer-button-background-color-hover': '#fafbfc', + '--theme-data-migration-layer-button-border-color': '#fafbfc', + '--theme-data-migration-layer-button-label-color': '#fafbfc', '--theme-data-migration-layer-text-color': '#fafbfc', '--theme-data-migration-layer-text-color-hover': '#243E62', '--theme-data-migration-layer-text-opacity-color': '#fafbfc', - '--theme-data-migration-button-border-color': '#fafbfc', - '--theme-data-migration-button-label-color': '#fafbfc', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#5e6066', @@ -326,21 +326,22 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-loading-spinner-color': '#5e6066', '--theme-loading-spinner-medium-color': '#fafbfc', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': 'rgba(36, 62, 98, 0.96)', - '--theme-manual-update-overlay-button-background-color': '#243e62', - '--theme-manual-update-overlay-button-background-color-hover': '#fafbfc', - '--theme-manual-update-overlay-button-border-color': '#fafbfc', - '--theme-manual-update-overlay-button-icon-color': 'rgba(250, 251, 252, 1)', - '--theme-manual-update-overlay-button-icon-color-hover': + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': 'rgba(36, 62, 98, 0.96)', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': '#243e62', + '--theme-app-update-overlay-button-background-color-hover': '#fafbfc', + '--theme-app-update-overlay-button-border-color': '#fafbfc', + '--theme-app-update-overlay-button-icon-color': 'rgba(250, 251, 252, 1)', + '--theme-app-update-overlay-button-icon-color-hover': 'rgba(36, 62, 98, 1)', + '--theme-app-update-overlay-button-label-color-hover': 'rgba(36, 62, 98, 1)', - '--theme-manual-update-overlay-button-label-color-hover': - 'rgba(36, 62, 98, 1)', - '--theme-manual-update-overlay-button-text-color-hover': '#243e62', - '--theme-manual-update-overlay-text-color': 'rgba(250, 251, 252, 0.7)', - '--theme-manual-update-overlay-text-highlight-color': '#fafbfc', - '--theme-manual-update-overlay-title-text-color': '#fafbfc', - '--theme-manual-update-overlay-button-label-color': '#fafbfc', + '--theme-app-update-overlay-button-text-color-hover': '#243e62', + '--theme-app-update-overlay-opacity-text-color': 'rgba(250, 251, 252, 0.7)', + '--theme-app-update-overlay-text-highlight-color': '#fafbfc', + '--theme-app-update-overlay-text-color': '#fafbfc', + '--theme-app-update-overlay-button-label-color': '#fafbfc', + '--theme-app-update-overlay-manual-update-text-color': '#ea4c5b', }, mnemonic: { '--theme-backup-mnemonic-background-color': 'rgba(68, 91, 124, 0.05)', @@ -392,7 +393,8 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#fafbfc', '--theme-news-feed-icon-color-connecting-screen': '#fafbfc', '--theme-news-feed-icon-color-syncing-screen': '#5e6066', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#fafbfc', @@ -442,7 +444,8 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(31, 193, 195, 0.2)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#fafbfc', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#fafbfc', @@ -498,6 +501,9 @@ export const LIGHT_BLUE_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(255, 255, 255, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(255, 255, 255, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#e0e5eb', + '--theme-progress-bar-large-progress-stripe2': '#fafbfc', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': 'transparent', @@ -1042,6 +1048,23 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(36, 62, 98, 0.96)', + '--theme-news-overlay-update-text-color': '#fafbfc', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-button-hover-text-color': '#273d5b', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(255, 255, 255, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(255, 255, 255, 0.5)', + '--theme-news-overlay-update-button-background-color': '#243E62', + '--theme-news-overlay-update-button-text-color': '#fafbfc', + '--theme-news-overlay-update-button-background-color-hover': '#fafbfc', + '--theme-news-overlay-update-button-border-color': '#fafbfc', + }, }; const LIGHT_BLUE_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/shelley-testnet.js b/source/renderer/app/themes/daedalus/shelley-testnet.js index 60304ef879..7317a871a5 100644 --- a/source/renderer/app/themes/daedalus/shelley-testnet.js +++ b/source/renderer/app/themes/daedalus/shelley-testnet.js @@ -113,11 +113,11 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--theme-data-migration-layer-box-shadow-color': '#36374d', '--theme-data-migration-layer-button-background-color': '#36374d', '--theme-data-migration-layer-button-background-color-hover': '#ffffff', + '--theme-data-migration-layer-button-border-color': '#ffffff', + '--theme-data-migration-layer-button-label-color': '#ffffff', '--theme-data-migration-layer-text-color': '#ffffff', '--theme-data-migration-layer-text-color-hover': '#36374d', '--theme-data-migration-layer-text-opacity-color': '#000000', - '--theme-data-migration-button-border-color': '#ffffff', - '--theme-data-migration-button-label-color': '#ffffff', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#ffffff', @@ -313,19 +313,21 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--theme-loading-spinner-color': '#ffffff', '--theme-loading-spinner-medium-color': '#ffffff', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': '#36374df5', - '--theme-manual-update-overlay-button-background-color': '#313245', - '--theme-manual-update-overlay-button-background-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-icon-color': '#ffffff', - '--theme-manual-update-overlay-button-icon-color-hover': '#36374d', - '--theme-manual-update-overlay-button-text-color-hover': '#36374d', - '--theme-manual-update-overlay-button-border-color': '#ffffff', - '--theme-manual-update-overlay-text-color': '#ffffffb3', - '--theme-manual-update-overlay-text-highlight-color': '#ffffff', - '--theme-manual-update-overlay-title-text-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color': '#ffffff', - '--theme-manual-update-overlay-button-label-color-hover': '#000000', + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': '#36374df5', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': '#313245', + '--theme-app-update-overlay-button-background-color-hover': '#ffffff', + '--theme-app-update-overlay-button-icon-color': '#ffffff', + '--theme-app-update-overlay-button-icon-color-hover': '#36374d', + '--theme-app-update-overlay-button-text-color-hover': '#36374d', + '--theme-app-update-overlay-button-border-color': '#ffffff', + '--theme-app-update-overlay-opacity-text-color': '#ffffffb3', + '--theme-app-update-overlay-text-highlight-color': '#ffffff', + '--theme-app-update-overlay-text-color': '#ffffff', + '--theme-app-update-overlay-button-label-color': '#ffffff', + '--theme-app-update-overlay-button-label-color-hover': '#000000', + '--theme-app-update-overlay-manual-update-text-color': '#ea4c5b', }, mnemonic: { '--theme-backup-mnemonic-background-color': '#414251', @@ -374,7 +376,8 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#ffffff', '--theme-news-feed-icon-color-connecting-screen': '#ffffff', '--theme-news-feed-icon-color-syncing-screen': '#ffffff', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#fafbfc', @@ -424,7 +427,8 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(137, 142, 230, 0.2)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#ffffff', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#ffffff', @@ -479,6 +483,9 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(255, 255, 255, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(255, 255, 255, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#e0e5eb', + '--theme-progress-bar-large-progress-stripe2': '#fafbfc', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': '#fff', @@ -1030,6 +1037,24 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(42, 43, 60, 0.96)', + '--theme-news-overlay-update-text-color': '#fff', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-subtitle-text-color1': '#898ee6', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(255, 255, 255, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(255, 255, 255, 0.5)', + '--theme-news-overlay-update-button-background-color': '#36374d', + '--theme-news-overlay-update-button-text-color': '#ffffff', + '--theme-news-overlay-update-button-hover-text-color': '#2a2b3c', + '--theme-news-overlay-update-button-background-color-hover': '#ffffff', + '--theme-news-overlay-update-button-border-color': '#ffffff', + }, }; const SHELLEY_TESTNET_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/white.js b/source/renderer/app/themes/daedalus/white.js index 3bfd374e8f..1badf55768 100644 --- a/source/renderer/app/themes/daedalus/white.js +++ b/source/renderer/app/themes/daedalus/white.js @@ -115,11 +115,11 @@ export const WHITE_THEME_OUTPUT = { '--theme-data-migration-layer-box-shadow-color': '#29b595', '--theme-data-migration-layer-button-background-color': '#ffffff', '--theme-data-migration-layer-button-background-color-hover': '#29b595', + '--theme-data-migration-layer-button-border-color': '#29b595', + '--theme-data-migration-layer-button-label-color': '#29b595', '--theme-data-migration-layer-text-color': '#2d2d2d', '--theme-data-migration-layer-text-color-hover': '#ffffff', '--theme-data-migration-layer-text-opacity-color': '#fafbfc', - '--theme-data-migration-button-border-color': '#29b595', - '--theme-data-migration-button-label-color': '#29b595', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#2d2d2d', @@ -313,19 +313,21 @@ export const WHITE_THEME_OUTPUT = { '--theme-loading-spinner-color': '#2d2d2d', '--theme-loading-spinner-medium-color': '#2d2d2d', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': '#fffffff5', - '--theme-manual-update-overlay-button-background-color': '#fff', - '--theme-manual-update-overlay-button-background-color-hover': '#29b595', - '--theme-manual-update-overlay-button-icon-color': '#29b595', - '--theme-manual-update-overlay-button-icon-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-text-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-border-color': '#29b595', - '--theme-manual-update-overlay-text-color': '#2d2d2db3', - '--theme-manual-update-overlay-text-highlight-color': '#2d2d2d', - '--theme-manual-update-overlay-title-text-color': '#2d2d2d', - '--theme-manual-update-overlay-button-label-color': '#29b595', - '--theme-manual-update-overlay-button-label-color-hover': '#fff', + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': '#fffffff5', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': '#fff', + '--theme-app-update-overlay-button-background-color-hover': '#29b595', + '--theme-app-update-overlay-button-icon-color': '#29b595', + '--theme-app-update-overlay-button-icon-color-hover': '#ffffff', + '--theme-app-update-overlay-button-text-color-hover': '#ffffff', + '--theme-app-update-overlay-button-border-color': '#29b595', + '--theme-app-update-overlay-opacity-text-color': '#2d2d2db3', + '--theme-app-update-overlay-text-highlight-color': '#2d2d2d', + '--theme-app-update-overlay-text-color': '#2d2d2d', + '--theme-app-update-overlay-button-label-color': '#29b595', + '--theme-app-update-overlay-button-label-color-hover': '#fff', + '--theme-app-update-overlay-manual-update-text-color': '#ea4c5b', }, mnemonic: { '--theme-backup-mnemonic-background-color': 'rgba(219, 219, 219, 0.2)', @@ -378,7 +380,8 @@ export const WHITE_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#2d2d2d', '--theme-news-feed-icon-color-connecting-screen': '#2d2d2d', '--theme-news-feed-icon-color-syncing-screen': '#2d2d2d', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(41, 181, 149, 0.1)', '--theme-news-feed-no-fetch-color': '#2d2d2d', @@ -429,7 +432,8 @@ export const WHITE_THEME_OUTPUT = { '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(31, 193, 195, 0.2)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#2d2d2d', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.05)', '--theme-news-item-title-color': '#2d2d2d', @@ -485,6 +489,9 @@ export const WHITE_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(45, 45, 45, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(45, 45, 45, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#29b595', + '--theme-progress-bar-large-progress-stripe2': '#69cbb4', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': 'transparent', @@ -1032,6 +1039,24 @@ export const WHITE_THEME_OUTPUT = { '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': '#ffffff', + '--theme-news-overlay-update-text-color': '#fafbfc', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(255, 255, 255, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(255, 255, 255, 0.5)', + '--theme-news-overlay-update-button-background-color': + 'rgba(41, 181, 149, 0.1)', + '--theme-news-overlay-update-button-text-color': '#29b595', + '--theme-news-overlay-update-button-hover-text-color': '#fff', + '--theme-news-overlay-update-button-background-color-hover': '#29b595', + '--theme-news-overlay-update-button-border-color': '#29b595', + }, }; const WHITE_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/yellow.js b/source/renderer/app/themes/daedalus/yellow.js index 0287a0bfd7..a0fc17e1a6 100644 --- a/source/renderer/app/themes/daedalus/yellow.js +++ b/source/renderer/app/themes/daedalus/yellow.js @@ -119,11 +119,11 @@ export const YELLOW_THEME_OUTPUT = { '--theme-data-migration-layer-button-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-data-migration-layer-button-background-color-hover': '#2d2d2d', + '--theme-data-migration-layer-button-border-color': '#2d2d2d', + '--theme-data-migration-layer-button-label-color': '#2d2d2d', '--theme-data-migration-layer-text-color': '#2d2d2d', '--theme-data-migration-layer-text-color-hover': '#ffffff', '--theme-data-migration-layer-text-opacity-color': '#fafbfc', - '--theme-data-migration-button-border-color': '#2d2d2d', - '--theme-data-migration-button-label-color': '#2d2d2d', }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': '#2d2d2d', @@ -308,7 +308,7 @@ export const YELLOW_THEME_OUTPUT = { '--theme-loading-no-disk-space-background-color': 'rgba(171, 23, 0, 0.94)', '--theme-loading-no-disk-space-text-color': '#ffffff', '--theme-loading-no-disk-space-attention-icon-color': '#ffffff', - '--theme-loading-status-icons-on-color': '#2dc06c', + '--theme-loading-status-icons-on-color': '#009900', '--theme-loading-status-icons-off-color': '#ea4c5b', '--theme-loading-status-icons-unloaded-loading-color': '#2d2d2d', '--theme-loading-status-icons-unloaded-syncing-color': '#2d2d2d', @@ -316,21 +316,21 @@ export const YELLOW_THEME_OUTPUT = { '--theme-loading-spinner-color': '#2d2d2d', '--theme-loading-spinner-medium-color': '#2d2d2d', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': - 'rgba(255, 185, 35, 0.96)', - '--theme-manual-update-overlay-button-background-color': - 'rgba(0, 0, 0, 0.1)', - '--theme-manual-update-overlay-button-background-color-hover': '#2d2d2d', - '--theme-manual-update-overlay-button-icon-color': '#2d2d2d', - '--theme-manual-update-overlay-button-icon-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-text-color-hover': '#ffffff', - '--theme-manual-update-overlay-button-border-color': '#2d2d2d', - '--theme-manual-update-overlay-text-color': 'rgba(45, 45, 45, 0.7)', - '--theme-manual-update-overlay-text-highlight-color': 'rgba(45, 45, 45, 1)', - '--theme-manual-update-overlay-title-text-color': 'rgba(45, 45, 45, 1)', - '--theme-manual-update-overlay-button-label-color': 'rgba(45, 45, 45, 1)', - '--theme-manual-update-overlay-button-label-color-hover': '#ffffff', + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': 'rgba(255, 185, 35, 0.96)', + '--theme-app-update-overlay-content-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color-hover': '#2d2d2d', + '--theme-app-update-overlay-button-icon-color': '#2d2d2d', + '--theme-app-update-overlay-button-icon-color-hover': '#ffffff', + '--theme-app-update-overlay-button-text-color-hover': '#ffffff', + '--theme-app-update-overlay-button-border-color': '#2d2d2d', + '--theme-app-update-overlay-opacity-text-color': 'rgba(45, 45, 45, 0.7)', + '--theme-app-update-overlay-text-highlight-color': '#rgba(45, 45, 45, 1)', + '--theme-app-update-overlay-text-color': 'rgba(45, 45, 45, 1)', + '--theme-app-update-overlay-button-label-color': 'rgba(45, 45, 45, 1)', + '--theme-app-update-overlay-button-label-color-hover': '#ffffff', + '--theme-app-update-overlay-manual-update-text-color': '#ea4c5b', }, mnemonic: { '--theme-backup-mnemonic-background-color': '#f1eee6', @@ -381,7 +381,8 @@ export const YELLOW_THEME_OUTPUT = { '--theme-news-feed-icon-color': '#2d2d2d', '--theme-news-feed-icon-color-connecting-screen': '#2d2d2d', '--theme-news-feed-icon-color-syncing-screen': '#2d2d2d', - '--theme-news-feed-icon-dot-background-color': '#be0b0b', + '--theme-news-feed-icon-green-dot-background-color': '#009900', + '--theme-news-feed-icon-red-dot-background-color': '#be0b0b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#2d2d2d', @@ -431,7 +432,8 @@ export const YELLOW_THEME_OUTPUT = { '--theme-news-item-alert-background-color': 'rgba(249, 128, 10, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(31, 193, 195, 0.3)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#009900', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#2d2d2d', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#2d2d2d', @@ -486,6 +488,9 @@ export const YELLOW_THEME_OUTPUT = { progressBar: { '--theme-progress-bar-background-color': 'rgba(45, 45, 45, 0.3)', '--theme-progress-bar-foreground-color': 'rgba(45, 45, 45, 0.7)', + '--theme-progress-bar-large-progress-stripe1': '#2d2d2d', + '--theme-progress-bar-large-progress-stripe2': '#3f3e3e', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': 'transparent', @@ -912,7 +917,7 @@ export const YELLOW_THEME_OUTPUT = { '--theme-transactions-state-text-color': '#ffffff', '--theme-transactions-search-background-color': '#f8f3ed', '--theme-transactions-icon-type-expend-background-color': '#84a2d2', - '--theme-transactions-icon-type-income-background-color': '#2dc06c', + '--theme-transactions-icon-type-income-background-color': '#009900', '--theme-transactions-icon-type-exchange-background-color': '#10aca4', '--theme-transactions-arrow-stroke-color': '#2d2d2d', '--theme-transactions-icon-type-pending-regular-background-color': @@ -1027,10 +1032,26 @@ export const YELLOW_THEME_OUTPUT = { '--rp-password-input-error-bg-color': 'rgba(234, 76, 91, 0.4)', '--rp-password-input-warning-score-color': '#f2a218', '--rp-password-input-warning-bg-color': 'rgba(242, 162, 24, 0.4)', - '--rp-password-input-success-score-color': '#2dc06c', + '--rp-password-input-success-score-color': '#009900', '--rp-password-input-success-bg-color': 'rgba(45, 192, 108, 0.4)', '--rp-password-input-tooltip-font-family': 'var(--rp-theme-font-medium)', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(255, 185, 35, 0.96)', + '--theme-news-overlay-update-text-color': '#2d2d2d', + '--theme-news-overlay-update-subtitle-text-color': 'rgba(45, 45, 45, 0.5)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': + 'rgba(45, 45, 45, 0.3)', + '--theme-news-overlay-update-content-scroll-hover-background-color': + 'rgba(45, 45, 45, 0.5)', + '--theme-news-overlay-update-button-background-color': 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-button-text-color': '#2d2d2d', + '--theme-news-overlay-update-button-hover-text-color': '#fafbfc', + '--theme-news-overlay-update-button-background-color-hover': '#2d2d2d', + '--theme-news-overlay-update-button-border-color': '#2d2d2d', + }, }; const YELLOW_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/utils/createTheme.js b/source/renderer/app/themes/utils/createTheme.js index 398333e183..13cc28f0dd 100644 --- a/source/renderer/app/themes/utils/createTheme.js +++ b/source/renderer/app/themes/utils/createTheme.js @@ -386,11 +386,11 @@ export const createDaedalusComponentsTheme = ( '--theme-data-migration-layer-box-shadow-color': `${background.secondary.regular}`, '--theme-data-migration-layer-button-background-color': `${background.secondary.regular}`, '--theme-data-migration-layer-button-background-color-hover': `${background.primary.regular}`, - '--theme-data-migration-layer-text-opacity-color': `${text.secondary}`, + '--theme-data-migration-layer-button-border-color': `${text.secondary}`, + '--theme-data-migration-layer-button-label-color': `${text.secondary}`, '--theme-data-migration-layer-text-color': `${text.secondary}`, '--theme-data-migration-layer-text-color-hover': `${text.primary}`, - '--theme-data-migration-button-border-color': `${text.secondary}`, - '--theme-data-migration-button-label-color': `${text.secondary}`, + '--theme-data-migration-layer-text-opacity-color': `${text.secondary}`, }, delegationSetupWizard: { '--theme-delegation-steps-activation-steps-indicator-color': `${text.primary}`, @@ -598,23 +598,27 @@ export const createDaedalusComponentsTheme = ( '--theme-loading-spinner-color': `${text.primary}`, '--theme-loading-spinner-medium-color': '#fff', }, - manualUpdate: { - '--theme-manual-update-overlay-background-color': `${chroma( + appUpdateOverlay: { + '--theme-app-update-overlay-background-color': `${chroma( background.secondary.regular ).alpha(0.96)}`, - '--theme-manual-update-overlay-button-background-color': `${background.secondary.dark}`, - '--theme-manual-update-overlay-button-background-color-hover': `${text.secondary}`, - '--theme-manual-update-overlay-button-icon-color': `${text.secondary}`, - '--theme-manual-update-overlay-button-icon-color-hover': `${background.secondary.regular}`, - '--theme-manual-update-overlay-button-text-color-hover': `${background.secondary.regular}`, - '--theme-manual-update-overlay-button-border-color': `${text.secondary}`, - '--theme-manual-update-overlay-text-color': `${chroma( + + '--theme-app-update-overlay-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-app-update-overlay-button-background-color': `${background.secondary.dark}`, + '--theme-app-update-overlay-button-background-color-hover': `${text.secondary}`, + '--theme-app-update-overlay-button-icon-color': `${text.secondary}`, + '--theme-app-update-overlay-button-icon-color-hover': `${background.secondary.regular}`, + '--theme-app-update-overlay-button-text-color-hover': `${background.secondary.regular}`, + '--theme-app-update-overlay-button-border-color': `${text.secondary}`, + '--theme-app-update-overlay-opacity-text-color': `${chroma( text.secondary ).alpha(0.7)}`, - '--theme-manual-update-overlay-text-highlight-color': `${text.secondary}`, - '--theme-manual-update-overlay-title-text-color': `${text.secondary}`, - '--theme-manual-update-overlay-button-label-color': `${text.secondary}`, - '--theme-manual-update-overlay-button-label-color-hover': `${text.secondary}`, + '--theme-app-update-overlay-text-highlight-color': `${text.secondary}`, + '--theme-app-update-overlay-text-color': `${text.secondary}`, + '--theme-app-update-overlay-manual-update-text-color': `${error.regular}`, + '--theme-app-update-overlay-button-label-color': `${text.secondary}`, + '--theme-app-update-overlay-button-label-color-hover': `${text.secondary}`, }, mnemonic: { '--theme-backup-mnemonic-background-color': `${background.primary.light}`, @@ -675,7 +679,8 @@ export const createDaedalusComponentsTheme = ( '--theme-news-feed-icon-color': '#fafbfc', '--theme-news-feed-icon-color-connecting-screen': '#fafbfc', '--theme-news-feed-icon-color-syncing-screen': '#5e6066', - '--theme-news-feed-icon-dot-background-color': '#ea4c5b', + '--theme-news-feed-icon-green-dot-background-color': '#2DC06C', + '--theme-news-feed-icon-red-dot-background-color': '#ea4c5b', '--theme-news-feed-icon-toggle-hover-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-feed-no-fetch-color': '#fafbfc', @@ -736,11 +741,26 @@ export const createDaedalusComponentsTheme = ( '--theme-news-item-alert-background-color': 'rgba(242, 162, 24, 0.5)', '--theme-news-item-announcement-background-color': 'rgba(234, 76, 91, 0.25)', - '--theme-news-item-badge-color': '#ea4c5b', + '--theme-news-item-badge-green-color': '#2DC06C', + '--theme-news-item-badge-red-color': '#ea4c5b', '--theme-news-item-content-link-color': '#fafbfc', '--theme-news-item-info-background-color': 'rgba(0, 0, 0, 0.1)', '--theme-news-item-title-color': '#fafbfc', }, + newsUpdateOverlay: { + '--theme-news-overlay-update-background-color': 'rgba(36, 62, 98, 0.96)', + '--theme-news-overlay-update-text-color': '#fafbfc', + '--theme-news-overlay-update-subtitle-text-color': + 'rgba(250, 251, 252, 0.7)', + '--theme-news-overlay-update-content-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-content-scroll-background-color': `rgba(255, 255, 255, 0.3)`, + '--theme-news-overlay-update-content-scroll-hover-background-color': `rgba(255, 255, 255, 0.5)`, + '--theme-news-overlay-update-button-background-color': + 'rgba(0, 0, 0, 0.1)', + '--theme-news-overlay-update-button-text-color': '#fafbfc', + '--theme-news-overlay-update-button-hover-text-color': '#5e6066', + }, appUpdate: { '--theme-node-update-background-color': `${background.primary.regular}`, '--theme-node-update-title-color': `${text.primary}`, @@ -798,6 +818,9 @@ export const createDaedalusComponentsTheme = ( '--theme-progress-bar-foreground-color': `${chroma( background.primary.light ).alpha(0.7)}`, + '--theme-progress-bar-large-progress-stripe1': '#e0e5eb', + '--theme-progress-bar-large-progress-stripe2': '#fafbfc', + '--theme-progress-bar-large-background-color': 'rgba(0, 0, 0, 0.1)', }, receiveQRCode: { '--theme-receive-qr-code-background-color': 'transparent', diff --git a/source/renderer/app/utils/formatters.js b/source/renderer/app/utils/formatters.js index c31aa32f38..83a2d85c47 100644 --- a/source/renderer/app/utils/formatters.js +++ b/source/renderer/app/utils/formatters.js @@ -1,9 +1,13 @@ // @flow import BigNumber from 'bignumber.js'; +import moment from 'moment'; import { DECIMAL_PLACES_IN_ADA, LOVELACES_PER_ADA, } from '../config/numbersConfig'; +import { momentLocales } from '../../../common/types/locales.types'; +import type { DownloadData } from '../../../common/types/downloadManager.types'; +import type { Locale } from '../../../common/types/locales.types'; export const formattedWalletAmount = ( amount: BigNumber, @@ -102,6 +106,46 @@ export const formattedBytesToSize = (bytes: number): string => { return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}`; }; +export type FormattedDownloadData = { + timeLeft: string, + downloaded: string, + total: string, + progress: number, +}; + +export const formattedDownloadData = ( + downloadData?: ?DownloadData, + userLocale: Locale +): FormattedDownloadData => { + let timeLeft = ''; + let downloaded = ''; + let total = ''; + let progress = 0; + if (downloadData) { + const { + serverFileSize, + downloadSize, + progress: rawProgress, + speed, + remainingSize, + } = downloadData; + const secondsLeft = remainingSize / speed; + moment.locale(momentLocales[userLocale]); + timeLeft = moment() + .add(secondsLeft, 'seconds') + .fromNow(true); + downloaded = formattedBytesToSize(downloadSize); + total = formattedBytesToSize(serverFileSize); + progress = parseInt(rawProgress, 10); + } + return { + timeLeft, + downloaded, + total, + progress, + }; +}; + export const generateThousands = (value: number) => { if (value <= 1000) { return Math.round(value); diff --git a/source/renderer/app/utils/storesUtils.js b/source/renderer/app/utils/storesUtils.js index 83c90e8909..b588145389 100644 --- a/source/renderer/app/utils/storesUtils.js +++ b/source/renderer/app/utils/storesUtils.js @@ -2,10 +2,11 @@ import { findKey, camelCase } from 'lodash'; import { LOCALES } from '../../../common/types/locales.types'; import Request from '../stores/lib/LocalizedRequest'; +import type { Locale } from '../../../common/types/locales.types'; export const getRequestKeys = ( param: string, - currentLocale: string + currentLocale: Locale ): Request => { const currentLanguage = findKey(LOCALES, l => l === currentLocale); const languageSufix = param === 'dateFormat' ? currentLanguage : ''; @@ -22,6 +23,12 @@ export const requestGetter = (req: Request, systemValue: string) => { return systemValue; }; +export const requestGetterLocale = (req: Request, systemValue: Locale) => { + const { result } = req.execute(); + if (isRequestSet(req)) return result; + return systemValue; +}; + export const hasLoadedRequest = (req: Request) => req.wasExecuted && req.result !== null; diff --git a/source/renderer/app/utils/waitFor.js b/source/renderer/app/utils/waitFor.js new file mode 100644 index 0000000000..e3f711ee81 --- /dev/null +++ b/source/renderer/app/utils/waitFor.js @@ -0,0 +1,9 @@ +// @flow + +export const waitFor = (conditionFunction: Function): Promise => { + const poll = resolve => { + if (conditionFunction()) resolve(); + else setTimeout(() => poll(resolve), 400); + }; + return new Promise(poll); +}; diff --git a/storybook/stories/_support/StoryLayout.js b/storybook/stories/_support/StoryLayout.js index 732b22c771..c8ae63e83f 100644 --- a/storybook/stories/_support/StoryLayout.js +++ b/storybook/stories/_support/StoryLayout.js @@ -223,7 +223,8 @@ export default class StoryLayout extends Component { /> ); diff --git a/storybook/stories/common/Widgets.stories.js b/storybook/stories/common/Widgets.stories.js index 24083831ac..8d5aa10af8 100644 --- a/storybook/stories/common/Widgets.stories.js +++ b/storybook/stories/common/Widgets.stories.js @@ -71,11 +71,6 @@ const messages = defineMessages({ defaultMessage: '!!!Save', description: 'Save description.', }, - followInstructions: { - id: 'manualUpdate.button.label', - defaultMessage: '!!!Follow instructions and manually update', - description: 'Follow instructions and manually update description.', - }, }); storiesOf('Common|Widgets', module) @@ -161,7 +156,7 @@ storiesOf('Common|Widgets', module) .add('ButtonLink', (props: { locale: string }) => ( )) diff --git a/storybook/stories/index.js b/storybook/stories/index.js index cf46e6677a..4598c7c5fc 100644 --- a/storybook/stories/index.js +++ b/storybook/stories/index.js @@ -18,6 +18,7 @@ import './settings'; import './news/NewsFeed.stories'; import './news/IncidentOverlay.stories'; import './news/AlertsOverlay.stories'; +import './news/AppUpdateOverlay.stories'; // Navigation import './navigation/Sidebar.stories'; diff --git a/storybook/stories/news/AlertsOverlay.stories.js b/storybook/stories/news/AlertsOverlay.stories.js index c86aedf76d..2a2f05c651 100644 --- a/storybook/stories/news/AlertsOverlay.stories.js +++ b/storybook/stories/news/AlertsOverlay.stories.js @@ -84,11 +84,11 @@ const getAlerts = (locale: string) => [ }), ]; -storiesOf('News|Alerts', module) +storiesOf('News|Overlays', module) .addDecorator((story, context) => ( {withKnobs(story, context)} )) - .add('Alerts Overlay', (props: { locale: string }) => ( + .add('Alerts', (props: { locale: string }) => ( {story()}) + .addDecorator(withKnobs) + .add('Update', ({ locale }: { locale: string }) => { + const scenario = radios( + 'Scenario', + { + Downloading: 'downloading', + 'Download complete': 'downloaded', + 'Process failed': 'failed', + }, + 'downloading' + ); + + let isUpdateDownloaded = true; + let isAutomaticUpdateFailed = false; + let isLinux = false; + let isWaitingToQuitDaedalus = false; + let installationProgress = 0; + + if (scenario === 'downloading') { + isUpdateDownloaded = false; + } else if (scenario === 'failed') { + isAutomaticUpdateFailed = true; + } else if (scenario === 'downloaded') { + isLinux = boolean('isLinux', false); + isWaitingToQuitDaedalus = boolean('isWaitingToQuitDaedalus', false); + if (isLinux && isWaitingToQuitDaedalus) + installationProgress = number('installationProgress', 30, { + range: true, + min: 0, + max: 100, + step: 1, + }); + } + + const downloadProgress = + scenario === 'downloading' + ? number('downloadProgress', 30, { + range: true, + min: 0, + max: 100, + step: 1, + }) + : 0; + + const timeLeftNumber = parseInt( + rangeMap(downloadProgress, 0, 100, 30, 1), + 10 + ); + + const downloadTimeLeft = { + 'EN-US': `${timeLeftNumber} minutes`, + 'JP-JP': `${timeLeftNumber}分`, + }; + + return ( + + ); + }); diff --git a/storybook/stories/news/IncidentOverlay.stories.js b/storybook/stories/news/IncidentOverlay.stories.js index 81a16bfd11..bc30a9876c 100644 --- a/storybook/stories/news/IncidentOverlay.stories.js +++ b/storybook/stories/news/IncidentOverlay.stories.js @@ -8,7 +8,7 @@ import IncidentOverlay from '../../../source/renderer/app/components/news/Incide import { dateOptions } from '../_support/profileSettings'; import { DATE_ENGLISH_OPTIONS } from '../../../source/renderer/app/config/profileConfig'; -storiesOf('News|Incidents', module) +storiesOf('News|Overlays', module) .addDecorator(story => ( {story({ @@ -41,7 +41,7 @@ storiesOf('News|Incidents', module) )} /> )) - .add('Incident Overlay - Themed', props => ( + .add('Incident - Themed', props => ( )) - .add('Incident Overlay - Grey', props => ( + .add('Incident - Grey', props => ( ( @@ -139,11 +35,13 @@ storiesOf('News|NewsFeed', module) onNewsItemActionClick={action('onNewsItemActionClick')} onClose={action('onClose')} news={new News.NewsCollection([])} - isNewsFeedOpen={boolean('isNewsFeedOpen2', true)} + isNewsFeedOpen={boolean('isNewsFeedOpen', true)} onOpenExternalLink={action('onOpenExternalLink')} onOpenAlert={action('onOpenAlert')} onProceedNewsAction={action('onOpenExternalLink')} + onOpenAppUpdate={action('onOpenAppUpdate')} currentDateFormat=" " + isUpdatePostponed={false} />
)) @@ -157,33 +55,56 @@ storiesOf('News|NewsFeed', module) onNewsItemActionClick={action('onNewsItemActionClick')} onClose={action('onClose')} news={new News.NewsCollection([])} - isNewsFeedOpen={boolean('isNewsFeedOpen2', true)} + isNewsFeedOpen={boolean('isNewsFeedOpen', true)} onOpenExternalLink={action('onOpenExternalLink')} onOpenAlert={action('onOpenAlert')} onProceedNewsAction={action('onOpenExternalLink')} + onOpenAppUpdate={action('onOpenAppUpdate')} currentDateFormat=" " + isUpdatePostponed={false} />
)) - .add('Fetched', () => ( -
- -
- )); + .add('Fetched', ({ locale }: { locale: string }) => { + const displayAppUpdateNewsItem = boolean('displayAppUpdateNewsItem', true); + const updateDownloadProgress = displayAppUpdateNewsItem + ? number('updateDownloadProgress', 30, updateDownloadProgressOptions) + : 0; + const news = new News.NewsCollection([ + getNewsItem(1, 'incident', locale), + getNewsItem(2, 'incident', locale, true), + getNewsItem(3, 'alert', locale), + getNewsItem(4, 'alert', locale, true), + getNewsItem(5, 'announcement', locale), + getNewsItem(6, 'announcement', locale, true), + getNewsItem(7, 'info', locale), + getNewsItem(8, 'info', locale, true), + getNewsItem(9, 'software-update', locale, true), + ]); + return ( +
+ +
+ ); + }); diff --git a/storybook/stories/news/_utils/fakeDataNewsFeed.js b/storybook/stories/news/_utils/fakeDataNewsFeed.js new file mode 100644 index 0000000000..770cc389f3 --- /dev/null +++ b/storybook/stories/news/_utils/fakeDataNewsFeed.js @@ -0,0 +1,31 @@ +// @flow +import { version } from '../../../../package.json'; +import News from '../../../../source/renderer/app/domains/News'; +import { update } from './fakeDataUpdate'; +import type { NewsType } from '../../../../source/renderer/app/api/news/types'; + +export const getNewsItem = ( + id: number, + type: NewsType, + locale: string, + read?: boolean +): News.News => + new News.News({ + id, + title: + type === 'software-update' + ? update[locale].title + : `Title - ${type} - ${locale} - ${read ? 'read' : 'unread'}`, + content: + type === 'software-update' + ? update[locale].content + : `Content - ${locale}`, + target: { daedalusVersion: version, platform: 'darwin' }, + action: { + label: 'Visit en-US', + url: 'https://iohk.zendesk.com/hc/en-us/articles/', + }, + date: new Date().getTime() - 100 - id, + type, + read: read || false, + }); diff --git a/storybook/stories/news/_utils/fakeDataUpdate.js b/storybook/stories/news/_utils/fakeDataUpdate.js new file mode 100644 index 0000000000..f0a41fb08c --- /dev/null +++ b/storybook/stories/news/_utils/fakeDataUpdate.js @@ -0,0 +1,112 @@ +// @flow +import inc from 'semver/functions/inc'; +import { version as currentVersion } from '../../../../package.json'; +import News from '../../../../source/renderer/app/domains/News'; +import type { NewsItem } from '../../../../source/renderer/app/api/news/types'; + +export const version = currentVersion; +export const availableAppVersion = inc(version, 'minor'); + +const commonUpdateData = { + target: { + daedalusVersion: version, + platforms: ['win32', 'linux', 'darwin'], + }, + date: 1594043606135, + softwareUpdate: { + darwin: { + version: availableAppVersion, + hash: '97d336d45b022b0390446497dbe8b43bb6174436df12d43c4fc2b953ce22b703', + url: + 'https://update-cardano-mainnet.iohk.io/daedalus-2.0.0-mainnet-13980.pkg', + }, + win32: { + version: availableAppVersion, + hash: '97d336d45b022b0390446497dbe8b43bb6174436df12d43c4fc2b953ce22b703', + url: + 'https://update-cardano-mainnet.iohk.io/daedalus-2.0.0-mainnet-13980.exe', + }, + linux: { + version: availableAppVersion, + hash: '97d336d45b022b0390446497dbe8b43bb6174436df12d43c4fc2b953ce22b703', + url: + 'https://update-cardano-mainnet.iohk.io/daedalus-2.0.0-mainnet-13980.bin', + }, + }, + type: 'software-update', + id: 'dswkljhfksdhfksdhf', +}; + +export const updateEN = { + title: `Daedalus ${availableAppVersion} update`, + content: + 'This release brings **cool new features** and *some bug fixes*. It provides a more seamless experience. \n\nYou can find more information in the [release notes](https://daedalus.io).', + action: { + label: 'Download Daedalus at daedalus.io', + url: 'https://daedalus.io', + }, + ...commonUpdateData, +}; + +export const updateJP = { + title: `Daedalus${availableAppVersion}アップデート`, + content: + 'このリリースでは、**クールな新機能**と*いくつかのバグ修正*が行われています。よりシームレスなエクスペリエンスを提供します。[リリースノート](https://daedalus.io)で詳細を確認できます。', + action: { + label: 'daedalus.ioからダウンロードする', + url: 'https://daedalus.io', + }, + ...commonUpdateData, +}; + +export const update = { + 'en-US': updateEN, + 'ja-JP': updateJP, +}; + +export const getNewsUpdateItem = ( + read?: boolean, + locale: string +): News.News => { + const date = new Date().getTime(); + return new News.News({ + id: date, + title: update[locale].title, + content: update[locale].content, + target: { daedalusVersion: version, platform: 'darwin' }, + action: { + label: 'Visit daedalus.io', + url: 'https://daedalus.io', + }, + date, + type: 'software-update', + read: read || false, + }); +}; + +export const newsFeedApiItemUpdate: NewsItem = { + title: { + 'en-US': updateEN.title, + 'ja-JP': updateJP.title, + }, + content: { + 'en-US': updateEN.content, + 'ja-JP': updateJP.content, + }, + target: { + daedalusVersion: version, + platform: 'linux', + }, + action: { + label: { + 'en-US': updateEN.action.label, + 'ja-JP': updateJP.action.label, + }, + url: { + 'en-US': updateEN.action.url, + 'ja-JP': updateJP.action.url, + }, + }, + date: 1571901607418, + type: 'software-update', +}; diff --git a/storybook/stories/nodes/environment/TopBarEnvironment.stories.js b/storybook/stories/nodes/environment/TopBarEnvironment.stories.js index 37ff9a890d..2d8ae5ec42 100644 --- a/storybook/stories/nodes/environment/TopBarEnvironment.stories.js +++ b/storybook/stories/nodes/environment/TopBarEnvironment.stories.js @@ -29,7 +29,8 @@ const topBarTestEnv = currentTheme => ( /> ); @@ -51,7 +52,8 @@ const topBarItnEnv = currentTheme => ( /> ); @@ -67,7 +69,8 @@ const topBarProductionEnv = currentTheme => ( ); diff --git a/storybook/stories/nodes/syncing/SyncingConnecting.stories.js b/storybook/stories/nodes/syncing/SyncingConnecting.stories.js index 3ad51ae1c9..011fd0c02e 100644 --- a/storybook/stories/nodes/syncing/SyncingConnecting.stories.js +++ b/storybook/stories/nodes/syncing/SyncingConnecting.stories.js @@ -12,6 +12,8 @@ export const DefaultSyncingConnectingStory = (props: { currentTheme: string, }) => ( ( ( ( - -); - -export const AutoUpdateWithoutVersion = () => ( - -); diff --git a/storybook/stories/nodes/updates/ManualUpdate.stories.js b/storybook/stories/nodes/updates/ManualUpdate.stories.js deleted file mode 100644 index ec09e03b7d..0000000000 --- a/storybook/stories/nodes/updates/ManualUpdate.stories.js +++ /dev/null @@ -1,12 +0,0 @@ -// @flow -import React from 'react'; -import { action } from '@storybook/addon-actions'; -import ManualUpdate from '../../../../source/renderer/app/components/loading/manual-update/ManualUpdate'; - -export const ManualUpdateStory = () => ( - -); diff --git a/storybook/stories/nodes/updates/Updates.stories.js b/storybook/stories/nodes/updates/Updates.stories.js index 33c871b376..141b19e01e 100644 --- a/storybook/stories/nodes/updates/Updates.stories.js +++ b/storybook/stories/nodes/updates/Updates.stories.js @@ -7,11 +7,6 @@ import { withKnobs } from '@storybook/addon-knobs'; import StoryDecorator from '../../_support/StoryDecorator'; // Stories -import { ManualUpdateStory } from './ManualUpdate.stories'; -import { - AutoUpdateWithVerison, - AutoUpdateWithoutVersion, -} from './AutomaticUpdate.stories'; import { DataLayerMigrationStory } from './DataLayerMigration.stories'; storiesOf('Nodes|Updates', module) @@ -19,7 +14,4 @@ storiesOf('Nodes|Updates', module) {withKnobs(story, context)} )) // ====== Stories ====== - .add('Manual Update', ManualUpdateStory) - .add('Automatic Update With Version', AutoUpdateWithVerison) - .add('Automatic Update Without Version', AutoUpdateWithoutVersion) .add('Data Layer Migration', DataLayerMigrationStory); diff --git a/storybook/stories/wallets/settings/WalletSettingsScreen.stories.js b/storybook/stories/wallets/settings/WalletSettingsScreen.stories.js index bc0e6a18bc..9eba3e41ed 100644 --- a/storybook/stories/wallets/settings/WalletSettingsScreen.stories.js +++ b/storybook/stories/wallets/settings/WalletSettingsScreen.stories.js @@ -4,6 +4,7 @@ import { text, boolean, number, select } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; import moment from 'moment'; import { isIncentivizedTestnetTheme } from '../../_support/utils'; +import type { Locale } from '../../../../source/common/types/locales.types'; // Screens import WalletSettings from '../../../../source/renderer/app/components/wallet/settings/WalletSettings'; @@ -81,7 +82,7 @@ const getWalletDates = (type: string, status: string) => { }; }; -export default (props: { currentTheme: string, locale: string }) => { +export default (props: { currentTheme: string, locale: Locale }) => { const { currentTheme, locale } = props; const { type, status } = select( diff --git a/tests/app/e2e/features/app-update.feature b/tests/app/e2e/features/app-update.feature new file mode 100644 index 0000000000..42d7988cd2 --- /dev/null +++ b/tests/app/e2e/features/app-update.feature @@ -0,0 +1,83 @@ +@e2e @skip +Feature: App Update + + Scenario: 2.2.0 not installed, download not started + Given I open the 2.1.0 build + And the download was not started + Then I should see the green dot in the notification bell + When I click the bell + Then I should see the download progress as a first Newsfeed item + When I click the Newsfeed item + Then I should see the Update Overlay + And the overlay should display the download progress + And the overlay should display a close button + And the overlay should close when clicking on any area + + Scenario: 2.2.0 not installed, download was interrupted + Given I open the 2.1.0 build + And the download was not started + Then I should see the green dot in the notification bell + When I click the bell + Then I should see the download progress as a first Newsfeed item + When I close and reopen the app + Then I should see the green dot in the notification bell + When I click the bell + Then I should see the download progress as a first Newsfeed item + And the download should resume from the same percentage as it was before closing + + Scenario: 2.2.0 not installed, download is completed + Given I open the 2.1.0 build + And the download was finished with success + Then I should see the update overlay + And the overlay should not have a progress bar + And the overlay should have a checkbox and a button + When I click the checkbox and the button + Then the app should close + And the installer should open + + Scenario: 2.2.0 not installed, download is completed, app reopened + Given I open the 2.1.0 build + And the download was finished with success + Then I should see the update overlay + When I close and reopen the app + Then I should see the update overlay + And the overlay should have a link underneath the main button, for a manual installation + + Scenario: 2.2.0 not installed, download is interrupted, file is deleted + Given I open the 2.1.0 build + And the download was not started + Then I should see the green dot in the notification bell + When I close the app + And delete the temporary file + And reopen the app + Then I should see the update overlay + And the overlay should display a red text saying "We were unable to launch the update installer automatically. Please manually update Daedalus to its latest version." + And the have a button for the manual update + When I close and reopen the app + Then I should still see the overlay with manual update info + + Scenario: 2.2.0 not installed, download is completed, file is deleted + Given I open the 2.1.0 build + And the download was finished with success + When I close the app + And delete the temporary file + And reopen the app + Then I should see the update overlay + When I click the checkbox and the button + Then I should see the overlay with manual update info + + Scenario: 2.2.0 is installed + Given I open the 2.0.0 build + Then I should not see the green dot in the beel + And I should not see the update item in the newsfeed + And I should not see the update overlay + + + + + + + + + + diff --git a/tests/app/e2e/features/node-update-notification.feature b/tests/app/e2e/features/node-update-notification.feature deleted file mode 100644 index ed27ff513c..0000000000 --- a/tests/app/e2e/features/node-update-notification.feature +++ /dev/null @@ -1,35 +0,0 @@ -@e2e @skip -# @API TODO - we don't have API endpoint for app update check -Feature: Node Update Notification - - Background: - Given I have completed the basic setup - - Scenario: Application version and next update version not match - When I set next update version to "10" - And I set next application version to "15" - Then I should see the node update notification overlay - And Overlay should display "a newer version" as available version and actions - - Scenario: Application version and next update version match - When I set next application version to "15" - And I set next update version to "15" - Then I should see the node update notification overlay - And Overlay should display "0.14.0" as available version and actions - - Scenario: User postpones a node update notification - When I set next application version to "15" - And I set next update version to "15" - Then I should see the node update notification overlay - And Overlay should display "0.14.0" as available version and actions - When I click the postpone update button - Then I should not see the notification component anymore - - @restartApp - Scenario: User accepts a node update notification - When I set next update version to "15" - And I set next application version to "15" - Then I should see the node update notification overlay - And Overlay should display "0.14.0" as available version and actions - When I click the accept update button - Then Daedalus should quit diff --git a/tests/app/e2e/steps/app-version-difference.js b/tests/app/e2e/steps/app-version-difference.js index 0f3bc128f5..cd1704b267 100644 --- a/tests/app/e2e/steps/app-version-difference.js +++ b/tests/app/e2e/steps/app-version-difference.js @@ -16,15 +16,15 @@ const nextAppVersion = [ currentAppVersionChunks[2], ].join('.'); const SELECTORS = { - DESCRIPTION: 'manualUpdate.description2', + DESCRIPTION: 'appUpdate.manualUpdateOverlay.description2', OVERLAY: '.ManualUpdate_content', VERSION_INFO: '.ManualUpdate_content .ManualUpdate_description p:nth-child(2)', }; Given(/^There is a newer application version available$/, async function() { - await this.client.execute(version => { - daedalus.api.ada.setLatestAppVersion(version); - }, nextAppVersion); + // await this.client.execute(version => { + // daedalus.api.ada.setLatestAppVersion(version); + // }, nextAppVersion); }); Then(/^I should see the "Manual Update" overlay$/, function() { @@ -54,12 +54,12 @@ Then( ); When(/^I trigger the apply-update endpoint$/, async function() { - await this.client.executeAsync(done => { - daedalus.api.ada - .applyUpdate() - .then(done) - .catch(e => { - throw e; - }); - }); + // await this.client.executeAsync(done => { + // daedalus.api.ada + // .applyUpdate() + // .then(done) + // .catch(e => { + // throw e; + // }); + // }); }); diff --git a/tests/app/e2e/steps/node-update-notification.js b/tests/app/e2e/steps/node-update-notification.js deleted file mode 100644 index 11c599d7eb..0000000000 --- a/tests/app/e2e/steps/node-update-notification.js +++ /dev/null @@ -1,78 +0,0 @@ -// @flow -import { When, Then } from 'cucumber'; -import { expect } from 'chai'; -import { environment } from '../../../../source/main/environment'; -import { getVisibleTextsForSelector } from '../../../common/e2e/steps/helpers'; -import type { Daedalus } from '../../../types'; - -declare var daedalus: Daedalus; - -const currentAppVersion = environment.version; - -const SELECTORS = { - currentAppVersionInfo: - '.AutomaticUpdateNotification_description p span b:nth-child(1)', - newAppVersionInfo: - '.AutomaticUpdateNotification_description p span b:nth-child(2)', - acceptButton: '.AutomaticUpdateNotification_acceptButton', - postponeButton: '.AutomaticUpdateNotification_postponeButton', - appUpdateOverlay: '.AutomaticUpdateNotification_dialog', - appUpdateComponent: '.AutomaticUpdateNotification_overlay', -}; - -Then('I should see the node update notification overlay', async function() { - return this.client.waitForVisible(SELECTORS.appUpdateOverlay); -}); - -When(/^I set next update version to "([^"]*)"$/, async function(applicationVersion) { - await this.client.executeAsync((applicationVersion, done) => { - daedalus.api.ada - .setNextUpdate(parseInt(applicationVersion, 10)) - .then(done) - .catch(e => { - throw e; - }); - }, applicationVersion); -}); - -When( - /^Overlay should display "([^"]*)" as available version and actions$/, - async function(nextVersion) { - const [newAppVersionInfo] = await getVisibleTextsForSelector( - this.client, - SELECTORS.newAppVersionInfo - ); - const [currentAppVersionInfo] = await getVisibleTextsForSelector( - this.client, - SELECTORS.currentAppVersionInfo - ); - expect(newAppVersionInfo.replace('v ', '')).to.equal(nextVersion); - expect(currentAppVersionInfo.replace('v ', '')).to.equal(currentAppVersion); - this.client.waitForVisible(SELECTORS.acceptButton); - this.client.waitForVisible(SELECTORS.postponeButton); - } -); - -When(/^I set next application version to "([^"]*)"$/, async function( - applicationVersion -) { - await this.client.execute(version => { - daedalus.api.ada.setApplicationVersion(parseInt(version, 10)); - }, applicationVersion); -}); - -When(/^I click the postpone update button$/, function() { - return this.waitAndClick(SELECTORS.postponeButton); -}); - -When(/^I click the accept update button$/, function() { - return this.waitAndClick(SELECTORS.acceptButton); -}); - -Then(/^I should not see the notification component anymore$/, function() { - return this.client.waitForVisible( - SELECTORS.appUpdateComponent, - null, - true - ); -}); diff --git a/yarn.lock b/yarn.lock index 18152d7d40..9129b951ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8625,6 +8625,12 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" +json-stable-stringify@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" + dependencies: + jsonify "~0.0.0" + json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -12621,7 +12627,7 @@ setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" -sha.js@^2.4.0, sha.js@^2.4.8: +sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" dependencies: @@ -12657,6 +12663,13 @@ shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" +shasum@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" + dependencies: + json-stable-stringify "~0.0.0" + sha.js "~2.4.4" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" From 7a14db15e7986e4eca8e18f6172b9ed2f427a95f Mon Sep 17 00:00:00 2001 From: Nikola Glumac Date: Fri, 25 Sep 2020 10:22:51 +0200 Subject: [PATCH 3/3] [DDW-395] Update WBE (#2188) * [DDW-395] Update WBE * fix case sensitive filename Co-authored-by: Michael Bishop --- CHANGELOG.md | 1 + nix/sources.json | 6 +++--- .../config/{appupdateConfig.js => appUpdateConfig.js} | 0 3 files changed, 4 insertions(+), 3 deletions(-) rename source/common/config/{appupdateConfig.js => appUpdateConfig.js} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0aaf02a15..6cb63e36da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Changelog ### Chores +- Updated `cardano-wallet` to version `2020-09-22` ([PR 2188](https://github.com/input-output-hk/daedalus/pull/2188)) - Updated `cardano-wallet` to revision `ffeca1d9` which includes `cardano-node` 1.20.0 ([PR 2187](https://github.com/input-output-hk/daedalus/pull/2187)) - Set "Delegation center" as default staking screen ([PR 2185](https://github.com/input-output-hk/daedalus/pull/2185)) - Changed the ordering of wallet addresses on the "Receive" screen so that the oldest one are on top and the newest one on the bottom of the list ([PR 2176](https://github.com/input-output-hk/daedalus/pull/2176)) diff --git a/nix/sources.json b/nix/sources.json index ce8d89e70f..8b33d5b5e3 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -29,10 +29,10 @@ "homepage": null, "owner": "input-output-hk", "repo": "cardano-wallet", - "rev": "ffeca1d9e0f302111588c7890785051b6cc6b0da", - "sha256": "0hmlv9sfqxhwf0syrfkvqcn6zndkk0s7z47fixfji62llzvhk9zl", + "rev": "e3145b822d4ece10f98039e3b5b535d9e43d190a", + "sha256": "1nfzfdv03nz2bhin2jflxsmh597vkjifb76jsdg0384x89gi0a10", "type": "tarball", - "url": "https://github.com/input-output-hk/cardano-wallet/archive/ffeca1d9e0f302111588c7890785051b6cc6b0da.tar.gz", + "url": "https://github.com/input-output-hk/cardano-wallet/archive/e3145b822d4ece10f98039e3b5b535d9e43d190a.tar.gz", "url_template": "https://github.com///archive/.tar.gz", "version": "v2020-04-28" }, diff --git a/source/common/config/appupdateConfig.js b/source/common/config/appUpdateConfig.js similarity index 100% rename from source/common/config/appupdateConfig.js rename to source/common/config/appUpdateConfig.js