diff --git a/packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.scss b/packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.scss index 1f1aa86d..241ddff9 100644 --- a/packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.scss +++ b/packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.scss @@ -138,6 +138,7 @@ margin: rem(1px); width: rem(106px); height: rem(106px); + background-color: $color-main-bg; &.wide { width: rem(160px); diff --git a/packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.tsx b/packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.tsx index 0f93fb24..7e316681 100644 --- a/packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.tsx +++ b/packages/browser-wallet/src/popup/popupX/page-layouts/MainLayout/Header/Header.tsx @@ -25,10 +25,10 @@ export default function Header({ accountOpen, setAccountOpen, }: HeaderProps) { - if (menuOpen) { - setAccountOpen(false); - } useEffect(() => { + if (menuOpen) { + setAccountOpen(false); + } if (menuOpen || accountOpen) { background?.classList.add('fade-bg'); } else { diff --git a/packages/browser-wallet/src/popup/popupX/pages/ChangePasscode/ChangePasscode.scss b/packages/browser-wallet/src/popup/popupX/pages/ChangePasscode/ChangePasscode.scss index 8b72c633..f716bd79 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/ChangePasscode/ChangePasscode.scss +++ b/packages/browser-wallet/src/popup/popupX/pages/ChangePasscode/ChangePasscode.scss @@ -1,7 +1,13 @@ .change-passcode-x { - .divider { - display: block; - margin: rem(16px) 0 rem(24px) 0; - border-bottom: 1px solid rgba($color-white, 0.1); + .change-passcode-page__form { + display: flex; + flex-direction: column; + gap: rem(8px); + + .divider { + display: block; + margin: rem(8px) 0 rem(16px) 0; + border-bottom: 1px solid rgba($color-white, 0.1); + } } } diff --git a/packages/browser-wallet/src/popup/popupX/pages/ChangePasscode/ChangePasscode.tsx b/packages/browser-wallet/src/popup/popupX/pages/ChangePasscode/ChangePasscode.tsx index 5ba298e4..d2ccbabb 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/ChangePasscode/ChangePasscode.tsx +++ b/packages/browser-wallet/src/popup/popupX/pages/ChangePasscode/ChangePasscode.tsx @@ -54,7 +54,7 @@ export default function ChangePasscode() {
; } + +export default withPasswordProtected(Loader, { + headingKey: 'privateKey.accountPrivateKey', + pageInfoKey: 'privateKey.passwordDescription', + submitKey: 'privateKey.showPrivateKey', +}); diff --git a/packages/browser-wallet/src/popup/popupX/pages/PrivateKey/i18n/en.ts b/packages/browser-wallet/src/popup/popupX/pages/PrivateKey/i18n/en.ts index a4289fe8..84f19520 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/PrivateKey/i18n/en.ts +++ b/packages/browser-wallet/src/popup/popupX/pages/PrivateKey/i18n/en.ts @@ -4,6 +4,8 @@ const t = { 'Your account private key is the access key to all the funds in your account. Copy it and keep it safe. To avoid mistakes, do not write it down manually.', export: 'Export', copyKey: 'Copy account private key', + passwordDescription: 'Please enter your passcode to show the private key', + showPrivateKey: 'Show private key', }; export default t; diff --git a/packages/browser-wallet/src/popup/popupX/pages/SeedPhrase/SeedPhrase.tsx b/packages/browser-wallet/src/popup/popupX/pages/SeedPhrase/SeedPhrase.tsx index eb1dca1d..9e752503 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/SeedPhrase/SeedPhrase.tsx +++ b/packages/browser-wallet/src/popup/popupX/pages/SeedPhrase/SeedPhrase.tsx @@ -5,14 +5,33 @@ import Page from '@popup/popupX/shared/Page'; import Text from '@popup/popupX/shared/Text'; import Card from '@popup/popupX/shared/Card'; import Copy from '@assets/svgX/copy.svg'; +import { useAsyncMemo } from 'wallet-common-helpers'; +import { decrypt } from '@shared/utils/crypto'; +import { useAtomValue } from 'jotai'; +import { encryptedSeedPhraseAtom, sessionPasscodeAtom } from '@popup/store/settings'; +import { copyToClipboard } from '@popup/popupX/shared/utils/helpers'; +import { withPasswordProtected } from '@popup/popupX/shared/utils/hoc'; -const RECOVERY_PHRASE = - 'meadow salad weather rural next promote fence mass leopard mail regret mushroom love coral viable layer lumber soft setup radar oppose miracle rural agree'.split( - ' ' +function SeedPhrase() { + const { t } = useTranslation('x', { keyPrefix: 'seedPhrase' }); + const passcode = useAtomValue(sessionPasscodeAtom); + const encryptedSeed = useAtomValue(encryptedSeedPhraseAtom); + + const seedPhrase = useAsyncMemo( + async () => { + if (encryptedSeed.loading || passcode.loading) { + return undefined; + } + if (encryptedSeed.value && passcode.value) { + return decrypt(encryptedSeed.value, passcode.value); + } + throw new Error('SeedPhrase should not be retrieved without unlocking the wallet.'); + }, + undefined, + [encryptedSeed.loading, passcode.loading] ); -export default function SeedPhrase() { - const { t } = useTranslation('x', { keyPrefix: 'seedPhrase' }); + if (!seedPhrase) return null; return ( @@ -20,12 +39,18 @@ export default function SeedPhrase() { {t('seedPhraseDescription')} - {RECOVERY_PHRASE.map((word) => ( - {word} + {seedPhrase.split(' ').map((word) => ( + {word} ))} - } label={t('copy')} /> + } label={t('copy')} onClick={() => copyToClipboard(seedPhrase)} /> ); } + +export default withPasswordProtected(SeedPhrase, { + headingKey: 'seedPhrase.seedPhrase', + pageInfoKey: 'seedPhrase.passwordDescription', + submitKey: 'seedPhrase.showSeedPhrase', +}); diff --git a/packages/browser-wallet/src/popup/popupX/pages/SeedPhrase/i18n/en.ts b/packages/browser-wallet/src/popup/popupX/pages/SeedPhrase/i18n/en.ts index cea08ba2..ef44dfb9 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/SeedPhrase/i18n/en.ts +++ b/packages/browser-wallet/src/popup/popupX/pages/SeedPhrase/i18n/en.ts @@ -3,6 +3,8 @@ const t = { seedPhraseDescription: 'Your seed phrase is the access key to all the funds in your wallet. If you forget it you will lose access to your wallet(s). Keep it somewhere safe.', copy: 'Copy seed phrase', + passwordDescription: 'Please enter your passcode to show the seed phrase.', + showSeedPhrase: 'Show secret recovery phrase', }; export default t; diff --git a/packages/browser-wallet/src/popup/popupX/shared/Form/Form.tsx b/packages/browser-wallet/src/popup/popupX/shared/Form/Form.tsx index 5ec20f5a..d5c5a9f7 100644 --- a/packages/browser-wallet/src/popup/popupX/shared/Form/Form.tsx +++ b/packages/browser-wallet/src/popup/popupX/shared/Form/Form.tsx @@ -75,7 +75,7 @@ const Form = forwardRef( const internal = useForm({ defaultValues }); const methods = external ?? internal; - const submit = () => (onSubmit === undefined ? noOp : methods.handleSubmit(onSubmit)); + const submit = onSubmit === undefined ? () => noOp : methods.handleSubmit(onSubmit); return ( diff --git a/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.scss b/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.scss new file mode 100644 index 00000000..f897151b --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.scss @@ -0,0 +1,23 @@ +.fullscreen-notice { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + + &__back { + border: none; + background: none; + + &:hover { + background: none !important; + } + } + + &__content { + padding: 0 rem(24px); + overflow: auto; + height: calc(100% - 7.4rem); + } +} diff --git a/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.stories.tsx b/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.stories.tsx new file mode 100644 index 00000000..d50c9386 --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.stories.tsx @@ -0,0 +1,106 @@ +/* eslint-disable react/function-component-definition, react/destructuring-assignment */ +import React from 'react'; +import { Meta, StoryObj } from '@storybook/react'; +import FullscreenNotice from './FullscreenNotice'; +import Page from '../Page'; +import Button from '../Button'; + +export default { + title: 'X/Shared/FullscreenNotice', + component: FullscreenNotice, + beforeEach: () => { + const body = document.getElementsByTagName('body').item(0); + body?.classList.add('popup-x'); + + return () => { + body?.classList.remove('popup-x'); + }; + }, + tags: ['!autodocs'], +} as Meta; + +type Story = StoryObj; + +export const Primary: Story = { + args: { + children: ( + + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. Tincidunt nunc pulvinar sapien et. Mattis aliquam faucibus purus in. + Tristique sollicitudin nibh sit amet commodo nulla facilisi. Eget nullam non nisi est sit amet. At + lectus urna duis convallis. Consequat semper viverra nam libero. Gravida rutrum quisque non tellus + orci ac auctor. Mattis aliquam faucibus purus in massa tempor nec feugiat. Consectetur adipiscing + elit duis tristique sollicitudin. Sit amet est placerat in egestas erat imperdiet sed euismod. + Ornare massa eget egestas purus viverra. Viverra maecenas accumsan lacus vel facilisis. Malesuada + fames ac turpis egestas integer eget aliquet nibh. Non diam phasellus vestibulum lorem sed risus. + Tincidunt vitae semper quis lectus nulla. Cursus euismod quis viverra nibh cras pulvinar mattis nunc + sed. +

+ + + +
+ ), + open: true, + }, +}; + +export const WallOfText: Story = { + args: { + children: ( + <> +

Lorem ipsum!

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. Tincidunt nunc pulvinar sapien et. Mattis aliquam faucibus purus in. + Tristique sollicitudin nibh sit amet commodo nulla facilisi. Eget nullam non nisi est sit amet. At + lectus urna duis convallis. Consequat semper viverra nam libero. Gravida rutrum quisque non tellus + orci ac auctor. Mattis aliquam faucibus purus in massa tempor nec feugiat. Consectetur adipiscing + elit duis tristique sollicitudin. Sit amet est placerat in egestas erat imperdiet sed euismod. + Ornare massa eget egestas purus viverra. Viverra maecenas accumsan lacus vel facilisis. Malesuada + fames ac turpis egestas integer eget aliquet nibh. Non diam phasellus vestibulum lorem sed risus. + Tincidunt vitae semper quis lectus nulla. Cursus euismod quis viverra nibh cras pulvinar mattis nunc + sed. +

+

+ Commodo quis imperdiet massa tincidunt nunc pulvinar sapien et ligula. Vitae congue mauris rhoncus + aenean vel elit scelerisque mauris pellentesque. Nec feugiat in fermentum posuere urna. Lobortis + scelerisque fermentum dui faucibus in ornare quam. Sit amet est placerat in egestas erat imperdiet + sed euismod. Nulla aliquet porttitor lacus luctus accumsan tortor posuere. Arcu risus quis varius + quam. Ullamcorper a lacus vestibulum sed arcu non odio. Eu mi bibendum neque egestas. Duis at + consectetur lorem donec. +

+

+ Hendrerit dolor magna eget est lorem ipsum dolor sit amet. Dignissim sodales ut eu sem. Tellus + molestie nunc non blandit massa enim nec. Amet nulla facilisi morbi tempus iaculis urna. Metus + vulputate eu scelerisque felis imperdiet proin. Mauris ultrices eros in cursus turpis massa + tincidunt dui ut. Praesent tristique magna sit amet purus gravida quis blandit turpis. Turpis nunc + eget lorem dolor sed viverra ipsum. Et egestas quis ipsum suspendisse ultrices gravida. Egestas diam + in arcu cursus euismod quis. Egestas purus viverra accumsan in. Convallis convallis tellus id + interdum. Donec pretium vulputate sapien nec sagittis. Consectetur adipiscing elit ut aliquam purus + sit amet luctus venenatis. +

+

+ Eget gravida cum sociis natoque penatibus et. Tincidunt eget nullam non nisi est. Amet mattis + vulputate enim nulla. Eget mi proin sed libero enim sed faucibus turpis in. Mattis ullamcorper velit + sed ullamcorper. Nunc vel risus commodo viverra. At tellus at urna condimentum mattis. At elementum + eu facilisis sed odio. Egestas dui id ornare arcu. Proin libero nunc consequat interdum varius. + Scelerisque eleifend donec pretium vulputate sapien nec. +

+

+ Eget velit aliquet sagittis id consectetur purus ut faucibus pulvinar. Sapien eget mi proin sed + libero enim. Eget velit aliquet sagittis id consectetur. Est placerat in egestas erat. Diam maecenas + ultricies mi eget mauris pharetra et ultrices neque. Ut sem nulla pharetra diam sit amet nisl + suscipit adipiscing. Curabitur vitae nunc sed velit dignissim sodales ut eu sem. Adipiscing at in + tellus integer feugiat scelerisque varius. Ultrices mi tempus imperdiet nulla malesuada pellentesque + elit eget. Quisque sagittis purus sit amet volutpat. Dignissim diam quis enim lobortis scelerisque + fermentum dui. At lectus urna duis convallis convallis tellus id. Ultrices dui sapien eget mi proin + sed. +

+ + ), + open: true, + }, +}; diff --git a/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.tsx b/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.tsx new file mode 100644 index 00000000..e5e9fca5 --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/FullscreenNotice.tsx @@ -0,0 +1,93 @@ +import React, { PropsWithChildren, useCallback, useEffect, useMemo } from 'react'; +import clsx from 'clsx'; +import { Portal, noOp } from 'wallet-common-helpers'; +import Back from '@assets/svgX/arrow-left.svg'; +import { Connection, Fullscreen } from '@popup/popupX/page-layouts/MainLayout/Header/components'; +import Button from '../Button'; + +type HeaderProps = { + isScrolling: boolean; + onBack(): void; +}; + +function Header({ isScrolling, onBack }: HeaderProps) { + return ( +
+
+ + +
+
+ } onClick={() => onBack()} /> +
+
+ ); +} + +const htmlElement = document.getElementsByTagName('html')[0]!; +const bodyElement = document.getElementsByTagName('body')[0]!; + +type Props = { + /** Control whether notice is shown or not */ + open: boolean; + /** Invoked when the notice is closed */ + onClose(): void; +}; + +/** + * @description + * Opens content in a modal overlay on top of the current wallet window. + * + * @example + * setIsOpen(false)}> + * + * + * This is the body + * + * Some action + * + * + * This content is shown in a modal! + * + */ +export default function FullscreenNotice({ open, onClose, children }: PropsWithChildren): JSX.Element | null { + const [scroll, setScroll] = React.useState(0); + const isScrolling = useMemo(() => scroll > 0, [!!scroll]); + const close = useCallback(() => { + onClose(); + }, [onClose]); + + useEffect(() => { + if (open) { + htmlElement.classList.add('modal-open'); + + // Prevent modal from stretching window height + htmlElement.style.height = bodyElement.style.height; + + return () => { + htmlElement.classList.remove('modal-open'); + // Reset to initial value + htmlElement.style.height = '100%'; + }; + } + return noOp; + }, [open]); + + if (!open) { + return null; + } + + return ( + +
+
{ + setScroll(e.currentTarget.scrollTop); + }} + > + {children} +
+ + ); +} diff --git a/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/index.ts b/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/index.ts new file mode 100644 index 00000000..6301de42 --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/FullscreenNotice/index.ts @@ -0,0 +1 @@ +export { default } from './FullscreenNotice'; diff --git a/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/PasswordProtect.scss b/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/PasswordProtect.scss new file mode 100644 index 00000000..9a8fb103 --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/PasswordProtect.scss @@ -0,0 +1,5 @@ +.confirm-password-x { + #confirm-password-form { + margin-top: rem(30px); + } +} diff --git a/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/PasswordProtect.tsx b/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/PasswordProtect.tsx new file mode 100644 index 00000000..2df9ed1e --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/PasswordProtect.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { Validate } from 'react-hook-form'; +import { useAtomValue } from 'jotai'; +import { useTranslation } from 'react-i18next'; +import Page from '@popup/popupX/shared/Page'; +import Text from '@popup/popupX/shared/Text'; +import Button from '@popup/popupX/shared/Button'; +import FormPassword from '@popup/popupX/shared/Form/Password'; +import Form from '@popup/popupX/shared/Form/Form'; +import { sessionPasscodeAtom } from '@popup/store/settings'; +import { useForm } from '@popup/shared/Form'; +import { TranslationKeyX } from '@popup/shell/i18n/i18n'; + +type FormValues = { + currentPasscode: string; +}; + +export type PasswordProtectConfigType = { + headingKey: TranslationKeyX; + pageInfoKey: TranslationKeyX; + submitKey: TranslationKeyX; +}; + +type PasswordProtectProps = { + setPasswordConfirmed: (passwordConfirmed: boolean) => void; + config: PasswordProtectConfigType; +}; + +export default function PasswordProtect({ + setPasswordConfirmed, + config: { headingKey, pageInfoKey, submitKey }, +}: PasswordProtectProps) { + const { t: tUse } = useTranslation('x'); + const t = (key: TranslationKeyX) => tUse(key) as unknown as string; + const { t: tPasscode } = useTranslation('x', { keyPrefix: 'sharedX.form.password' }); + const passcode = useAtomValue(sessionPasscodeAtom); + const form = useForm(); + + const handleSubmit = () => { + setPasswordConfirmed(true); + }; + + function validateCurrentPasscode(): Validate { + return (currentPasscode) => (currentPasscode !== passcode.value ? tPasscode('incorrectPasscode') : undefined); + } + + return ( + + + + {t(pageInfoKey)} + + {(f) => { + return ( + + ); + }} + + + + + + + ); +} diff --git a/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/index.ts b/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/index.ts new file mode 100644 index 00000000..d64c6e98 --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/PasswordProtect/index.ts @@ -0,0 +1,2 @@ +export { default } from './PasswordProtect'; +export type { PasswordProtectConfigType } from './PasswordProtect'; diff --git a/packages/browser-wallet/src/popup/popupX/shared/i18n/en.ts b/packages/browser-wallet/src/popup/popupX/shared/i18n/en.ts index 558045b2..49b2586a 100644 --- a/packages/browser-wallet/src/popup/popupX/shared/i18n/en.ts +++ b/packages/browser-wallet/src/popup/popupX/shared/i18n/en.ts @@ -5,6 +5,9 @@ const t = { weak: 'Weak', medium: 'Medium', strong: 'Strong', + incorrectPasscode: 'Incorrect passcode', + currentPasscode: 'Enter current passcode', + passcodeRequired: 'A passcode must be entered', }, tokenAmount: { token: { diff --git a/packages/browser-wallet/src/popup/popupX/shared/utils/hoc.tsx b/packages/browser-wallet/src/popup/popupX/shared/utils/hoc.tsx index d8c54b5d..91961cdb 100644 --- a/packages/browser-wallet/src/popup/popupX/shared/utils/hoc.tsx +++ b/packages/browser-wallet/src/popup/popupX/shared/utils/hoc.tsx @@ -1,6 +1,7 @@ -import React from 'react'; +import React, { useState } from 'react'; import { useSelectedCredential } from '@popup/shared/utils/account-helpers'; import Loader from '@popup/popupX/shared/Loader'; +import PasswordProtect, { PasswordProtectConfigType } from '@popup/popupX/shared/PasswordProtect'; export function withSelectedCredential

( Component: React.ComponentType

@@ -16,3 +17,16 @@ export function withSelectedCredential

( } return NewComponent; } + +export function withPasswordProtected(Component: React.ComponentType, config: PasswordProtectConfigType) { + function NewComponent() { + const [passwordConfirmed, setPasswordConfirmed] = useState(false); + + if (!passwordConfirmed) { + return ; + } + + return ; + } + return NewComponent; +} diff --git a/packages/browser-wallet/src/popup/popupX/shared/utils/typescriptHelpers.ts b/packages/browser-wallet/src/popup/popupX/shared/utils/typescriptHelpers.ts new file mode 100644 index 00000000..be18801b --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/utils/typescriptHelpers.ts @@ -0,0 +1,4 @@ +// Generic type for iterating through nested object keys +export type ObjectPath = { + [K in keyof T]: `${D}${Exclude}${'' | (T[K] extends object ? ObjectPath : '')}`; +}[keyof T]; diff --git a/packages/browser-wallet/src/popup/popupX/styles/_elements.scss b/packages/browser-wallet/src/popup/popupX/styles/_elements.scss index 8636c339..1d11dc03 100644 --- a/packages/browser-wallet/src/popup/popupX/styles/_elements.scss +++ b/packages/browser-wallet/src/popup/popupX/styles/_elements.scss @@ -37,10 +37,12 @@ @import '../shared/Text/Text'; @import '../shared/Loader/Loader'; @import '../shared/IdCard/IdCard'; +@import '../shared/PasswordProtect/PasswordProtect'; @import '../shared/Web3IdCard/Web3IdCard'; @import '../shared/Button/Button'; @import '../shared/ExternalLink/ExternalLink'; @import '../shared/Carousel/Carousel'; @import '../shared/Form/Form'; @import '../shared/Form/TokenAmount/TokenAmount'; +@import '../shared/FullscreenNotice/FullscreenNotice'; @import '../shared/EditableValue/EditableValue'; diff --git a/packages/browser-wallet/src/popup/shared/Modal/Modal.stories.tsx b/packages/browser-wallet/src/popup/shared/Modal/Modal.stories.tsx index 6cc9a200..b5a97619 100644 --- a/packages/browser-wallet/src/popup/shared/Modal/Modal.stories.tsx +++ b/packages/browser-wallet/src/popup/shared/Modal/Modal.stories.tsx @@ -6,6 +6,7 @@ import Modal from './Modal'; export default { title: 'Shared/Modal', component: Modal, + tags: ['!autodocs'], } as Meta; type Story = StoryObj; diff --git a/packages/browser-wallet/src/popup/shell/Root.tsx b/packages/browser-wallet/src/popup/shell/Root.tsx index 8135cd16..788ecbb3 100644 --- a/packages/browser-wallet/src/popup/shell/Root.tsx +++ b/packages/browser-wallet/src/popup/shell/Root.tsx @@ -136,9 +136,7 @@ export default function Root() { return ( - + diff --git a/packages/browser-wallet/src/popup/shell/i18n/i18n.ts b/packages/browser-wallet/src/popup/shell/i18n/i18n.ts index ffeaca0e..2dfb128d 100644 --- a/packages/browser-wallet/src/popup/shell/i18n/i18n.ts +++ b/packages/browser-wallet/src/popup/shell/i18n/i18n.ts @@ -3,6 +3,7 @@ import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import countries from 'i18n-iso-countries'; +import { ObjectPath } from '@popup/popupX/shared/utils/typescriptHelpers'; import en from './locales/en'; import da from './locales/da'; @@ -10,6 +11,7 @@ import da from './locales/da'; countries.registerLocale(require('i18n-iso-countries/langs/en.json')); countries.registerLocale(require('i18n-iso-countries/langs/da.json')); +export type TranslationKeyX = ObjectPath; export const defaultNS: keyof typeof en = 'shared'; const ns: Array = Object.keys(en) as Array;