diff --git a/app/actions/DaemonActions.js b/app/actions/DaemonActions.js index 764fef237d..abae592043 100644 --- a/app/actions/DaemonActions.js +++ b/app/actions/DaemonActions.js @@ -28,6 +28,7 @@ import { STANDARD_EXTERNAL_REQUESTS } from "constants"; import { DIFF_CONNECTION_ERROR, LOCALE, TESTNET } from "constants"; import * as cfgConstants from "constants/config"; import { CSPP_URL, CSPP_URL_LEGACY } from "constants"; +import { preDefinedGradients } from "helpers"; export const DECREDITON_VERSION = "DECREDITON_VERSION"; export const SELECT_LANGUAGE = "SELECT_LANGUAGE"; @@ -770,3 +771,42 @@ export const getDexLogs = () => (dispatch, getState) => .then((logs) => resolve(logs)) .catch((err) => reject(err)); }); + +export const generateRandomGradient = () => { + const randomColor = Math.floor(Math.random() * 16777215).toString(16); + const invertedColor = (Number(`0x1${randomColor}`) ^ 0xffffff) + .toString(16) + .substr(1); + return `linear-gradient(#${randomColor} 0%, #${invertedColor} 100%)`; +}; + +export const checkDisplayWalletGradients = (availableWallets) => ( + dispatch, + getState +) => { + const missingGradientWallets = []; + let availableGradients = [...preDefinedGradients]; + availableWallets + .sort((a, b) => b.lastAccess - a.lastAccess) + .forEach(({ wallet, displayWalletGradient }) => { + if (!displayWalletGradient) { + missingGradientWallets.push(wallet); + } else { + availableGradients = availableGradients.filter( + (gradient) => gradient != displayWalletGradient + ); + } + }); + + // set missing gradients + if (missingGradientWallets.length > 0) { + availableGradients.reverse(); + missingGradientWallets.forEach((walletName) => { + const gradient = availableGradients.pop() ?? generateRandomGradient(); + const config = wallet.getWalletCfg(isTestNet(getState()), walletName); + config.set(cfgConstants.DISPLAY_WALLET_GRADIENT, gradient); + }); + + dispatch(getAvailableWallets()); + } +}; diff --git a/app/actions/SettingsActions.js b/app/actions/SettingsActions.js index e02eefc958..e76bee83ec 100644 --- a/app/actions/SettingsActions.js +++ b/app/actions/SettingsActions.js @@ -52,6 +52,10 @@ export const saveSettings = (settings) => async (dispatch, getState) => { config.set(configConstants.NETWORK, settings.network); config.set(configConstants.THEME, settings.theme); config.set(configConstants.UI_ANIMATIONS, settings.uiAnimations); + config.set( + configConstants.AUTO_WALLET_LAUNCHING, + settings.autoWalletLaunching + ); if (walletName) { const walletConfig = wallet.getWalletCfg(isTestNet(getState()), walletName); @@ -62,7 +66,10 @@ export const saveSettings = (settings) => async (dispatch, getState) => { } if ( - !equalElements(oldAllowedExternalRequests, settings.allowedExternalRequests) + !equalElements( + oldAllowedExternalRequests ?? [], + settings.allowedExternalRequests + ) ) { wallet.reloadAllowedExternalRequests(); } diff --git a/app/actions/WalletLoaderActions.js b/app/actions/WalletLoaderActions.js index e240375272..84c250c01c 100644 --- a/app/actions/WalletLoaderActions.js +++ b/app/actions/WalletLoaderActions.js @@ -28,6 +28,7 @@ import * as cfgConstants from "constants/config"; import { RESCAN_PROGRESS } from "./ControlActions"; import { stopAccountMixer } from "./AccountMixerActions"; import { TRZ_WALLET_CLOSED } from "actions/TrezorActions"; +import { saveSettings, updateStateSettingsChanged } from "./SettingsActions"; const { SyncNotificationType } = api; @@ -717,3 +718,25 @@ export const acceptStakingWarning = () => (dispatch, getState) => { showStakingWarning: false }); }; + +export const STOP_UNFINISHED_WALLET_FAILED = "STOP_UNFINISHED_WALLET_FAILED"; +export const stopUnfinishedWallet = () => async (dispatch) => { + try { + await wallet.stopWallet(); + dispatch(setSelectedWallet(null)); + } catch (err) { + dispatch({ error: err, type: STOP_UNFINISHED_WALLET_FAILED }); + } +}; + +export const setAutoWalletLaunching = (autoWalletLaunching) => async ( + dispatch, + getState +) => { + dispatch(updateStateSettingsChanged({ autoWalletLaunching }, true)); + const tempSettings = getState().settings.tempSettings; + const config = wallet.getGlobalCfg(); + config.set(cfgConstants.AUTO_WALLET_LAUNCHING, autoWalletLaunching); + + await dispatch(saveSettings(tempSettings)); +}; diff --git a/app/components/indicators/AnimatedLinearProgressFull/AnimatedLinearProgressFull.jsx b/app/components/indicators/AnimatedLinearProgressFull/AnimatedLinearProgressFull.jsx new file mode 100644 index 0000000000..91680ae036 --- /dev/null +++ b/app/components/indicators/AnimatedLinearProgressFull/AnimatedLinearProgressFull.jsx @@ -0,0 +1,233 @@ +import { useState, useCallback } from "react"; +import { useDaemonStartup, useMountEffect } from "hooks"; +import { HeaderTimeMsg } from "views/GetStartedPage/messages"; +import { FormattedRelative } from "shared"; +import { FormattedMessage as T } from "react-intl"; +import ReactTimeout from "react-timeout"; +import { classNames, Tooltip } from "pi-ui"; +import styles from "./AnimatedLinearProgressFull.module.css"; +import { useRescan } from "hooks"; +import { KeyBlueButton } from "buttons"; +import { AutoWalletLaunchingModal } from "modals"; + +const AnimatedLinearProgressFull = ({ + setInterval, + min, + error, + text, + animationType, + initialAnimationType, + hideTextBlock, + onCancelLoadingWallet, + onContinueOpeningWallet, + onSaveAndContinueOpeningWallet, + nextStateAfterWalletLoading, + hideOpenWalletButton +}) => { + const { + isSPV, + getNeededBlocks, + getEstimatedTimeLeft, + selectedWalletSelector, + getCurrentBlockCount, + syncFetchHeadersLastHeaderTime, + syncFetchHeadersFirstHeaderTime, + getDcrwalletLogs, + getDaemonSynced, + onGetDcrdLogs, + syncRescanAttempt, + startWalletServiceAttempt, + synced + } = useDaemonStartup(); + const { rescanEndBlock, rescanStartBlock, rescanCurrentBlock } = useRescan(); + const [lastDcrwalletLogLine, setLogLine] = useState(""); + const [lastDcrdLogLine, setDcrdLogLine] = useState(""); + const [ + isAutoWalletLaunchingModalVisible, + setIsAutoWalletLaunchingModalVisible + ] = useState(false); + const showAutoWalletLaunchingModalVisible = useCallback( + () => setIsAutoWalletLaunchingModalVisible(true), + [setIsAutoWalletLaunchingModalVisible] + ); + + useMountEffect(() => { + setInterval(async () => { + try { + const lastDcrwalletLogLine = await getDcrwalletLogs(); + setLogLine(lastDcrwalletLogLine); + } catch (err) { + console.log(err); + } + }, 2000); + setInterval(async () => { + try { + const lastDcrdLogLine = await onGetDcrdLogs(); + setDcrdLogLine(lastDcrdLogLine); + } catch (err) { + console.log(err); + } + }, 2000); + }, [setInterval, getDcrwalletLogs, onGetDcrdLogs]); + + const now = Date.now(); + let perComplete; + if (!getDaemonSynced && getCurrentBlockCount) { + perComplete = (getCurrentBlockCount - min) / (getNeededBlocks - min); + } else if (syncRescanAttempt && rescanCurrentBlock) { + perComplete = + (rescanCurrentBlock - rescanStartBlock) / + (rescanEndBlock - rescanStartBlock); + } else if (syncFetchHeadersLastHeaderTime) { + const syncFetchHeadersLastHeaderTimeTs = new Date( + syncFetchHeadersLastHeaderTime + ).getTime(); + const syncFetchHeadersFirstHeaderTimeTs = new Date( + syncFetchHeadersFirstHeaderTime + ).getTime(); + perComplete = + (syncFetchHeadersLastHeaderTimeTs - syncFetchHeadersFirstHeaderTimeTs) / + (now - syncFetchHeadersFirstHeaderTimeTs); + } + + const leftStartingPoint = perComplete ? perComplete * 100 : 0; + let finishDateEstimation = null; + if (getEstimatedTimeLeft !== null) { + finishDateEstimation = new Date(); + finishDateEstimation.setSeconds( + finishDateEstimation.getSeconds() + getEstimatedTimeLeft + ); + } + + return ( +
+
+ {perComplete > 0 && ( +
+ )} +
+ {selectedWalletSelector?.value?.wallet && + `${selectedWalletSelector.value.wallet} - `} + {text} +
+ {getCurrentBlockCount && !getDaemonSynced && ( +
+ + + {finishDateEstimation && ( + + )} + ({getCurrentBlockCount} / {getNeededBlocks}) + +
+ )} + {syncRescanAttempt && rescanCurrentBlock ? ( +
+ rescanStartBlock + ? rescanCurrentBlock + : rescanStartBlock, + rescanEndBlock: rescanEndBlock + }} + /> +
+ ) : startWalletServiceAttempt ? ( +
+ +
+ ) : ( + selectedWalletSelector && + !synced && + syncFetchHeadersLastHeaderTime && ( +
+ + + + +
+ ) + )} +
+
+ {isSPV && ( +
+ }> +
+ +
+ )} +
+
+
+ {!hideOpenWalletButton && + selectedWalletSelector && + (nextStateAfterWalletLoading ? ( + <> + + + + + + ) : ( + !startWalletServiceAttempt && ( + + + + ) + ))} +
+ {!hideTextBlock && ( +
+ {!getDaemonSynced && lastDcrdLogLine && !selectedWalletSelector && ( +
+
{lastDcrdLogLine}
+
+ )} + {selectedWalletSelector && lastDcrwalletLogLine && ( +
+
{lastDcrwalletLogLine}
+
+ )} +
+ )} +
+ ); +}; + +export default ReactTimeout(AnimatedLinearProgressFull); diff --git a/app/components/indicators/AnimatedLinearProgressFull/AnimatedLinearProgressFull.module.css b/app/components/indicators/AnimatedLinearProgressFull/AnimatedLinearProgressFull.module.css new file mode 100644 index 0000000000..821e8cb274 --- /dev/null +++ b/app/components/indicators/AnimatedLinearProgressFull/AnimatedLinearProgressFull.module.css @@ -0,0 +1,98 @@ +.linearProgress { + height: 9.4rem; + width: 100%; + background-color: var(--loader-bg); + position: relative; + display: grid; + + grid-template-columns: max-content auto; + padding: 1.7rem 4rem 2.2rem 4rem; + color: var(--main-dark-blue); + z-index: 9999; +} + +.linearProgressBar { + top: 0; + left: 0; + background: linear-gradient( + 270deg, + var(--display-wallet-gradient-selected-right), + var(--input-color) + ); + border-radius: 0 0.25rem 0.25rem 0; + height: 0.5rem; + box-shadow: 0px 5px 13px var(--input-color-blue-shadow); + transition: width 0.3s linear 0ms; + position: absolute; +} + +.linearProgressBar.error { + background-color: var(--error-text-color); +} + +.linearProgressText { + font-family: var(--font-family-regular); + + font-size: 1.1rem; + font-weight: 700; + line-height: 1.4rem; +} + +.icons { + display: flex; + justify-content: flex-end; +} + +.icon { + background-image: var(--blockchain-default); + background-repeat: no-repeat; + background-size: 3rem; + width: 3rem; + height: 3rem; +} + +.icon.initial { + background-image: var(--blockchain-initial); + background-size: 2.5rem; +} + +.loaderBarEstimation { + font-size: 1.1rem; + font-weight: 400; + line-height: 1.4rem; +} + +.loaderBarEstimation .bold { + font-weight: 700; +} + +.lastLogLines { + font-size: 0.9rem; + text-align: right; + margin-top: 0.5rem; +} + +.openWalletButton, +.cancelLoadingButton { + line-height: 1.6rem !important; + padding: 0.6rem 1rem !important; + margin-top: 0.5rem; +} + +.cancelLoadingButton { + background-color: var(--grey-7) !important; +} + +.spvIcon { + background-image: var(--menu-spvon-icon); + height: 3rem; + width: 3rem; + background-size: 3rem; + margin-right: 1.5rem; +} + +@media screen and (max-width: 768px) { + .loaderBarEstimation { + display: none; + } +} diff --git a/app/components/indicators/AnimatedLinearProgressFull/index.js b/app/components/indicators/AnimatedLinearProgressFull/index.js new file mode 100644 index 0000000000..4262b5ecfb --- /dev/null +++ b/app/components/indicators/AnimatedLinearProgressFull/index.js @@ -0,0 +1 @@ +export { default } from "./AnimatedLinearProgressFull"; diff --git a/app/components/indicators/LinearProgress/AnimatedLinearProgressFull.jsx b/app/components/indicators/LinearProgress/AnimatedLinearProgressFull.jsx deleted file mode 100644 index 0246a4d014..0000000000 --- a/app/components/indicators/LinearProgress/AnimatedLinearProgressFull.jsx +++ /dev/null @@ -1,182 +0,0 @@ -import { useState } from "react"; -import { useDaemonStartup, useMountEffect } from "hooks"; -import { HeaderTimeMsg } from "views/GetStartedPage/messages"; -import { FormattedRelative } from "shared"; -import { FormattedMessage as T } from "react-intl"; -import ReactTimeout from "react-timeout"; -import { classNames } from "pi-ui"; -import styles from "./LinearProgress.module.css"; - -const AnimatedLinearProgressFull = ({ - setInterval, - min, - error, - text, - animationType, - initialAnimationType, - hideTextBlock -}) => { - const { - isSPV, - getNeededBlocks, - getEstimatedTimeLeft, - selectedWalletSelector, - getCurrentBlockCount, - syncFetchHeadersLastHeaderTime, - getDcrwalletLogs, - getDaemonSynced, - onGetDcrdLogs - } = useDaemonStartup(); - const [lastDcrwalletLogLine, setLogLine] = useState(""); - const [lastDcrdLogLine, setDcrdLogLine] = useState(""); - - useMountEffect(() => { - setInterval(async () => { - try { - const lastDcrwalletLogLine = await getDcrwalletLogs(); - setLogLine(lastDcrwalletLogLine); - } catch (err) { - console.log(err); - } - }, 2000); - setInterval(async () => { - try { - const lastDcrdLogLine = await onGetDcrdLogs(); - setDcrdLogLine(lastDcrdLogLine); - } catch (err) { - console.log(err); - } - }, 2000); - }, [setInterval, getDcrwalletLogs, onGetDcrdLogs]); - - const perComplete = (getCurrentBlockCount - min) / (getNeededBlocks - min); - const leftStartingPoint = perComplete ? perComplete * 100 : 0; - let finishDateEstimation = null; - if (getEstimatedTimeLeft !== null) { - finishDateEstimation = new Date(); - finishDateEstimation.setSeconds( - finishDateEstimation.getSeconds() + getEstimatedTimeLeft - ); - } - return ( - <> -
- {getDaemonSynced || isSPV ? ( - <> -
-
- {text} -
- - ) : ( - <> -
-
- {perComplete > 0.1 && perComplete < 1 && ( -
- )} - {perComplete > 0.25 && perComplete < 1 && ( -
- )} - {perComplete > 0.4 && perComplete < 1 && ( -
- )} - {perComplete > 0.6 && perComplete < 1 && ( -
- )} - {perComplete > 0.75 && perComplete < 1 && ( -
- )} - {perComplete > 0.9 && perComplete < 1 && ( -
- )} -
-
- {text} -
- - )} -
- {!hideTextBlock && ( -
- {getCurrentBlockCount && !getDaemonSynced && ( -
- - - {finishDateEstimation && ( - - )} - ({getCurrentBlockCount} / {getNeededBlocks}) - -
- )} - {selectedWalletSelector && syncFetchHeadersLastHeaderTime && ( -
- - - - -
- )} - {!getDaemonSynced && lastDcrdLogLine && !selectedWalletSelector && ( -
-
{lastDcrdLogLine}
-
- )} - {selectedWalletSelector && lastDcrwalletLogLine && ( -
-
{lastDcrwalletLogLine}
-
- )} -
- )} - - ); -}; - -export default ReactTimeout(AnimatedLinearProgressFull); diff --git a/app/components/indicators/LinearProgress/LinearProgress.module.css b/app/components/indicators/LinearProgress/LinearProgress.module.css index 8ac76f32b2..6c4bfa8fb1 100644 --- a/app/components/indicators/LinearProgress/LinearProgress.module.css +++ b/app/components/indicators/LinearProgress/LinearProgress.module.css @@ -26,88 +26,3 @@ .linearProgressBar.error { background-color: var(--error-text-color); } -@keyframes slide { - 0% { - opacity: 0.01; - } - - 25% { - opacity: 0.08; - } - - 75% { - opacity: 0.08; - } - - 100% { - left: 59px; - } -} -.linearProgressBox { - position: absolute; - margin-top: 7px; - height: 41px; - width: 41px; - border-radius: 3px; - opacity: 0; - background-color: var(--background-back-color); - animation: slide 3s cubic-bezier(0.46, 0.03, 0.52, 0.96) infinite; -} -.linearProgressBox.one { - animation-delay: 0s; -} -.linearProgressBox.two { - animation-delay: 0.5s; -} -.linearProgressBox.three { - animation-delay: 1s; -} -.linearProgressBox.four { - animation-delay: 1.5s; -} -.linearProgressBox.five { - animation-delay: 2s; -} -.linearProgressBox.six { - animation-delay: 2.5s; -} -.linearProgressText { - height: 100%; - text-align: left; - color: var(--linear-progress-text-default); - font-size: 11px; - font-weight: 600; - padding-left: 46px; - padding-top: 19px; - font-family: var(--font-family-regular); - background-image: var(--blockchain-default); - background-repeat: no-repeat; - background-position: 20px 20px; - background-size: 16px; - position: absolute; - width: 100%; -} -.linearProgressText.initial { - color: var(--linear-progress-text-initial); - background-image: var(--blockchain-initial); -} -.isRow { - display: flex; - flex-direction: row; -} -.loaderBarEstimation { - text-align: left; - font-size: 9px; -} -.loaderBarEstimation .bold { - opacity: 1; - font-weight: 700; -} -.lastLogLines { - font-size: 9px; -} -@media (--sm-viewport) { - .loaderBarEstimation { - display: none; - } -} diff --git a/app/components/indicators/index.js b/app/components/indicators/index.js index 372a71050e..e48f9504da 100644 --- a/app/components/indicators/index.js +++ b/app/components/indicators/index.js @@ -8,7 +8,7 @@ export { default as NoTicketsIndicator } from "./NoMoreIndicators/NoTickets"; export { default as DecredLoading } from "./DecredLoading/DecredLoading"; export { default as SimpleLoading } from "./SimpleLoading/SimpleLoading"; export { default as RescanProgress } from "./RescanProgress/RescanProgress"; -export { default as AnimatedLinearProgressFull } from "./LinearProgress/AnimatedLinearProgressFull"; +export { default as AnimatedLinearProgressFull } from "./AnimatedLinearProgressFull"; export { default as LinearProgressSmall } from "./LinearProgress/LinearProgressSmall"; export { default as LoaderBarBottom } from "./LoaderBarBottom"; export { default as NoStats } from "./NoMoreIndicators/NoStats"; diff --git a/app/components/modals/AutoWalletLaunchingModal/AutoWalletLaunchingModal.jsx b/app/components/modals/AutoWalletLaunchingModal/AutoWalletLaunchingModal.jsx new file mode 100644 index 0000000000..e3fc53dbd6 --- /dev/null +++ b/app/components/modals/AutoWalletLaunchingModal/AutoWalletLaunchingModal.jsx @@ -0,0 +1,49 @@ +import { FormattedMessage as T } from "react-intl"; +import Modal from "../Modal"; +import styles from "./AutoWalletLaunchingModal.module.css"; +import { PiUiButton, InvisiblePiUiButton } from "buttons"; +import { useState } from "react"; +import { Checkbox } from "pi-ui"; + +const AutoWalletLaunchingModal = ({ onCancelModal, onSubmit, show }) => { + const [autoOpenWallet, setAutoOpeningWallet] = useState(true); + return ( + +
+ +
+ + } + id="autostart" + description={ + + } + checked={autoOpenWallet} + onChange={() => setAutoOpeningWallet(!autoOpenWallet)} + /> +