diff --git a/package.json b/package.json index 1492ffbb6a..26b0f4305e 100644 --- a/package.json +++ b/package.json @@ -103,9 +103,12 @@ "@types/node": "14.18.1", "@types/qrcode.react": "1.0.2", "@types/react": "16.9.56", + "@types/react-router": "5.1.18", + "@types/react-router-dom": "5.3.3", "@types/react-svg-inline": "2.1.3", "@types/react-table": "^7.7.9", "@types/trezor-connect": "8.1.18", + "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.20.0", "@typescript-eslint/parser": "5.20.0", "@xarc/run": "1.1.1", @@ -291,6 +294,7 @@ "url": "0.11.0", "usb-detection": "4.13.0", "util": "0.12.4", + "uuid": "8.3.2", "validator": "13.7.0" }, "devEngines": { diff --git a/source/common/config/electron-store.config.ts b/source/common/config/electron-store.config.ts index 7455f7c4ff..0162bde678 100644 --- a/source/common/config/electron-store.config.ts +++ b/source/common/config/electron-store.config.ts @@ -23,6 +23,8 @@ export const STORAGE_KEYS: Record = { STAKE_POOLS_LIST_VIEW_TOOLTIP: 'STAKE-POOLS-LIST-VIEW-TOOLTIP', STAKING_INFO_WAS_OPEN: 'ALONZO-INFO-WAS-OPEN', TERMS_OF_USE_ACCEPTANCE: 'TERMS-OF-USE-ACCEPTANCE', + ANALYTICS_ACCEPTANCE: 'ANALYTICS-ACCEPTANCE', + USER_ID: 'USER-ID', THEME: 'THEME', TOKEN_FAVORITES: 'TOKEN-FAVORITES', USER_DATE_FORMAT_ENGLISH: 'USER-DATE-FORMAT-ENGLISH', diff --git a/source/common/ipc/constants.ts b/source/common/ipc/constants.ts index 75bdd93820..3fe6c407d5 100644 --- a/source/common/ipc/constants.ts +++ b/source/common/ipc/constants.ts @@ -1,4 +1,6 @@ -export const DIALOGS = { +import { ApplicationDialog } from '../../renderer/app/types/applicationDialogTypes'; + +export const DIALOGS: Record = { ABOUT: 'ABOUT_DIALOG', DAEDALUS_DIAGNOSTICS: 'DAEDALUS_DIAGNOSTICS_DIALOG', ITN_REWARDS_REDEMPTION: 'ITN_REWARDS_REDEMPTION_DIALOG', diff --git a/source/common/ipc/lib/IpcConversation.ts b/source/common/ipc/lib/IpcConversation.ts index 7d65e42d8c..e56335abfe 100644 --- a/source/common/ipc/lib/IpcConversation.ts +++ b/source/common/ipc/lib/IpcConversation.ts @@ -1,5 +1,5 @@ import { isString } from 'lodash'; -import uuid from 'uuid'; +import { v4 as uuidv4 } from 'uuid'; export type IpcSender = { send: (channel: string, conversationId: string, ...args: Array) => void; @@ -68,7 +68,7 @@ export class IpcConversation { receiver: IpcReceiver ): Promise { return new Promise((resolve, reject) => { - const conversationId = uuid(); + const conversationId = uuidv4(); const handler = ( event, diff --git a/source/common/types/electron-store.types.ts b/source/common/types/electron-store.types.ts index bb3481c833..e31266e8fa 100644 --- a/source/common/types/electron-store.types.ts +++ b/source/common/types/electron-store.types.ts @@ -26,6 +26,8 @@ export type StorageKey = | 'USER-TIME-FORMAT' | 'WALLET-MIGRATION-STATUS' | 'WALLETS' + | 'ANALYTICS-ACCEPTANCE' + | 'USER-ID' | 'WINDOW-BOUNDS'; export type StoreMessage = { type: StorageType; diff --git a/source/common/types/environment.types.ts b/source/common/types/environment.types.ts index 84504094ff..06fabda70e 100644 --- a/source/common/types/environment.types.ts +++ b/source/common/types/environment.types.ts @@ -23,7 +23,8 @@ export type Environment = { mainProcessID: string; rendererProcessID: string; os: string; - cpu: string; + system: string; + cpu: Cpu; ram: number; hasMetHardwareRequirements: boolean; installerVersion: string; @@ -33,6 +34,7 @@ export type Environment = { isLinux: boolean; isBlankScreenFixActive: boolean; keepLocalClusterRunning: boolean; + analyticsFeatureEnabled: boolean; }; // constants export const PRODUCTION = 'production'; diff --git a/source/main/environment.ts b/source/main/environment.ts index 91ce590684..6720986a82 100644 --- a/source/main/environment.ts +++ b/source/main/environment.ts @@ -50,6 +50,7 @@ const isPreview = checkIsPreview(NETWORK); const isShelleyQA = checkIsShelleyQA(NETWORK); const isSelfnode = checkIsSelfnode(NETWORK); const isDevelopment = checkIsDevelopment(NETWORK); +const analyticsFeatureEnabled = isMainnet || isStaging || isTestnet; const keepLocalClusterRunning = process.env.KEEP_LOCAL_CLUSTER_RUNNING; const API_VERSION = process.env.API_VERSION || 'dev'; const NODE_VERSION = '1.35.3'; // TODO: pick up this value from process.env @@ -111,6 +112,7 @@ export const environment: Environment = Object.assign( isBlankScreenFixActive, keepLocalClusterRunning, hasMetHardwareRequirements, + analyticsFeatureEnabled, }, process.env ); diff --git a/source/renderer/app/Routes.tsx b/source/renderer/app/Routes.tsx index e9125b9d6e..75ce499e84 100644 --- a/source/renderer/app/Routes.tsx +++ b/source/renderer/app/Routes.tsx @@ -34,6 +34,7 @@ import WalletSettingsPage from './containers/wallet/WalletSettingsPage'; import WalletUtxoPage from './containers/wallet/WalletUtxoPage'; import VotingRegistrationPage from './containers/voting/VotingRegistrationPage'; import { IS_STAKING_INFO_PAGE_AVAILABLE } from './config/stakingConfig'; +import AnalyticsConsentPage from './containers/profile/AnalyticsConsentPage'; export const Routes = withRouter(() => ( @@ -49,6 +50,10 @@ export const Routes = withRouter(() => ( component={InitialSettingsPage} /> + */ -export type Listener

= (params: P) => any; +export type Listener

= (params: P) => void; /** * Action class with typed params */ @@ -30,8 +30,8 @@ export default class Action { this.listeners.push(listener); } - trigger(params?: Params) { - this.listeners.forEach((listener) => listener(params)); + async trigger(params?: Params): Promise { + await Promise.all(this.listeners.map((listener) => listener(params))); } remove(listener: Listener) { diff --git a/source/renderer/app/actions/profile-actions.ts b/source/renderer/app/actions/profile-actions.ts index 5732ac47e4..6614a60556 100644 --- a/source/renderer/app/actions/profile-actions.ts +++ b/source/renderer/app/actions/profile-actions.ts @@ -1,6 +1,8 @@ -import Action from './lib/Action'; // ======= PROFILE ACTIONS ======= +import Action from './lib/Action'; +import { AnalyticsAcceptanceStatus } from '../analytics/types'; export default class ProfileActions { + acceptAnalytics: Action = new Action(); acceptTermsOfUse: Action = new Action(); acceptDataLayerMigration: Action = new Action(); getLogs: Action = new Action(); diff --git a/source/renderer/app/analytics/index.ts b/source/renderer/app/analytics/index.ts new file mode 100644 index 0000000000..328c7daa03 --- /dev/null +++ b/source/renderer/app/analytics/index.ts @@ -0,0 +1 @@ +export { AnalyticsAcceptanceStatus } from './types'; diff --git a/source/renderer/app/analytics/types.ts b/source/renderer/app/analytics/types.ts new file mode 100644 index 0000000000..f67185663c --- /dev/null +++ b/source/renderer/app/analytics/types.ts @@ -0,0 +1,5 @@ +export enum AnalyticsAcceptanceStatus { + PENDING = 'PENDING', + ACCEPTED = 'ACCEPTED', + REJECTED = 'REJECTED', +} diff --git a/source/renderer/app/api/utils/localStorage.ts b/source/renderer/app/api/utils/localStorage.ts index 53755cd9ab..cf341b7cc8 100644 --- a/source/renderer/app/api/utils/localStorage.ts +++ b/source/renderer/app/api/utils/localStorage.ts @@ -1,12 +1,13 @@ /* eslint-disable consistent-return */ -import { includes, without, get } from 'lodash'; +import { get, includes, without } from 'lodash'; +import { v4 as uuidv4 } from 'uuid'; import { toJS } from '../../../../common/utils/helper'; import { electronStoreConversation } from '../../ipc/electronStoreConversation'; import type { WalletMigrationStatus } from '../../stores/WalletMigrationStore'; import { WalletMigrationStatuses } from '../../stores/WalletMigrationStore'; import { - STORAGE_TYPES as types, STORAGE_KEYS as keys, + STORAGE_TYPES as types, } from '../../../../common/config/electron-store.config'; import type { NewsTimestamp } from '../news/types'; import type { @@ -16,8 +17,8 @@ import type { import type { StorageKey } from '../../../../common/types/electron-store.types'; import type { Currency, DeprecatedCurrency } from '../../types/currencyTypes'; import { - CURRENCY_IS_ACTIVE_BY_DEFAULT, CURRENCY_DEFAULT_SELECTED, + CURRENCY_IS_ACTIVE_BY_DEFAULT, } from '../../config/currencyConfig'; import { AssetLocalData, @@ -27,6 +28,7 @@ import { UnpairedHardwareWalletData, WalletLocalData, } from '../../types/localDataTypes'; +import { AnalyticsAcceptanceStatus } from '../../analytics'; export type SetHardwareWalletLocalDataRequestType = { walletId: string; @@ -121,6 +123,28 @@ export default class LocalStorageApi { LocalStorageApi.set(keys.TERMS_OF_USE_ACCEPTANCE, true); unsetTermsOfUseAcceptance = (): Promise => LocalStorageApi.unset(keys.TERMS_OF_USE_ACCEPTANCE); + getAnalyticsAcceptance = (): Promise => + LocalStorageApi.get( + keys.ANALYTICS_ACCEPTANCE, + AnalyticsAcceptanceStatus.PENDING + ); + setAnalyticsAcceptance = (status: AnalyticsAcceptanceStatus): Promise => + LocalStorageApi.set(keys.ANALYTICS_ACCEPTANCE, status); + unsetAnalyticsAcceptance = (): Promise => + LocalStorageApi.set( + keys.ANALYTICS_ACCEPTANCE, + AnalyticsAcceptanceStatus.PENDING + ); + getUserID = async (): Promise => { + let userId: string = await LocalStorageApi.get(keys.USER_ID, null); + + if (!userId) { + userId = uuidv4(); + await LocalStorageApi.set(keys.USER_ID, userId); + } + + return userId; + }; getUserTheme = (): Promise => LocalStorageApi.get(keys.THEME); setUserTheme = (theme: string): Promise => LocalStorageApi.set(keys.THEME, theme); diff --git a/source/renderer/app/components/dapp/DappTransactionRequest.scss b/source/renderer/app/components/dapp/DappTransactionRequest.scss index aba4af8978..3bb2959748 100644 --- a/source/renderer/app/components/dapp/DappTransactionRequest.scss +++ b/source/renderer/app/components/dapp/DappTransactionRequest.scss @@ -44,53 +44,9 @@ } } } - hr { - border: 0; - border-top: 1px solid var(--theme-dapp-transaction-request-separator); - margin: 20px 0; - opacity: 0.3; - } .transactionFee { color: var(--theme-dapp-transaction-request-fees-text-color); font-family: var(--font-regular); margin-bottom: 20px; } - .additionalData, - .metadata { - background-color: var( - --theme-dapp-transaction-request-code-background-color - ); - pre { - font-family: var(--font-mono); - font-size: 14px; - line-height: 1.38; - max-height: 250px; - overflow: auto; - padding: 9px 12px; - user-select: text; - white-space: pre-wrap; - word-wrap: break-word; - &::-webkit-scrollbar-corner { - background: transparent !important; - } - &::-webkit-scrollbar-button { - height: 2px !important; - } - } - } - .toggleButton { - background-color: var( - --theme-dapp-transaction-request-toggle-button-background-color - ); - border-radius: 4px; - color: var(--theme-label-button-color); - cursor: pointer; - font-family: var(--font-medium); - font-size: 10px; - letter-spacing: 0.5px; - line-height: 1.2; - margin-left: 10px; - padding: 4px 8px; - text-transform: uppercase; - } } diff --git a/source/renderer/app/components/dapp/DappTransactionRequest.tsx b/source/renderer/app/components/dapp/DappTransactionRequest.tsx index f8186ec0bc..9575bbb268 100644 --- a/source/renderer/app/components/dapp/DappTransactionRequest.tsx +++ b/source/renderer/app/components/dapp/DappTransactionRequest.tsx @@ -1,12 +1,12 @@ -import React, { useState, useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import BigNumber from 'bignumber.js'; import classnames from 'classnames'; import { Select } from 'react-polymorph/lib/components/Select'; import { defineMessages, - intlShape, - injectIntl, FormattedHTMLMessage, + injectIntl, + intlShape, } from 'react-intl'; import { observer } from 'mobx-react'; import styles from './DappTransactionRequest.scss'; @@ -18,6 +18,9 @@ import AssetsTransactionConfirmation from '../assets/AssetsTransactionConfirmati import { formattedWalletAmount } from '../../utils/formatters'; import { isTokenMissingInWallet, tokenHasBalance } from '../../utils/assets'; import type { AssetToken } from '../../api/assets/types'; +import { MonospaceTextBlock } from '../widgets/monospace-text-block/MonospaceTextBlock'; +import { CollapsibleSection } from '../widgets/collapsible-section/CollapsibleSection'; +import { Separator } from '../widgets/separator/Separator'; const messages = defineMessages({ title: { @@ -92,16 +95,6 @@ type Props = { wallets: Array; }; const DappTransactionRequest = observer((props: Props) => { - const [isAdditionalDataVisible, toggleAdditionalData] = useState( - false - ); - const [isMetadataVisible, toggleMetadata] = useState(false); - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. - const getToggleLabel = useCallback((isVisible: boolean) => - isVisible - ? intl.formatMessage(globalMessages.hide) - : intl.formatMessage(globalMessages.view) - ); const { adaAmount, address, @@ -144,6 +137,7 @@ const DappTransactionRequest = observer((props: Props) => { }} /> ) : null; + const walletsDropdownStyles = classnames([ styles.walletsDropdown, walletsDropdownHasError || hasTokenError ? styles.error : null, @@ -200,8 +194,7 @@ const DappTransactionRequest = observer((props: Props) => { className={styles.addWalletSelect} /> )} - -


+

{intl.formatMessage(messages.receiverLabel)}

@@ -219,34 +212,14 @@ const DappTransactionRequest = observer((props: Props) => {
+{formattedWalletAmount(transactionFee)}
-

- {intl.formatMessage(messages.additionalDataLabel)} - -

- {isAdditionalDataVisible && ( -
-
{additionalData}
-
- )} -

- {intl.formatMessage(messages.metaDataLabel)} - -

- {isMetadataVisible && ( -
-
{metadata}
-
- )} + + {additionalData} + + + {metadata} + ); }); diff --git a/source/renderer/app/components/profile/analytics/Analytics.stories.tsx b/source/renderer/app/components/profile/analytics/Analytics.stories.tsx new file mode 100644 index 0000000000..f51e73eb9d --- /dev/null +++ b/source/renderer/app/components/profile/analytics/Analytics.stories.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { withKnobs } from '@storybook/addon-knobs'; +import AnalyticsConsentForm from './AnalyticsConsentForm'; +import StoryDecorator from '../../../../../../storybook/stories/_support/StoryDecorator'; + +storiesOf('Analytics', module) + .addDecorator(withKnobs) + .addDecorator((story) => {story()}) + .add('Analytics Consent Form', () => ); diff --git a/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.messages.ts b/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.messages.ts new file mode 100644 index 0000000000..0b90e08a8d --- /dev/null +++ b/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.messages.ts @@ -0,0 +1,42 @@ +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + title: { + id: 'analytics.form.title', + defaultMessage: '!!!Anonymous data collection', + description: 'Analytics form title', + }, + description: { + id: 'analytics.form.description', + defaultMessage: + '!!!All data is anonymous and is used only for product development purposes.', + description: 'Analytics data collection description', + }, + dataCollectionSwitchButton: { + id: 'analytics.form.dataCollectionSwitchText', + defaultMessage: '!!!Allow anonymous data collection', + description: 'Data collection agreement switch button label', + }, + allowButton: { + id: 'analytics.form.allowButton', + defaultMessage: '!!!Allow', + description: 'Analytics data collection allow button text', + }, + skipButton: { + id: 'analytics.dialog.skipButton', + defaultMessage: '!!!Skip', + description: 'Analytics data collection skip button text', + }, + privacyPolicyLink: { + id: 'analytics.form.privacyPolicyLink', + defaultMessage: '!!!Daedalus Privacy Policy', + description: 'Daedalus Privacy Policy link text', + }, + analyticsSectionPrivacyPolicy: { + id: 'analytics.form.analyticsSectionPrivacyPolicy', + defaultMessage: + '!!!Read more about our privacy practices in the {privacyPolicyLink}.', + description: + 'Analytics data collection description, under collapsible details', + }, +}); diff --git a/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.scss b/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.scss new file mode 100644 index 0000000000..fc43c7feef --- /dev/null +++ b/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.scss @@ -0,0 +1,62 @@ +.component { + align-items: center; + color: var(--theme-main-body-messages-color); + display: flex; + font-family: var(--font-regular); + height: 100%; + justify-content: center; + line-height: 1.38; +} + +.centeredBox { + background-color: var(--theme-bordered-box-background-color); + border: var(--theme-bordered-box-border); + border-radius: 10px; + padding: 30px 30px 20px; + width: 620px; +} + +.title, +.dataCollectionTitle { + font-family: var(--font-medium); + margin-bottom: 14px; +} + +.dataCollectionTitle { + align-items: center; + display: flex; + font-family: var(--font-medium); +} + +.description { + font-family: var(--font-light); + margin-bottom: 20px; +} + +.descriptionLink { + color: var(--theme-link-main-color); +} + +.actions { + display: flex; + margin-top: 24px; +} + +.skipButton { + margin-right: 12px; +} + +.privacyPolicyLink { + font-family: var(--font-regular); + // unfortunately, global overrides for link component set it to 14px with higher specificity + font-size: 16px !important; + word-break: break-word; +} + +.privacyPolicyDescription { + color: var(--theme-main-body-messages-color); + font-family: var(--font-light); + font-size: 16px; + line-height: 1.43; + margin-top: 20px; +} diff --git a/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.tsx b/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.tsx new file mode 100644 index 0000000000..53b8ae8d69 --- /dev/null +++ b/source/renderer/app/components/profile/analytics/AnalyticsConsentForm.tsx @@ -0,0 +1,81 @@ +import React, { FC, useCallback } from 'react'; +import { FormattedMessage, injectIntl } from 'react-intl'; +import { Button } from 'react-polymorph/lib/components/Button'; +import { Link } from 'react-polymorph/lib/components/Link'; +import { ButtonSpinnerSkin } from 'react-polymorph/lib/skins/simple/ButtonSpinnerSkin'; +import classnames from 'classnames'; +import styles from './AnalyticsConsentForm.scss'; +import { Intl } from '../../../types/i18nTypes'; +import { messages } from './AnalyticsConsentForm.messages'; +import { CollectedDataOverview } from './CollectedDataOverview'; + +interface AnalyticsConsentFormProps { + intl: Intl; + loading: boolean; + onSubmit: (analyticsAccepted: boolean) => void; + onExternalLinkClick: (url: string) => void; +} + +function AnalyticsConsentForm({ + intl, + loading, + onSubmit, + onExternalLinkClick, +}: AnalyticsConsentFormProps) { + const handleAllow = useCallback(() => { + onSubmit(true); + }, []); + const handleSkip = useCallback(() => { + onSubmit(false); + }, []); + + const privacyPolicyLink = ( + + onExternalLinkClick( + 'https://static.iohk.io/terms/iog-privacy-policy.pdf' + ) + } + label={intl.formatMessage(messages.privacyPolicyLink)} + hasIconAfter={false} + /> + ); + + return ( +
+
+

{intl.formatMessage(messages.title)}

+

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

+ +

+ +

+
+
+
+
+ ); +} + +export default injectIntl(AnalyticsConsentForm); diff --git a/source/renderer/app/components/profile/analytics/CollectedDataOverview.messages.ts b/source/renderer/app/components/profile/analytics/CollectedDataOverview.messages.ts new file mode 100644 index 0000000000..29be57c7ca --- /dev/null +++ b/source/renderer/app/components/profile/analytics/CollectedDataOverview.messages.ts @@ -0,0 +1,41 @@ +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + title: { + id: 'analytics.form.dataCollectionDetailsTitle', + defaultMessage: + '!!!We collect data on (1) User click behavior and (2) Device information.', + description: 'Data collection details title', + }, + userBehaviorTitle: { + id: 'analytics.form.dataCollectionDetailsUserBehaviourTitle', + defaultMessage: '!!!User click behavior', + description: 'Title for the user behaviour data collection', + }, + userBehaviorText: { + id: 'analytics.form.dataCollectionDetailsUserBehaviorText', + defaultMessage: + '!!!Clicks, page visits, page scrolling, number of wallets, number of native assets, session duration, type of wallets (soft vs hardware wallets), geolocation (country of location), and page performance.', + description: 'Description for the user behaviour data collection', + }, + deviceInfoTitle: { + id: 'analytics.form.dataCollectionDetailsDeviceInfoTitle', + defaultMessage: '!!!Device info', + description: 'Title for the device info data collection', + }, + deviceInfoText: { + id: 'analytics.form.dataCollectionDetailsDeviceInfoText', + defaultMessage: '!!!Operating system, RAM, and disk space.', + description: 'Description for the device info data collection', + }, + expandButton: { + id: 'analytics.dialog.expandButton', + defaultMessage: '!!!Expand details', + description: 'Expand details button', + }, + collapseButton: { + id: 'analytics.dialog.collapseButton', + defaultMessage: '!!!Collapse details', + description: 'Collapse details button', + }, +}); diff --git a/source/renderer/app/components/profile/analytics/CollectedDataOverview.scss b/source/renderer/app/components/profile/analytics/CollectedDataOverview.scss new file mode 100644 index 0000000000..4fbe8dce4f --- /dev/null +++ b/source/renderer/app/components/profile/analytics/CollectedDataOverview.scss @@ -0,0 +1,25 @@ +.dataCollectionList { + color: var(--theme-main-body-messages-color); + font: 16px var(--font-light); + line-height: 2; + list-style: auto outside; + padding-left: 30px; + + li { + padding-left: 12px; + + &::marker { + font-weight: bold; + } + } + + h3 { + font: 16px var(--font-medium); + margin-bottom: 6px; + } + + p { + line-height: 1.43; + margin-bottom: 6px; + } +} diff --git a/source/renderer/app/components/profile/analytics/CollectedDataOverview.tsx b/source/renderer/app/components/profile/analytics/CollectedDataOverview.tsx new file mode 100644 index 0000000000..995819330d --- /dev/null +++ b/source/renderer/app/components/profile/analytics/CollectedDataOverview.tsx @@ -0,0 +1,28 @@ +import React, { FC } from 'react'; +import { injectIntl } from 'react-intl'; +import { CollapsibleSection } from '../../widgets/collapsible-section/CollapsibleSection'; +import styles from './CollectedDataOverview.scss'; +import { messages } from './CollectedDataOverview.messages'; + +export const CollectedDataOverview: FC = injectIntl(({ intl }) => { + return ( + +
    +
  1. +

    {intl.formatMessage(messages.userBehaviorTitle)}

    +

    {intl.formatMessage(messages.userBehaviorText)}

    +
  2. +
  3. +

    {intl.formatMessage(messages.deviceInfoTitle)}

    +

    {intl.formatMessage(messages.deviceInfoText)}

    +
  4. +
+
+ ); +}); diff --git a/source/renderer/app/components/settings/SettingsLayout.scss b/source/renderer/app/components/settings/SettingsLayout.scss index 99c2cf31c7..dc6068fea6 100644 --- a/source/renderer/app/components/settings/SettingsLayout.scss +++ b/source/renderer/app/components/settings/SettingsLayout.scss @@ -19,5 +19,5 @@ border: var(--theme-settings-pane-border); border-radius: 4px; overflow: visible; - padding: 30px 25px 10px 25px; + padding: 30px 25px; } diff --git a/source/renderer/app/components/settings/categories/SupportSettings.messages.ts b/source/renderer/app/components/settings/categories/SupportSettings.messages.ts new file mode 100644 index 0000000000..f581daf656 --- /dev/null +++ b/source/renderer/app/components/settings/categories/SupportSettings.messages.ts @@ -0,0 +1,86 @@ +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + faqTitle: { + id: 'settings.support.faq.title', + defaultMessage: '!!!Help and support', + description: 'Title "Help and support" on the support settings page.', + }, + faqContent: { + id: 'settings.support.faq.content', + defaultMessage: + '!!!If you are experiencing a problem, please look for guidance using the list of {faqLink} on the support pages. If you can’t find a solution, please submit a support ticket.', + description: + 'Content for the "Help and support" section on the support settings page.', + }, + faqLink: { + id: 'settings.support.faq.faqLink', + defaultMessage: '!!!Known Issues', + description: + '"Known Issues" link in the "Help and support" section on the support settings page', + }, + stepsTitle: { + id: 'settings.support.steps.title', + defaultMessage: '!!!Steps for creating a support request:', + description: + 'Title "Steps for creating a support request" on the support settings page.', + }, + stepsDownloadLogsTitle: { + id: 'settings.support.steps.downloadLogs.title', + defaultMessage: '!!!Download the logs', + description: 'Title "Download the logs" on the support settings page.', + }, + stepsDownloadLogsDescription: { + id: 'settings.support.steps.downloadLogs.description', + defaultMessage: + '!!!Please {downloadLogsLink} and attach the downloaded file when submitting a support request to help the support team investigate the issue. Logs do not contain sensitive information.', + description: + 'Description of "Download the logs" on the support settings page.', + }, + stepsDownloadLogsLink: { + id: 'settings.support.steps.downloadLogs.link', + defaultMessage: '!!!download your logs here', + description: + '"download your logs here" link in the Logs section on the support settings page', + }, + stepsReportProblemTitle: { + id: 'settings.support.steps.reportProblem.title', + defaultMessage: '!!!Report a problem', + description: 'Title "Report a problem" on the support settings page.', + }, + stepsReportProblemDescription: { + id: 'settings.support.steps.reportProblem.description', + defaultMessage: + '!!!Please {downloadLogsLink} and attach the downloaded file when submitting a support request to help the support team investigate the issue. Logs do not contain sensitive information.', + description: + 'Description of "Download the logs" on the support settings page.', + }, + stepsReportProblemLink: { + id: 'settings.support.steps.reportProblem.link', + defaultMessage: '!!!download your logs here', + description: + '"download your logs here" link in the Logs section on the support settings page', + }, + analyticsSectionTitle: { + id: 'analytics.form.title', + defaultMessage: '!!!Analytics data collection', + description: 'Analytics form title', + }, + analyticsAcceptedDescription: { + id: 'analytics.form.analyticsAcceptedDescription', + defaultMessage: + '!!!You have opted in to analytics data collection. You can {changeAnalyticsSettingsLink}.', + description: 'Analytics data collection description when user opted in', + }, + analyticsDeclinedDescription: { + id: 'analytics.form.analyticsDeclinedDescription', + defaultMessage: + '!!!You have opted out of analytics data collection. You can {changeAnalyticsSettingsLink}.', + description: 'Analytics data collection description when user opted out ', + }, + changeAnalyticsSettingsLink: { + id: 'analytics.form.changeAnalyticsSettingsLink', + defaultMessage: '!!!change this setting here', + description: 'Change analytics settings link text', + }, +}); diff --git a/source/renderer/app/components/settings/categories/SupportSettings.scss b/source/renderer/app/components/settings/categories/SupportSettings.scss index 235d439f65..251a73c8ef 100644 --- a/source/renderer/app/components/settings/categories/SupportSettings.scss +++ b/source/renderer/app/components/settings/categories/SupportSettings.scss @@ -1,9 +1,7 @@ @import '../../../themes/mixins/error-message'; @import '../../../themes/mixins/link'; -.component { - padding-bottom: 10px; - +.supportGuide { h1 { color: var(--theme-support-settings-text-color); font-family: var(--font-semibold); @@ -21,19 +19,20 @@ } ol { - color: var(--theme-support-settings-item-color); + color: var(--theme-main-body-messages-color); font-family: var(--font-semibold); list-style: decimal; - padding-left: 17px; - } + padding-left: 30px; - li { - padding-left: 6.5px; + li { + font-family: var(--font-medium); + padding-left: 12px; + } } p { color: var(--theme-support-settings-text-color); - font-family: var(--font-regular); + font-family: var(--font-light); font-size: 16px; } @@ -63,3 +62,23 @@ } } } + +.analyticsSectionTitle { + color: var(--theme-main-body-messages-color); + font-family: var(--font-medium); + margin-bottom: 14px; +} + +.analyticsSectionDescription { + color: var(--theme-main-body-messages-color); + font-family: var(--font-light); + font-size: 16px; + line-height: 1.43; +} + +.changeAnalyticsSettingsLink { + font-family: var(--font-regular); + // unfortunately, global overrides for link component set it to 14px with higher specificity + font-size: 16px !important; + word-break: break-word; +} diff --git a/source/renderer/app/components/settings/categories/SupportSettings.tsx b/source/renderer/app/components/settings/categories/SupportSettings.tsx index e5f424598f..11f1ff3772 100644 --- a/source/renderer/app/components/settings/categories/SupportSettings.tsx +++ b/source/renderer/app/components/settings/categories/SupportSettings.tsx @@ -1,95 +1,35 @@ import React, { Component } from 'react'; -import { observer } from 'mobx-react'; -import { defineMessages, intlShape, FormattedMessage } from 'react-intl'; +import { inject, observer } from 'mobx-react'; +import { FormattedMessage, injectIntl, Intl } from 'react-intl'; import classNames from 'classnames'; import { Link } from 'react-polymorph/lib/components/Link'; import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin'; import styles from './SupportSettings.scss'; import globalMessages from '../../../i18n/global-messages'; +import { InjectedProps } from '../../../types/injectedPropsType'; +import { messages } from './SupportSettings.messages'; +import { Separator } from '../../widgets/separator/Separator'; -const messages = defineMessages({ - faqTitle: { - id: 'settings.support.faq.title', - defaultMessage: '!!!Help and support', - description: 'Title "Help and support" on the support settings page.', - }, - faqContent: { - id: 'settings.support.faq.content', - defaultMessage: - '!!!If you are experiencing a problem, please look for guidance using the list of {faqLink} on the support pages. If you can’t find a solution, please submit a support ticket.', - description: - 'Content for the "Help and support" section on the support settings page.', - }, - faqLink: { - id: 'settings.support.faq.faqLink', - defaultMessage: '!!!Known Issues', - description: - '"Known Issues" link in the "Help and support" section on the support settings page', - }, - stepsTitle: { - id: 'settings.support.steps.title', - defaultMessage: '!!!Steps for creating a support request:', - description: - 'Title "Steps for creating a support request" on the support settings page.', - }, - stepsDownloadLogsTitle: { - id: 'settings.support.steps.downloadLogs.title', - defaultMessage: '!!!Download the logs', - description: 'Title "Download the logs" on the support settings page.', - }, - stepsDownloadLogsDescription: { - id: 'settings.support.steps.downloadLogs.description', - defaultMessage: - '!!!Please {downloadLogsLink} and attach the downloaded file when submitting a support request to help the support team investigate the issue. Logs do not contain sensitive information.', - description: - 'Description of "Download the logs" on the support settings page.', - }, - stepsDownloadLogsLink: { - id: 'settings.support.steps.downloadLogs.link', - defaultMessage: '!!!download your logs here', - description: - '"download your logs here" link in the Logs section on the support settings page', - }, - stepsReportProblemTitle: { - id: 'settings.support.steps.reportProblem.title', - defaultMessage: '!!!Report a problem', - description: 'Title "Report a problem" on the support settings page.', - }, - stepsReportProblemDescription: { - id: 'settings.support.steps.reportProblem.description', - defaultMessage: - '!!!Please {downloadLogsLink} and attach the downloaded file when submitting a support request to help the support team investigate the issue. Logs do not contain sensitive information.', - description: - 'Description of "Download the logs" on the support settings page.', - }, - stepsReportProblemLink: { - id: 'settings.support.steps.reportProblem.link', - defaultMessage: '!!!download your logs here', - description: - '"download your logs here" link in the Logs section on the support settings page', - }, -}); -type Props = { +interface SupportSettingsProps { + intl: Intl; onExternalLinkClick: (...args: Array) => any; onSupportRequestClick: (...args: Array) => any; + onChangeAnalyticsSettings: () => void; onDownloadLogs: (...args: Array) => any; disableDownloadLogs: boolean; -}; + analyticsAccepted: boolean; +} @observer -class SupportSettings extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - +class SupportSettings extends Component { render() { const { onExternalLinkClick, onSupportRequestClick, onDownloadLogs, disableDownloadLogs, + intl, } = this.props; - const { intl } = this.context; const faqLinkUrl = intl.formatMessage(globalMessages.faqLinkUrl); const faqLink = ( @@ -122,52 +62,80 @@ class SupportSettings extends Component { skin={LinkSkin} /> ); + + const changeAnalyticsSettingsLink = ( + + ); + return ( -
- {/* Help and Support */} + <> +
+ {/* Help and Support */} -

{intl.formatMessage(messages.faqTitle)}

+

{intl.formatMessage(messages.faqTitle)}

-

+

+ +

+ + {/* Steps for creating a support request: */} + +

{intl.formatMessage(messages.stepsTitle)}

+ +
    +
  1. +

    {intl.formatMessage(messages.stepsDownloadLogsTitle)}

    +

    + +

    +
  2. +
  3. +

    {intl.formatMessage(messages.stepsReportProblemTitle)}

    +

    + +

    +
  4. +
+
+ + + +

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

+

- - {/* Steps for creating a support request: */} - -

{intl.formatMessage(messages.stepsTitle)}

- -
    -
  1. -

    {intl.formatMessage(messages.stepsDownloadLogsTitle)}

    -

    - -

    -
  2. -
  3. -

    {intl.formatMessage(messages.stepsReportProblemTitle)}

    -

    - -

    -
  4. -
-
+ ); } } -export default SupportSettings; +export default injectIntl(SupportSettings); diff --git a/source/renderer/app/components/settings/categories/WalletsSettings.scss b/source/renderer/app/components/settings/categories/WalletsSettings.scss index 09a0c23650..7173f5fee5 100644 --- a/source/renderer/app/components/settings/categories/WalletsSettings.scss +++ b/source/renderer/app/components/settings/categories/WalletsSettings.scss @@ -67,9 +67,6 @@ } .separator { - background: var(--theme-wallet-settings-section-separator-color); - border: none; - height: 1px; margin: 15px 0; } diff --git a/source/renderer/app/components/settings/categories/WalletsSettings.tsx b/source/renderer/app/components/settings/categories/WalletsSettings.tsx index 4db714bf37..48299eab2f 100644 --- a/source/renderer/app/components/settings/categories/WalletsSettings.tsx +++ b/source/renderer/app/components/settings/categories/WalletsSettings.tsx @@ -9,6 +9,7 @@ import styles from './WalletsSettings.scss'; import { currencyConfig } from '../../../config/currencyConfig'; import globalMessages from '../../../i18n/global-messages'; import type { LocalizedCurrency } from '../../../types/currencyTypes'; +import { Separator } from '../../widgets/separator/Separator'; const messages = defineMessages({ currencyTitleLabel: { @@ -87,7 +88,7 @@ class WalletSettings extends Component { {currencyIsActive && (
-
+
{intl.formatMessage(messages.currencyPoweredByLabel)} { + const [expanded, setExpanded] = useState(false); + const handleToggle = useCallback(() => { + setExpanded((previousExpanded) => !previousExpanded); + }, [setExpanded]); + + const buttonMessage = intl.formatMessage( + expanded ? collapseMessage || globalMessages.hide : expandMessage + ); + + return ( + <> +

+ {header} + {expandButtonStyle === 'button' && ( + + )} + {expandButtonStyle === 'link' && ( + <> + {' '} + + . + + )} +

+ {expanded && children} + + ); + } +); diff --git a/source/renderer/app/components/widgets/forms/NormalSwitch.tsx b/source/renderer/app/components/widgets/forms/NormalSwitch.tsx index 5bb12ca56e..4f356bb2d9 100644 --- a/source/renderer/app/components/widgets/forms/NormalSwitch.tsx +++ b/source/renderer/app/components/widgets/forms/NormalSwitch.tsx @@ -2,9 +2,11 @@ import React, { Component } from 'react'; import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; import { SwitchSkin } from 'react-polymorph/lib/skins/simple/SwitchSkin'; import { IDENTIFIERS } from 'react-polymorph/lib/themes/API'; +import classnames from 'classnames'; import styles from './NormalSwitch.scss'; type Props = { + className?: string; checked?: boolean; label?: string; onChange?: (...args: Array) => any; @@ -13,7 +15,7 @@ export default class NormalSwitch extends Component { render() { return ( {children}; +} diff --git a/source/renderer/app/components/widgets/separator/Separator.scss b/source/renderer/app/components/widgets/separator/Separator.scss new file mode 100644 index 0000000000..05c36698f9 --- /dev/null +++ b/source/renderer/app/components/widgets/separator/Separator.scss @@ -0,0 +1,5 @@ +.hr { + border: 0; + border-top: 1px solid var(--theme-widgets-separator-color); + margin: 20px 0; +} diff --git a/source/renderer/app/components/widgets/separator/Separator.tsx b/source/renderer/app/components/widgets/separator/Separator.tsx new file mode 100644 index 0000000000..c30c016d62 --- /dev/null +++ b/source/renderer/app/components/widgets/separator/Separator.tsx @@ -0,0 +1,7 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './Separator.scss'; + +export function Separator({ className }: { className?: string }) { + return
; +} diff --git a/source/renderer/app/config/analyticsConfig.ts b/source/renderer/app/config/analyticsConfig.ts new file mode 100644 index 0000000000..9fbfef09db --- /dev/null +++ b/source/renderer/app/config/analyticsConfig.ts @@ -0,0 +1,17 @@ +import { Network } from '../../../common/types/environment.types'; + +export const ANALYTICS_API_ENDPOINT = 'https://mazurek.matomo.cloud/matomo.php'; +export const DEV_MODE_SITE_MAP_ID = 5; +export const NETWORK_TO_ANALYTICS_SITE_ID_MAP: Record = { + mainnet: 4, + mainnet_flight: 4, + testnet: 3, + preprod: 3, + preview: 3, + staging: 5, + shelley_qa: 5, + alonzo_purple: 5, + selfnode: 5, + development: 5, + vasil_dev: 5, +}; diff --git a/source/renderer/app/containers/profile/AnalyticsConsentPage.tsx b/source/renderer/app/containers/profile/AnalyticsConsentPage.tsx new file mode 100644 index 0000000000..3dbfa94a85 --- /dev/null +++ b/source/renderer/app/containers/profile/AnalyticsConsentPage.tsx @@ -0,0 +1,36 @@ +import React, { FC, useCallback } from 'react'; +import TopBar from '../../components/layout/TopBar'; +import TopBarLayout from '../../components/layout/TopBarLayout'; +import AnalyticsConsentForm from '../../components/profile/analytics/AnalyticsConsentForm'; +import { AnalyticsAcceptanceStatus } from '../../analytics/types'; +import { useActions } from '../../hooks/useActions'; +import { useStores } from '../../hooks/useStores'; + +export function AnalyticsConsentPage() { + const actions = useActions(); + const { networkStatus, profile, app } = useStores(); + + const handleSubmit = useCallback(async (analyticsAccepted: boolean) => { + await actions.profile.acceptAnalytics.trigger( + analyticsAccepted + ? AnalyticsAcceptanceStatus.ACCEPTED + : AnalyticsAcceptanceStatus.REJECTED + ); + }, []); + + const { setAnalyticsAcceptanceRequest } = profile; + const { isShelleyActivated } = networkStatus; + + const topbar = ; + return ( + + + + ); +} + +export default AnalyticsConsentPage; diff --git a/source/renderer/app/containers/settings/categories/SupportSettingsPage.tsx b/source/renderer/app/containers/settings/categories/SupportSettingsPage.tsx index 49346d243d..134b374fee 100644 --- a/source/renderer/app/containers/settings/categories/SupportSettingsPage.tsx +++ b/source/renderer/app/containers/settings/categories/SupportSettingsPage.tsx @@ -4,6 +4,7 @@ import { defineMessages, intlShape } from 'react-intl'; import SupportSettings from '../../../components/settings/categories/SupportSettings'; import { generateSupportRequestLink } from '../../../../../common/utils/reporting'; import type { InjectedProps } from '../../../types/injectedPropsType'; +import { AnalyticsAcceptanceStatus } from '../../../analytics'; const messages = defineMessages({ supportRequestLinkUrl: { @@ -20,10 +21,18 @@ class SupportSettingsPage extends Component { static contextTypes = { intl: intlShape.isRequired, }; + static defaultProps = { actions: null, stores: null, }; + + handleChangeAnalyticsSettings = () => { + this.props.actions.profile.acceptAnalytics.trigger( + AnalyticsAcceptanceStatus.PENDING + ); + }; + handleSupportRequestClick = async ( event: React.SyntheticEvent ) => { @@ -42,6 +51,7 @@ class SupportSettingsPage extends Component { ); this.props.stores.app.openExternalLink(supportUrl); }; + handleDownloadLogs = () => { const { app } = this.props.actions; app.downloadLogs.trigger(); @@ -55,9 +65,14 @@ class SupportSettingsPage extends Component { onExternalLinkClick={stores.app.openExternalLink} onSupportRequestClick={this.handleSupportRequestClick} onDownloadLogs={this.handleDownloadLogs} + onChangeAnalyticsSettings={this.handleChangeAnalyticsSettings} disableDownloadLogs={ this.props.stores.app.isDownloadNotificationVisible } + analyticsAccepted={ + this.props.stores.profile.analyticsAcceptanceStatus === + AnalyticsAcceptanceStatus.ACCEPTED + } /> ); } diff --git a/source/renderer/app/hooks/useActions.ts b/source/renderer/app/hooks/useActions.ts new file mode 100644 index 0000000000..d39c367932 --- /dev/null +++ b/source/renderer/app/hooks/useActions.ts @@ -0,0 +1,7 @@ +import { MobXProviderContext } from 'mobx-react'; +import React from 'react'; +import { ActionsMap } from '../actions'; + +export function useActions(): ActionsMap { + return React.useContext(MobXProviderContext).actions; +} diff --git a/source/renderer/app/hooks/useStores.ts b/source/renderer/app/hooks/useStores.ts new file mode 100644 index 0000000000..0999d90233 --- /dev/null +++ b/source/renderer/app/hooks/useStores.ts @@ -0,0 +1,7 @@ +import { MobXProviderContext } from 'mobx-react'; +import React from 'react'; +import { StoresMap } from '../stores'; + +export function useStores(): StoresMap { + return React.useContext(MobXProviderContext).stores; +} diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index 7cb073cc56..cdac62b7d3 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -2619,6 +2619,156 @@ ], "path": "source/renderer/app/components/loading/system-time-error/SystemTimeError.tsx" }, + { + "descriptors": [ + { + "defaultMessage": "!!!Analytics data collection", + "description": "Analytics form title", + "id": "analytics.form.title" + }, + { + "defaultMessage": "!!!Help and support", + "description": "Title \"Help and support\" on the support settings page.", + "id": "settings.support.faq.title" + }, + { + "defaultMessage": "!!!If you are experiencing a problem, please look for guidance using the list of {faqLink} on the support pages. If you can’t find a solution, please submit a support ticket.", + "description": "Content for the \"Help and support\" section on the support settings page.", + "id": "settings.support.faq.content" + }, + { + "defaultMessage": "!!!Known Issues", + "description": "\"Known Issues\" link in the \"Help and support\" section on the support settings page", + "id": "settings.support.faq.faqLink" + }, + { + "defaultMessage": "!!!Steps for creating a support request:", + "description": "Title \"Steps for creating a support request\" on the support settings page.", + "id": "settings.support.steps.title" + }, + { + "defaultMessage": "!!!Download the logs", + "description": "Title \"Download the logs\" on the support settings page.", + "id": "settings.support.steps.downloadLogs.title" + }, + { + "defaultMessage": "!!!Please {downloadLogsLink} and attach the downloaded file when submitting a support request to help the support team investigate the issue. Logs do not contain sensitive information.", + "description": "Description of \"Download the logs\" on the support settings page.", + "id": "settings.support.steps.downloadLogs.description" + }, + { + "defaultMessage": "!!!download your logs here", + "description": "\"download your logs here\" link in the Logs section on the support settings page", + "id": "settings.support.steps.downloadLogs.link" + }, + { + "defaultMessage": "!!!Report a problem", + "description": "Title \"Report a problem\" on the support settings page.", + "id": "settings.support.steps.reportProblem.title" + }, + { + "defaultMessage": "!!!Please {downloadLogsLink} and attach the downloaded file when submitting a support request to help the support team investigate the issue. Logs do not contain sensitive information.", + "description": "Description of \"Download the logs\" on the support settings page.", + "id": "settings.support.steps.reportProblem.description" + }, + { + "defaultMessage": "!!!download your logs here", + "description": "\"download your logs here\" link in the Logs section on the support settings page", + "id": "settings.support.steps.reportProblem.link" + }, + { + "defaultMessage": "!!!You have opted in to analytics data collection. You can {changeAnalyticsSettingsLink}.", + "description": "Analytics data collection description when user opted in", + "id": "analytics.form.analyticsAcceptedDescription" + }, + { + "defaultMessage": "!!!You have opted out of analytics data collection. You can {changeAnalyticsSettingsLink}.", + "description": "Analytics data collection description when user opted out ", + "id": "analytics.form.analyticsDeclinedDescription" + }, + { + "defaultMessage": "!!!change this setting here", + "description": "Change analytics settings link text", + "id": "analytics.form.changeAnalyticsSettingsLink" + } + ], + "path": "source/renderer/app/components/settings/categories/SupportSettings.messages.ts" + }, + { + "descriptors": [ + { + "defaultMessage": "!!!All data is anonymous and is used only for product development purposes.", + "description": "Analytics data collection description", + "id": "analytics.form.description" + }, + { + "defaultMessage": "!!!Allow anonymous data collection", + "description": "Data collection agreement switch button label", + "id": "analytics.form.dataCollectionSwitchText" + }, + { + "defaultMessage": "!!!Allow", + "description": "Analytics data collection allow button text", + "id": "analytics.form.allowButton" + }, + { + "defaultMessage": "!!!Skip", + "description": "Analytics data collection skip button text", + "id": "analytics.dialog.skipButton" + }, + { + "defaultMessage": "!!!Daedalus Privacy Policy", + "description": "Daedalus Privacy Policy link text", + "id": "analytics.form.privacyPolicyLink" + }, + { + "defaultMessage": "!!!Read more about our privacy practices in the {privacyPolicyLink}.", + "description": "Analytics data collection description, under collapsible details", + "id": "analytics.form.analyticsSectionPrivacyPolicy" + } + ], + "path": "source/renderer/app/components/profile/analytics/AnalyticsConsentForm.messages.ts" + }, + { + "descriptors": [ + { + "defaultMessage": "!!!We collect data on (1) User click behavior and (2) Device information.", + "description": "Data collection details title", + "id": "analytics.form.dataCollectionDetailsTitle" + }, + { + "defaultMessage": "!!!User click behavior", + "description": "Title for the user behaviour data collection", + "id": "analytics.form.dataCollectionDetailsUserBehaviourTitle" + }, + { + "defaultMessage": "!!!Clicks, page visits, page scrolling, number of wallets, number of native assets, session duration, type of wallets (soft vs hardware wallets), geolocation (country of location), and page performance.", + "description": "Description for the user behaviour data collection", + "id": "analytics.form.dataCollectionDetailsUserBehaviorText" + }, + { + "defaultMessage": "!!!Device info", + "description": "Title for the device info data collection", + "id": "analytics.form.dataCollectionDetailsDeviceInfoTitle" + }, + { + "defaultMessage": "!!!Operating system, RAM, and disk space.", + "description": "Description for the device info data collection", + "id": "analytics.form.dataCollectionDetailsDeviceInfoText" + }, + { + "defaultMessage": "!!!Expand details", + "description": "Expand details button", + "id": "analytics.dialog.expandButton" + }, + { + "defaultMessage": "!!!Collapse details", + "description": "Collapse details button", + "id": "analytics.dialog.collapseButton" + } + ], + "path": "source/renderer/app/components/profile/analytics/CollectedDataOverview.messages.ts" + }, { "descriptors": [ { @@ -2859,61 +3009,6 @@ ], "path": "source/renderer/app/components/widgets/forms/InlineEditingInput.tsx" }, - { - "descriptors": [ - { - "defaultMessage": "!!!Help and support", - "description": "Title \"Help and support\" on the support settings page.", - "id": "settings.support.faq.title" - }, - { - "defaultMessage": "!!!If you are experiencing a problem, please look for guidance using the list of {faqLink} on the support pages. If you can’t find a solution, please submit a support ticket.", - "description": "Content for the \"Help and support\" section on the support settings page.", - "id": "settings.support.faq.content" - }, - { - "defaultMessage": "!!!Known Issues", - "description": "\"Known Issues\" link in the \"Help and support\" section on the support settings page", - "id": "settings.support.faq.faqLink" - }, - { - "defaultMessage": "!!!Steps for creating a support request:", - "description": "Title \"Steps for creating a support request\" on the support settings page.", - "id": "settings.support.steps.title" - }, - { - "defaultMessage": "!!!Download the logs", - "description": "Title \"Download the logs\" on the support settings page.", - "id": "settings.support.steps.downloadLogs.title" - }, - { - "defaultMessage": "!!!Please {downloadLogsLink} and attach the downloaded file when submitting a support request to help the support team investigate the issue. Logs do not contain sensitive information.", - "description": "Description of \"Download the logs\" on the support settings page.", - "id": "settings.support.steps.downloadLogs.description" - }, - { - "defaultMessage": "!!!download your logs here", - "description": "\"download your logs here\" link in the Logs section on the support settings page", - "id": "settings.support.steps.downloadLogs.link" - }, - { - "defaultMessage": "!!!Report a problem", - "description": "Title \"Report a problem\" on the support settings page.", - "id": "settings.support.steps.reportProblem.title" - }, - { - "defaultMessage": "!!!Please {downloadLogsLink} and attach the downloaded file when submitting a support request to help the support team investigate the issue. Logs do not contain sensitive information.", - "description": "Description of \"Download the logs\" on the support settings page.", - "id": "settings.support.steps.reportProblem.description" - }, - { - "defaultMessage": "!!!download your logs here", - "description": "\"download your logs here\" link in the Logs section on the support settings page", - "id": "settings.support.steps.reportProblem.link" - } - ], - "path": "source/renderer/app/components/settings/categories/SupportSettings.tsx" - }, { "descriptors": [ { diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json index 63551db606..ffa6a90ddf 100755 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -1,6 +1,23 @@ { "ImageUploadWidget.clickToUploadLabel": "!!!or click to upload", "ImageUploadWidget.dropFileHint": "!!!Drop file here", + "analytics.dialog.collapseButton": "Collapse details", + "analytics.dialog.expandButton": "Expand details", + "analytics.dialog.skipButton": "Skip", + "analytics.form.allowButton": "Allow", + "analytics.form.analyticsAcceptedDescription": "You have opted in to analytics data collection. You can {changeAnalyticsSettingsLink}.", + "analytics.form.analyticsDeclinedDescription": "You have opted out of analytics data collection. You can {changeAnalyticsSettingsLink}.", + "analytics.form.analyticsSectionPrivacyPolicy": "Read more about our privacy practices in the {privacyPolicyLink}.", + "analytics.form.changeAnalyticsSettingsLink": "change this setting here", + "analytics.form.dataCollectionDetailsDeviceInfoText": "Operating system, RAM, and disk space.", + "analytics.form.dataCollectionDetailsDeviceInfoTitle": "Device info", + "analytics.form.dataCollectionDetailsTitle": "We collect data on (1) User click behavior and (2) Device information.", + "analytics.form.dataCollectionDetailsUserBehaviorText": "Clicks, page visits, page scrolling, number of wallets, number of native assets, session duration, type of wallets (soft vs hardware wallets), geolocation (country of location), and page performance.", + "analytics.form.dataCollectionDetailsUserBehaviourTitle": "User click behavior", + "analytics.form.dataCollectionSwitchText": "Allow anonymous data collection", + "analytics.form.description": "All data is anonymous and is used only for product development purposes.", + "analytics.form.privacyPolicyLink": "Daedalus Privacy Policy", + "analytics.form.title": "Analytics data collection", "api.errors.ApiMethodNotYetImplementedError": "This API method is not yet implemented.", "api.errors.CanNotCalculateTransactionFeesError": "Cannot calculate fees while there are pending transactions.", "api.errors.ForbiddenMnemonicError": "Invalid recovery phrase. Submitted recovery phrase is one of the example recovery phrases from the documentation and should not be used for wallets holding funds.", diff --git a/source/renderer/app/i18n/locales/ja-JP.json b/source/renderer/app/i18n/locales/ja-JP.json index 773c7ed226..173dca13a1 100755 --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -1,6 +1,23 @@ { "ImageUploadWidget.clickToUploadLabel": "!!!or click to upload", "ImageUploadWidget.dropFileHint": "!!!Drop file here", + "analytics.dialog.collapseButton": "詳細を隠す", + "analytics.dialog.expandButton": "詳細を表示する", + "analytics.dialog.skipButton": "スキップする", + "analytics.form.allowButton": "許可する", + "analytics.form.analyticsAcceptedDescription": "分析データの収集が設定されています。この設定は{changeAnalyticsSettingsLink}できます。", + "analytics.form.analyticsDeclinedDescription": "分析データの収集が解除されています。この設定は{changeAnalyticsSettingsLink}できます。", + "analytics.form.analyticsSectionPrivacyPolicy": "プライバシーに関する詳細は、{privacyPolicyLink}をご覧ください。", + "analytics.form.changeAnalyticsSettingsLink": "ここから変更", + "analytics.form.dataCollectionDetailsDeviceInfoText": "オペレーティングシステム、RAM、ディスク空き容量", + "analytics.form.dataCollectionDetailsDeviceInfoTitle": "デバイス情報", + "analytics.form.dataCollectionDetailsTitle": "(1)ユーザーのクリック行動と(2)デバイス情報に関するデータを収集します。", + "analytics.form.dataCollectionDetailsUserBehaviorText": "クリック数、ページへのアクセス、ページのスクロール、ウォレットの数、ネイティブ資産の数、セッションの期間、ウォレットの種類(ソフトウォレット、ハードウェアウォレット)、位置情報(国)、ページのパフォーマンス", + "analytics.form.dataCollectionDetailsUserBehaviourTitle": "ユーザーのクリック行動", + "analytics.form.dataCollectionSwitchText": "匿名データ収集を許可する", + "analytics.form.description": "分析データは、製品開発の目的でのみ使用されます。", + "analytics.form.privacyPolicyLink": "Daedalusプライバシーポリシー", + "analytics.form.title": "分析データの収集", "api.errors.ApiMethodNotYetImplementedError": "このAPIはまだ実装されていません。", "api.errors.CanNotCalculateTransactionFeesError": "処理中のトランザクションがあるため、手数料を計算できません。", "api.errors.ForbiddenMnemonicError": "ウォレットの復元フレーズが無効です。送信された復元フレーズは参照用復元フレーズ例の1つです。資金を保有しているウォレットには使用できません。", diff --git a/source/renderer/app/routes-config.ts b/source/renderer/app/routes-config.ts index 6ba8dbbf3b..a25b0aa24f 100644 --- a/source/renderer/app/routes-config.ts +++ b/source/renderer/app/routes-config.ts @@ -16,6 +16,7 @@ export const ROUTES = { PROFILE: { INITIAL_SETTINGS: '/profile/initial-settings', TERMS_OF_USE: '/profile/terms-of-service', + ANALYTICS: '/profile/analytics', DATA_LAYER_MIGRATION: '/profile/data-layer-migration', }, WALLETS: { diff --git a/source/renderer/app/stores/AppStore.ts b/source/renderer/app/stores/AppStore.ts index c55152a255..75968db700 100644 --- a/source/renderer/app/stores/AppStore.ts +++ b/source/renderer/app/stores/AppStore.ts @@ -172,7 +172,8 @@ export default class AppStore extends Store { get isSetupPage(): boolean { return ( this.currentRoute === ROUTES.PROFILE.INITIAL_SETTINGS || - this.currentRoute === ROUTES.PROFILE.TERMS_OF_USE + this.currentRoute === ROUTES.PROFILE.TERMS_OF_USE || + this.currentRoute === ROUTES.PROFILE.ANALYTICS ); } diff --git a/source/renderer/app/stores/ProfileStore.ts b/source/renderer/app/stores/ProfileStore.ts index cf7f724a19..ea8c53ee06 100644 --- a/source/renderer/app/stores/ProfileStore.ts +++ b/source/renderer/app/stores/ProfileStore.ts @@ -1,6 +1,8 @@ import { action, observable, computed, runInAction } from 'mobx'; import BigNumber from 'bignumber.js'; import { includes, camelCase } from 'lodash'; +import axios from 'axios'; +import { v4 as uuidv4 } from 'uuid'; import { toJS } from '../../../common/utils/helper'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; @@ -41,6 +43,7 @@ import { PROFILE_SETTINGS, } from '../config/profileConfig'; import { buildSystemInfo } from '../utils/buildSystemInfo'; +import { AnalyticsAcceptanceStatus } from '../analytics/types'; export default class ProfileStore extends Store { @observable @@ -104,13 +107,19 @@ export default class ProfileStore extends Store { this.api.localStorage.setTermsOfUseAcceptance ); @observable - getDataLayerMigrationAcceptanceRequest: Request = new Request( - // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'ProfileStor... Remove this comment to see the full error message + getAnalyticsAcceptanceRequest: Request< + AnalyticsAcceptanceStatus + > = new Request(this.api.localStorage.getAnalyticsAcceptance); + @observable + setAnalyticsAcceptanceRequest: Request = new Request( + this.api.localStorage.setAnalyticsAcceptance + ); + @observable + getDataLayerMigrationAcceptanceRequest: Request = new Request( this.api.localStorage.getDataLayerMigrationAcceptance ); @observable setDataLayerMigrationAcceptanceRequest: Request = new Request( - // @ts-ignore ts-migrate(2339) FIXME: Property 'api' does not exist on type 'ProfileStor... Remove this comment to see the full error message this.api.localStorage.setDataLayerMigrationAcceptance ); @observable @@ -124,8 +133,7 @@ export default class ProfileStore extends Store { @observable error: LocalizableError | null | undefined = null; @observable - // @ts-ignore ts-migrate(2739) FIXME: Type '{}' is missing the following properties from... Remove this comment to see the full error message - logFiles: LogFiles = {}; + logFiles: LogFiles = null; @observable compressedLogsFilePath: string | null | undefined = null; @observable @@ -147,6 +155,7 @@ export default class ProfileStore extends Store { this._finishInitialScreenSettings ); profileActions.updateUserLocalSetting.listen(this._updateUserLocalSetting); + profileActions.acceptAnalytics.listen(this._setAnalyticsAcceptanceStatus); profileActions.acceptTermsOfUse.listen(this._acceptTermsOfUse); profileActions.acceptDataLayerMigration.listen( this._acceptDataLayerMigration @@ -163,13 +172,17 @@ export default class ProfileStore extends Store { this.registerReactions([ this._updateBigNumberFormat, this._redirectToInitialSettingsIfNoLocaleSet, + this._redirectToAnalyticsScreenIfNotConfirmed, this._redirectToTermsOfUseScreenIfTermsNotAccepted, // this._redirectToDataLayerMigrationScreenIfMigrationHasNotAccepted, + this._redirectToMainUiAfterAnalyticsAreConfirmed, this._redirectToMainUiAfterTermsAreAccepted, this._redirectToMainUiAfterDataLayerMigrationIsAccepted, ]); this._getTermsOfUseAcceptance(); + this._getAnalyticsAcceptance(); + this._getDataLayerMigrationAcceptance(); this._getDesktopDirectoryPath(); @@ -293,6 +306,14 @@ export default class ProfileStore extends Store { return this.getTermsOfUseAcceptanceRequest.result === true; } + @computed + get analyticsAcceptanceStatus(): AnalyticsAcceptanceStatus { + return ( + this.getAnalyticsAcceptanceRequest.result || + AnalyticsAcceptanceStatus.PENDING + ); + } + @computed get hasLoadedDataLayerMigrationAcceptance(): boolean { return ( @@ -303,7 +324,6 @@ export default class ProfileStore extends Store { @computed get isDataLayerMigrationAccepted(): boolean { - // @ts-ignore ts-migrate(2367) FIXME: This condition will always return 'false' since th... Remove this comment to see the full error message return this.getDataLayerMigrationAcceptanceRequest.result === true; } @@ -342,7 +362,6 @@ export default class ProfileStore extends Store { const consolidatedValue = // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message value || (this as any)[camelCase(['current', param])]; - // @ts-ignore ts-migrate(2339) FIXME: Property 'set' does not exist on type 'LocalizedRe... Remove this comment to see the full error message const { set, get } = getRequestKeys(param, this.currentLocale); await (this as any)[set].execute(consolidatedValue); await (this as any)[get].execute(); @@ -365,9 +384,15 @@ export default class ProfileStore extends Store { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message await this.getTermsOfUseAcceptanceRequest.execute(); }; - _getTermsOfUseAcceptance = async () => { - // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message - await this.getTermsOfUseAcceptanceRequest.execute(); + _getTermsOfUseAcceptance = () => { + this.getTermsOfUseAcceptanceRequest.execute(); + }; + _setAnalyticsAcceptanceStatus = (status: AnalyticsAcceptanceStatus) => { + this.setAnalyticsAcceptanceRequest.execute(status); + this.getAnalyticsAcceptanceRequest.execute(); + }; + _getAnalyticsAcceptance = () => { + this.getAnalyticsAcceptanceRequest.execute(); }; _acceptDataLayerMigration = async () => { // @ts-ignore ts-migrate(1320) FIXME: Type of 'await' operand must either be a valid pro... Remove this comment to see the full error message @@ -419,9 +444,22 @@ export default class ProfileStore extends Store { }); } }; + _isOnAnalyticsPage = () => + this.stores.app.currentRoute === ROUTES.PROFILE.ANALYTICS; _isOnTermsOfUsePage = () => - // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'ProfileS... Remove this comment to see the full error message this.stores.app.currentRoute === ROUTES.PROFILE.TERMS_OF_USE; + _redirectToAnalyticsScreenIfNotConfirmed = () => { + if ( + !this.isInitialScreen && + this.isCurrentLocaleSet && + this.areTermsOfUseAccepted && + this.analyticsAcceptanceStatus === AnalyticsAcceptanceStatus.PENDING + ) { + this.actions.router.goToRoute.trigger({ + route: ROUTES.PROFILE.ANALYTICS, + }); + } + }; _redirectToDataLayerMigrationScreenIfMigrationHasNotAccepted = () => { // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'ProfileS... Remove this comment to see the full error message const { isConnected } = this.stores.networkStatus; @@ -433,6 +471,7 @@ export default class ProfileStore extends Store { isConnected && this.isCurrentLocaleSet && this.areTermsOfUseAccepted && + this.analyticsAcceptanceStatus !== AnalyticsAcceptanceStatus.PENDING && // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'ProfileS... Remove this comment to see the full error message this.stores.wallets.hasLoadedWallets && dataLayerMigrationNotAccepted @@ -456,6 +495,14 @@ export default class ProfileStore extends Store { this._redirectToRoot(); } }; + _redirectToMainUiAfterAnalyticsAreConfirmed = () => { + if ( + this.analyticsAcceptanceStatus !== AnalyticsAcceptanceStatus.PENDING && + this._isOnAnalyticsPage() + ) { + this._redirectToRoot(); + } + }; _redirectToMainUiAfterDataLayerMigrationIsAccepted = () => { if ( this.isDataLayerMigrationAccepted && @@ -553,7 +600,6 @@ export default class ProfileStore extends Store { // Collect all relevant state snapshot params and send them for log file creation _setStateSnapshotLog = async () => { try { - // @ts-ignore ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. logger.info('ProfileStore: Requesting state snapshot log file creation'); // @ts-ignore ts-migrate(2339) FIXME: Property 'stores' does not exist on type 'ProfileS... Remove this comment to see the full error message const { networkStatus } = this.stores; diff --git a/source/renderer/app/stores/UiDialogsStore.ts b/source/renderer/app/stores/UiDialogsStore.ts index cbee6bb1a9..5585eef1b0 100644 --- a/source/renderer/app/stores/UiDialogsStore.ts +++ b/source/renderer/app/stores/UiDialogsStore.ts @@ -1,9 +1,10 @@ +import { ReactNode } from 'react'; import { observable, action } from 'mobx'; import Store from './lib/Store'; export default class UiDialogsStore extends Store { @observable - activeDialog: ((...args: Array) => any) | null | undefined = null; + activeDialog: ReactNode | null = null; @observable secondsSinceActiveDialogIsOpen = 0; @observable @@ -20,8 +21,7 @@ export default class UiDialogsStore extends Store { ); } - isOpen = (dialog: (...args: Array) => any): boolean => - this.activeDialog === dialog; + isOpen = (dialog: ReactNode): boolean => this.activeDialog === dialog; countdownSinceDialogOpened = (countDownTo: number) => Math.max(countDownTo - this.secondsSinceActiveDialogIsOpen, 0); @action diff --git a/source/renderer/app/themes/daedalus/cardano.ts b/source/renderer/app/themes/daedalus/cardano.ts index 0387de1a91..2a61b3b0ae 100644 --- a/source/renderer/app/themes/daedalus/cardano.ts +++ b/source/renderer/app/themes/daedalus/cardano.ts @@ -17,6 +17,9 @@ export const CARDANO_THEME_OUTPUT = { '--theme-about-window-icon-close-button-color': '#fff', '--theme-about-window-icon-close-hover-background': 'rgba(0, 0, 0, 0.2)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(68, 91, 124, 0.05)', + }, appUpdate: { '--theme-node-update-background-color': '#efefef', '--theme-node-update-title-color': '#5e6066', @@ -168,12 +171,7 @@ export const CARDANO_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#fff', }, dapps: { - '--theme-dapp-transaction-request-separator': '#5e6066', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(94, 96, 102, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(94, 96, 102, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': 'rgba(32, 34, 37, 0.96)', @@ -1119,10 +1117,6 @@ export const CARDANO_THEME_OUTPUT = { '--theme-wallet-restore-dialog-step-walletType-hardwareWalletDisclaimer-text-color': '#ea4c5b', }, - walletSettings: { - '--theme-wallet-settings-section-separator-color': - 'rgba(94, 96, 102, 0.15)', - }, widgets: { '--theme-widgets-asset-token-fingerprint-background-color': 'rgba(94,96,102,0.1)', @@ -1130,6 +1124,11 @@ export const CARDANO_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': 'rgba(255, 255, 255, 0.97)', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#5e6066', + '--theme-widgets-separator-color': 'rgba(94, 96, 102, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(94, 96, 102, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(94, 96, 102, 0.05)', }, }; const CARDANO_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/dark-blue.ts b/source/renderer/app/themes/daedalus/dark-blue.ts index fac12d93dd..e7e913ec3f 100644 --- a/source/renderer/app/themes/daedalus/dark-blue.ts +++ b/source/renderer/app/themes/daedalus/dark-blue.ts @@ -17,6 +17,9 @@ export const DARK_BLUE_THEME_OUTPUT = { '--theme-about-window-icon-close-button-color': '#fff', '--theme-about-window-icon-close-hover-background': 'rgba(0, 0, 0, 0.2)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(233, 244, 254, 0.05)', + }, appUpdate: { '--theme-node-update-background-color': '#536370', '--theme-node-update-title-color': '#e9f4fe', @@ -167,12 +170,7 @@ export const DARK_BLUE_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#e9f4fe', }, dapps: { - '--theme-dapp-transaction-request-separator': '#e9f4fe', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(233, 244, 254, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(233, 244, 254, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': 'rgba(38, 51, 69, 0.96)', @@ -1123,10 +1121,6 @@ export const DARK_BLUE_THEME_OUTPUT = { '--theme-wallet-restore-dialog-step-walletType-hardwareWalletDisclaimer-text-color': '#ea4c5b', }, - walletSettings: { - '--theme-wallet-settings-section-separator-color': - 'rgba(233, 244, 254, 0.15)', - }, widgets: { '--theme-widgets-asset-token-fingerprint-background-color': 'rgba(102,122,138,0.1)', @@ -1134,6 +1128,11 @@ export const DARK_BLUE_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': 'rgba(83, 99, 112, 0.98)', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#e9f4fe', + '--theme-widgets-separator-color': 'rgba(233, 244, 254, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(233, 244, 254, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(233, 244, 254, 0.05)', }, }; const DARK_BLUE_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/dark-cardano.ts b/source/renderer/app/themes/daedalus/dark-cardano.ts index 946a8837e0..513eb9489e 100644 --- a/source/renderer/app/themes/daedalus/dark-cardano.ts +++ b/source/renderer/app/themes/daedalus/dark-cardano.ts @@ -15,6 +15,9 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-about-window-icon-close-button-color': '#fafbfc', '--theme-about-window-icon-close-hover-background': 'rgba(0, 0, 0, 0.16)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(255, 255, 255, 0.07)', + }, appUpdate: { '--theme-node-update-background-color': '#121326', '--theme-node-update-title-color': '#ffffff', @@ -159,12 +162,7 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#fff', }, dapps: { - '--theme-dapp-transaction-request-separator': '#ffffff', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(255, 255, 255, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(255, 255, 255, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': '#36374d', @@ -1109,10 +1107,6 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-wallet-restore-dialog-step-walletType-hardwareWalletDisclaimer-text-color': '#ea4c5b', }, - walletSettings: { - '--theme-wallet-settings-section-separator-color': - 'rgba(255, 255, 255, 0.15)', - }, widgets: { '--theme-widgets-asset-token-fingerprint-background-color': 'rgba(255,255,255,0.1)', @@ -1120,6 +1114,11 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': 'rgba(42, 43, 60, 0.98)', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#ffffff', + '--theme-widgets-separator-color': 'rgba(255, 255, 255, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(255, 255, 255, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(255, 255, 255, 0.05)', }, }; const DARK_CARDANO_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/flight-candidate.ts b/source/renderer/app/themes/daedalus/flight-candidate.ts index ddb94c2a95..de3e5201c0 100644 --- a/source/renderer/app/themes/daedalus/flight-candidate.ts +++ b/source/renderer/app/themes/daedalus/flight-candidate.ts @@ -15,6 +15,9 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-about-window-icon-close-button-color': '#fafbfc', '--theme-about-window-icon-close-hover-background': 'rgba(0, 0, 0, 0.16)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(255, 255, 255, 0.07)', + }, appUpdate: { '--theme-node-update-background-color': '#121326', '--theme-node-update-title-color': '#ffffff', @@ -159,12 +162,7 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#fff', }, dapps: { - '--theme-dapp-transaction-request-separator': '#ffffff', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(255, 255, 255, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(255, 255, 255, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': '#36374d', @@ -1107,10 +1105,6 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-wallet-restore-dialog-step-walletType-hardwareWalletDisclaimer-text-color': '#ea4c5b', }, - walletSettings: { - '--theme-wallet-settings-section-separator-color': - 'rgba(255, 255, 255, 0.15)', - }, widgets: { '--theme-widgets-asset-token-fingerprint-background-color': 'rgba(255,255,255,0.1)', @@ -1118,6 +1112,11 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': 'rgba(42, 43, 60, 0.98)', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#ffffff', + '--theme-widgets-separator-color': 'rgba(255, 255, 255, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(255, 255, 255, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(255, 255, 255, 0.05)', }, }; const FLIGHT_CANDIDATE_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/incentivized-testnet.ts b/source/renderer/app/themes/daedalus/incentivized-testnet.ts index 0f10dd23b2..777714f59c 100644 --- a/source/renderer/app/themes/daedalus/incentivized-testnet.ts +++ b/source/renderer/app/themes/daedalus/incentivized-testnet.ts @@ -15,6 +15,9 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--theme-about-window-icon-close-button-color': '#fafbfc', '--theme-about-window-icon-close-hover-background': 'rgba(0, 0, 0, 0.16)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(255, 255, 255, 0.07)', + }, appUpdate: { '--theme-node-update-background-color': '#121326', '--theme-node-update-title-color': '#ffffff', @@ -160,12 +163,7 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#fff', }, dapps: { - '--theme-dapp-transaction-request-separator': '#ffffff', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(255, 255, 255, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(255, 255, 255, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': '#36374d', @@ -1120,6 +1118,11 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': 'rgba(42, 43, 60, 0.98)', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#ffffff', + '--theme-widgets-separator-color': 'rgba(255, 255, 255, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(255, 255, 255, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(255, 255, 255, 0.05)', }, }; const INCENTIVIZED_TESTNET_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/light-blue.ts b/source/renderer/app/themes/daedalus/light-blue.ts index ecc36039ce..51d360bdaf 100644 --- a/source/renderer/app/themes/daedalus/light-blue.ts +++ b/source/renderer/app/themes/daedalus/light-blue.ts @@ -17,6 +17,9 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-about-window-icon-close-button-color': '#fff', '--theme-about-window-icon-close-hover-background': 'rgba(0, 0, 0, 0.2)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(68, 91, 124, 0.05)', + }, appUpdate: { '--theme-node-update-background-color': '#ebeff2', '--theme-node-update-title-color': '#5e6066', @@ -167,12 +170,7 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#fafbfc', }, dapps: { - '--theme-dapp-transaction-request-separator': '#5e6066', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(94, 96, 102, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(94, 96, 102, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': '#243E62', @@ -358,7 +356,7 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-input-text-color': '#5e6066', }, link: { - '--theme-link-main-color': 'rgb(41, 111, 208)', + '--theme-link-main-color': '#296fd0', }, loading: { '--theme-loading-background-color': '#fafbfc', @@ -1114,10 +1112,6 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-wallet-restore-dialog-step-walletType-hardwareWalletDisclaimer-text-color': '#ea4c5b', }, - walletSettings: { - '--theme-wallet-settings-section-separator-color': - 'rgba(94, 96, 102, 0.15)', - }, widgets: { '--theme-widgets-asset-token-fingerprint-background-color': 'rgba(94,96,102,0.1)', @@ -1125,6 +1119,11 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': 'rgba(255, 255, 255, 0.97)', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#5e6066', + '--theme-widgets-separator-color': 'rgba(94, 96, 102, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(94, 96, 102, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(94, 96, 102, 0.05)', }, }; const LIGHT_BLUE_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/shelley-testnet.ts b/source/renderer/app/themes/daedalus/shelley-testnet.ts index a04ecc862c..35d19ecadc 100644 --- a/source/renderer/app/themes/daedalus/shelley-testnet.ts +++ b/source/renderer/app/themes/daedalus/shelley-testnet.ts @@ -15,6 +15,9 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--theme-about-window-icon-close-button-color': '#fafbfc', '--theme-about-window-icon-close-hover-background': 'rgba(0, 0, 0, 0.16)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(255, 255, 255, 0.07)', + }, appUpdate: { '--theme-node-update-background-color': '#121326', '--theme-node-update-title-color': '#ffffff', @@ -159,12 +162,7 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#fff', }, dapps: { - '--theme-dapp-transaction-request-separator': '#ffffff', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(255, 255, 255, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(255, 255, 255, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': '#36374d', @@ -1107,10 +1105,6 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--theme-wallet-restore-dialog-step-walletType-hardwareWalletDisclaimer-text-color': '#ea4c5b', }, - walletSettings: { - '--theme-wallet-settings-section-separator-color': - 'rgba(255, 255, 255, 0.15)', - }, widgets: { '--theme-widgets-asset-token-fingerprint-background-color': 'rgba(255,255,255,0.1)', @@ -1118,6 +1112,11 @@ export const SHELLEY_TESTNET_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': 'rgba(42, 43, 60, 0.98)', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#ffffff', + '--theme-widgets-separator-color': 'rgba(255, 255, 255, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(255, 255, 255, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(255, 255, 255, 0.05)', }, }; const SHELLEY_TESTNET_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/white.ts b/source/renderer/app/themes/daedalus/white.ts index 5bc117b24c..f3ece4c884 100644 --- a/source/renderer/app/themes/daedalus/white.ts +++ b/source/renderer/app/themes/daedalus/white.ts @@ -16,6 +16,9 @@ export const WHITE_THEME_OUTPUT = { '--theme-about-window-icon-close-hover-background': 'rgba(41, 181, 149, 0.1)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(94, 96, 102, 0.07)', + }, appUpdate: { '--theme-node-update-background-color': '#f9f9f9', '--theme-node-update-title-color': '#2d2d2d', @@ -162,12 +165,7 @@ export const WHITE_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#2d2d2d', }, dapps: { - '--theme-dapp-transaction-request-separator': '#2d2d2d', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(45, 45, 45, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(45, 45, 45, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': '#ffffff', @@ -1109,9 +1107,6 @@ export const WHITE_THEME_OUTPUT = { '--theme-wallet-restore-dialog-step-walletType-hardwareWalletDisclaimer-text-color': '#ea4c5b', }, - walletSettings: { - '--theme-wallet-settings-section-separator-color': 'rgba(45, 45, 45, 0.15)', - }, widgets: { '--theme-widgets-asset-token-fingerprint-background-color': 'rgba(45,45,45,0.1)', @@ -1119,6 +1114,11 @@ export const WHITE_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': '#fff', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#2d2d2d', + '--theme-widgets-separator-color': 'rgba(45, 45, 45, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(45, 45, 45, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(45, 45, 45, 0.05)', }, }; const WHITE_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/themes/daedalus/yellow.ts b/source/renderer/app/themes/daedalus/yellow.ts index cc66661e19..bd1c7e718b 100644 --- a/source/renderer/app/themes/daedalus/yellow.ts +++ b/source/renderer/app/themes/daedalus/yellow.ts @@ -15,6 +15,9 @@ export const YELLOW_THEME_OUTPUT = { '--theme-about-window-icon-close-button-color': '#2d2d2d', '--theme-about-window-icon-close-hover-background': 'rgba(0, 0, 0, 0.1)', }, + analytics: { + '--theme-analytics-toc-details-background': 'rgba(45, 45, 45, 0.07)', + }, appUpdate: { '--theme-node-update-background-color': '#f8f3ed', '--theme-node-update-title-color': '#2d2d2d', @@ -164,12 +167,7 @@ export const YELLOW_THEME_OUTPUT = { '--theme-connecting-sync-progress-color': '#2d2d2d', }, dapps: { - '--theme-dapp-transaction-request-separator': '#2d2d2d', '--theme-dapp-transaction-request-fees-text-color': '#ea4c5b', - '--theme-dapp-transaction-request-toggle-button-background-color': - 'rgba(45, 45, 45, 0.1)', - '--theme-dapp-transaction-request-code-background-color': - 'rgba(45, 45, 45, 0.05)', }, dataMigration: { '--theme-data-migration-layer-background-color': 'rgba(255, 185, 35, 0.96)', @@ -1101,9 +1099,6 @@ export const YELLOW_THEME_OUTPUT = { '--theme-wallet-restore-dialog-step-walletType-hardwareWalletDisclaimer-text-color': '#ea4c5b', }, - walletSettings: { - '--theme-wallet-settings-section-separator-color': 'rgba(45, 45, 45, 0.15)', - }, widgets: { '--theme-widgets-asset-token-fingerprint-background-color': 'rgba(45,45,45,0.1)', @@ -1111,6 +1106,11 @@ export const YELLOW_THEME_OUTPUT = { '--theme-widgets-asset-token-background-color': '#ffffff', '--theme-widgets-asset-token-box-shadow': 'rgba(0, 0, 0, 0.25)', '--theme-widgets-itemsDropdown-option-label-text-color': '#2d2d2d', + '--theme-widgets-separator-color': 'rgba(45, 45, 45, 0.15)', + '--theme-widgets-collapsible-section-button-background-color': + 'rgba(45, 45, 45, 0.1)', + '--theme-widgets-monospace-text-block-background-color': + 'rgba(45, 45, 45, 0.05)', }, }; const YELLOW_THEME_PARAMS: CreateThemeParams = { diff --git a/source/renderer/app/types/applicationDialogTypes.ts b/source/renderer/app/types/applicationDialogTypes.ts index 90735c3c8b..a84f18a883 100644 --- a/source/renderer/app/types/applicationDialogTypes.ts +++ b/source/renderer/app/types/applicationDialogTypes.ts @@ -2,4 +2,5 @@ export type ApplicationDialog = | 'ABOUT_DIALOG' | 'DAEDALUS_DIAGNOSTICS_DIALOG' | 'TOGGLE_RTS_FLAGS_MODE_DIALOG' + | 'ITN_REWARDS_REDEMPTION_DIALOG' | null; diff --git a/source/renderer/app/utils/storesUtils.ts b/source/renderer/app/utils/storesUtils.ts index 769c04b30f..68ae237654 100644 --- a/source/renderer/app/utils/storesUtils.ts +++ b/source/renderer/app/utils/storesUtils.ts @@ -6,7 +6,7 @@ import type { Locale } from '../../../common/types/locales.types'; export const getRequestKeys = ( param: string, currentLocale: Locale -): Request => { +): { get: string; set: string } => { const currentLanguage = findKey(LOCALES, (l) => l === currentLocale); const languageSufix = param === 'dateFormat' ? currentLanguage : ''; // @ts-ignore ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message diff --git a/source/renderer/index.ejs b/source/renderer/index.ejs index a4a1f0b727..7ee5a3f289 100644 --- a/source/renderer/index.ejs +++ b/source/renderer/index.ejs @@ -9,11 +9,12 @@ Daedalus