Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ import { useTranslation } from 'react-i18next';
import styles from './MainLoader.module.scss';

export interface MainLoaderProps {
text?: string;
overlay?: boolean;
}

export const MainLoader = ({ text, overlay = false }: MainLoaderProps): React.ReactElement => {
export const MainLoader = ({ overlay = false }: MainLoaderProps): React.ReactElement => {
const { t } = useTranslation();

return (
<div className={classNames([styles.loaderContainer, { [styles.overlay]: overlay }])} data-testid="main-loader">
<Loader className={styles.loader} data-testid="main-loader-image" />
<p className={styles.loaderText} data-testid="main-loader-text">
{text ?? t('general.loading')}
{t('general.loading')}
</p>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
Links,
LockWallet,
NetworkChoise,
LanguageChoice,
LanguageInfo,
RenameWalletDrawer,
Separator,
SettingsLink,
Expand Down Expand Up @@ -98,6 +100,11 @@ export const DropdownMenuOverlay: VFC<Props> = ({
sendAnalyticsEvent(PostHogAction.UserWalletProfileNetworkClick);
};

const handleLanguageChoice = () => {
setCurrentSection(Sections.Language);
sendAnalyticsEvent(PostHogAction.UserWalletProfileLanguageClick);
};

const goBackToMainSection = useCallback(() => setCurrentSection(Sections.Main), []);

useEffect(() => {
Expand Down Expand Up @@ -209,6 +216,7 @@ export const DropdownMenuOverlay: VFC<Props> = ({
/>
</div>
)}
<LanguageChoice onClick={handleLanguageChoice} />
<NetworkChoise onClick={handleNetworkChoise} />
{lockWalletButton && (
<>
Expand All @@ -219,6 +227,7 @@ export const DropdownMenuOverlay: VFC<Props> = ({
</div>
)}
{currentSection === Sections.NetworkInfo && <NetworkInfo onBack={goBackToMainSection} />}
{currentSection === Sections.Language && <LanguageInfo onBack={goBackToMainSection} />}
{currentSection === Sections.WalletAccounts && <WalletAccounts onBack={goBackToMainSection} isPopup={isPopup} />}
{isRenamingWallet && (
<RenameWalletDrawer open={isRenamingWallet} popupView={isPopup} onClose={() => setIsRenamingWallet(false)} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import cn from 'classnames';
import styles from '../DropdownMenuOverlay.module.scss';
import { useBackgroundServiceAPIContext } from '@providers';
import { storage as webStorage } from 'webextension-polyfill';

type LanguageChoiceProps = {
onClick: () => void;
};

export const LanguageChoice = ({ onClick }: LanguageChoiceProps): React.ReactElement => {
const { t } = useTranslation();
const { getBackgroundStorage } = useBackgroundServiceAPIContext();
const [language, setLanguage] = useState<string>();

useEffect(() => {
const getLanguage = async () => {
const { languageChoice } = await getBackgroundStorage();
if (languageChoice) setLanguage(languageChoice);
};
getLanguage();
webStorage.onChanged.addListener(getLanguage);
}, [getBackgroundStorage]);

return (
<div
data-testid="header-menu-language-choice-container"
className={cn(styles.menuItem, styles.cta)}
onClick={() => onClick()}
>
<div className={styles.networkChoise}>
<span data-testid="header-menu-language-choice-label">{t('browserView.topNavigationBar.links.language')}</span>
<span data-testid="header-menu-language-choice-value" className={styles.value}>
{language || 'en'}
</span>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { NavigationButton, PostHogAction } from '@lace/common';
import styles from './NetworkInfo.module.scss';
import { useBackgroundServiceAPIContext } from '@providers';
import { Radio, RadioChangeEvent } from 'antd';
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
import { storage as webStorage } from 'webextension-polyfill';

type LanguageChoiceProps = {
onBack: () => void;
};

const LANG_CHOICES = ['en', 'ja']; // hardcoding for v1 only

export const LanguageInfo = ({ onBack }: LanguageChoiceProps): React.ReactElement => {
const { t } = useTranslation();
const [language, setLanguage] = useState('en'); // default
const posthog = usePostHogClientContext();

const { getBackgroundStorage, handleChangeLanguage } = useBackgroundServiceAPIContext();

useEffect(() => {
const getLanguage = async () => {
const { languageChoice } = await getBackgroundStorage();
if (languageChoice) setLanguage(languageChoice);
};
webStorage.onChanged.addListener(getLanguage);
getLanguage();
}, [getBackgroundStorage]);

const handleLanguageChangeRequest = useCallback(
(e: RadioChangeEvent) => {
handleChangeLanguage(e.target.value);
posthog.sendEvent(PostHogAction.UserWalletProfileLanguageSelectClick, { language: e.target.value });
},
[handleChangeLanguage, posthog]
);

return (
<div data-testid="user-dropdown-language-info-section" className={styles.container}>
<div className={styles.navigation} data-testid="drawer-navigation">
<NavigationButton iconClassName={styles.iconClassName} icon="arrow" onClick={onBack} />
</div>
<div className={styles.titleSection}>
<div data-testid="user-dropdown-language-title" className={styles.title}>
{t('browserView.settings.wallet.language.title')}
</div>
<div data-testid="user-dropdown-language-description" className={styles.subTitle}>
{t('browserView.settings.wallet.language.drawerDescription')}
</div>
</div>
<div className={styles.content} data-testid="user-dropdown-language-choice">
<Radio.Group
className={styles.radioGroup}
onChange={handleLanguageChangeRequest}
value={language}
data-testid={'language-choice-radio-group'}
>
{LANG_CHOICES.map((choice) => (
<Radio key={`language-choice-${choice}`} value={choice}>
{choice}
</Radio>
))}
</Radio.Group>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ export * from './NetworkInfo';
export * from './AddNewWalletLink';
export * from './AddNewBitcoinWalletLink';
export * from './RenameWalletDrawer';
export * from './LanguageInfo';
export * from './LanguageChoice';
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum Sections {
Main = 'main',
NetworkInfo = 'network_info',
WalletAccounts = 'wallet_accounts'
WalletAccounts = 'wallet_accounts',
Language = 'language'
}
25 changes: 25 additions & 0 deletions apps/browser-extension-wallet/src/hooks/useAppInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { setBackgroundStorage } from '@lib/scripts/background/storage';
import { useCustomSubmitApi } from '@hooks/useCustomSubmitApi';
import { bitcoinWalletManager } from '@lib/wallet-api-ui';
import { useCurrentBlockchain } from '@src/multichain';
import { useBackgroundServiceAPIContext } from '@providers';
import { initI18n } from '@lace/translation';
import { Message, MessageTypes } from '@lib/scripts/types';

export const useAppInit = (): void => {
const {
Expand All @@ -26,6 +29,28 @@ export const useAppInit = (): void => {
const walletState = useWalletState();
const { environmentName, currentChain } = useWalletStore();
const { getCustomSubmitApiForNetwork } = useCustomSubmitApi();
const backgroundServices = useBackgroundServiceAPIContext();

useEffect(() => {
const subscription = backgroundServices.requestMessage$?.subscribe(({ type, data }: Message): void => {
if (type === MessageTypes.CHANGE_LANGUAGE) {
initI18n(data);
backgroundServices.setBackgroundStorage({ languageChoice: data });
}
});

backgroundServices
.getBackgroundStorage()
.then((bs) => {
initI18n(bs.languageChoice ?? globalThis.navigator.language ?? 'en');
})
.catch((error) => {
// eslint-disable-next-line no-console
console.log(error);
});

return () => subscription.unsubscribe();
}, [backgroundServices]);

useEffect(() => {
setWalletState(walletState);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const backgroundServiceProperties: RemoteApiProperties<BackgroundService>
handleOpenNamiBrowser: RemoteApiPropertyType.MethodReturningPromise,
closeAllTabsAndOpenPopup: RemoteApiPropertyType.MethodReturningPromise,
handleChangeTheme: RemoteApiPropertyType.MethodReturningPromise,
handleChangeLanguage: RemoteApiPropertyType.MethodReturningPromise,
handleChangeMode: RemoteApiPropertyType.MethodReturningPromise,
clearBackgroundStorage: RemoteApiPropertyType.MethodReturningPromise,
getBackgroundStorage: RemoteApiPropertyType.MethodReturningPromise,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { POPUP_WINDOW_NAMI_TITLE } from '@utils/constants';
import { catchAndBrandExtensionApiError } from '@utils/catch-and-brand-extension-api-error';
import { initCardanoTokenPrices } from './cardanoTokenPrices';
import { pollController$ } from '../session/poll-controller';
import { Language } from '@lace/translation';

export const requestMessage$ = new Subject<Message>();
export const backendFailures$ = new BehaviorSubject(0);
Expand Down Expand Up @@ -181,6 +182,8 @@ const closeAllTabsAndOpenPopup = async () => {

const handleChangeTheme = (data: ChangeThemeData) => requestMessage$.next({ type: MessageTypes.CHANGE_THEME, data });

const handleChangeLanguage = (data: Language) => requestMessage$.next({ type: MessageTypes.CHANGE_LANGUAGE, data });

const handleChangeMode = (data: ChangeModeData) => requestMessage$.next({ type: MessageTypes.CHANGE_MODE, data });

const { ADA_PRICE_CHECK_INTERVAL, SAVED_PRICE_DURATION } = config();
Expand Down Expand Up @@ -352,6 +355,7 @@ export const exposeBackgroundService = (wallet$: Observable<ActiveWallet>): void
migrationState$,
coinPrices,
handleChangeTheme,
handleChangeLanguage,
handleChangeMode,
clearBackgroundStorage,
getBackgroundStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { themes } from '@providers/ThemeProvider';
import { BackgroundStorage, MigrationState } from './storage';
import { CoinPrices } from './prices';
import type { clearBackgroundStorage } from '../background/storage';
import { Language } from '@lace/translation';

export enum BaseChannels {
BACKGROUND_ACTIONS = 'background-actions'
Expand Down Expand Up @@ -31,7 +32,8 @@ export enum MessageTypes {
HTTP_CONNECTION = 'http-connnection',
WS_CONNECTION = 'ws-connnection',
OPEN_COLLATERAL_SETTINGS = 'open-collateral-settings',
CHANGE_MODE = 'change-mode'
CHANGE_MODE = 'change-mode',
CHANGE_LANGUAGE = 'change-language'
}

export enum BrowserViewSections {
Expand Down Expand Up @@ -84,12 +86,22 @@ interface ChangeMode {
type: MessageTypes.CHANGE_MODE;
data: ChangeModeData;
}
interface ChangeThemeMessage {
type: MessageTypes.CHANGE_THEME;
data: ChangeThemeData;
}

interface ChangeLanguageMessage {
type: MessageTypes.CHANGE_LANGUAGE;
data: Language;
}
export type Message =
| ChangeThemeMessage
| HTTPConnectionMessage
| WSConnectionMessage
| OpenBrowserMessage
| ChangeMode;
| ChangeMode
| ChangeLanguageMessage;

export type UnhandledError = {
type: 'error' | 'unhandledrejection';
Expand All @@ -104,6 +116,7 @@ export type BackgroundService = {
migrationState$: BehaviorSubject<MigrationState | undefined>;
coinPrices: CoinPrices;
handleChangeTheme: (data: ChangeThemeData) => void;
handleChangeLanguage: (data: Language) => void;
handleChangeMode: (data: ChangeModeData) => void;
setBackgroundStorage: (data: BackgroundStorage) => Promise<void>;
getBackgroundStorage: () => Promise<BackgroundStorage>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
RawFeatureFlagPayloads
} from '@lib/scripts/types/feature-flags';
import { LogLevelString } from '@lace/common';
import { Language } from '@lace/translation';

export interface PendingMigrationState {
from: string;
Expand Down Expand Up @@ -49,6 +50,7 @@ export interface BackgroundStorage {
dappInjectCompatibilityMode?: boolean;
optedInBeta?: boolean;
logLevel?: LogLevelString;
languageChoice?: Language;
}

export type BackgroundStorageKeys = keyof BackgroundStorage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export const Activity = (): React.ReactElement => {
id: transaction.transactionHash,
formattedTimestamp:
transaction.status === Bitcoin.TransactionStatus.Pending
? 'PENDING'
? t('browserView.activity.entry.name.pending')
: formattedTimestamp(new Date(transaction.timestamp * 1000)),
amount: `${new BigNumber(net.toString()).dividedBy(100_000_000).toFixed(8, BigNumber.ROUND_HALF_UP)} BTC`,
fiatAmount: `${computeBalance(Number(net) / SATS_IN_BTC, fiatCurrency.code, bitcoinPrice)} ${
Expand All @@ -194,7 +194,7 @@ export const Activity = (): React.ReactElement => {
items
};
});
}, [addresses, recentTransactions, bitcoinPrice, explorerBaseUrl, pendingTransaction, fiatCurrency]);
}, [addresses, recentTransactions, bitcoinPrice, explorerBaseUrl, pendingTransaction, fiatCurrency, t]);

const isLoading =
addresses.length === 0 || explorerBaseUrl.length === 0 || currentCursor === null || !activityFetched;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { TextArea } from '@lace/common';
import { ShieldedPgpKeyData } from '@src/types';
import { useWalletOnboarding } from '../../walletOnboardingContext';
import { useAnalyticsContext } from '@providers';
import { useTranslation } from 'react-i18next';

interface Validation {
error?: string;
Expand Down Expand Up @@ -53,6 +54,7 @@ const decryptQrCodeMnemonicWithPrivateKey = async ({ pgpInfo }: DecryptProps): P
};

export const EnterPgpPrivateKey: VFC = () => {
const { t } = useTranslation();
const { postHogActions } = useWalletOnboarding();
const analytics = useAnalyticsContext();
const { back, createWalletData, next, pgpInfo, setPgpInfo, setMnemonic } = useRestoreWallet();
Expand All @@ -74,7 +76,7 @@ export const EnterPgpPrivateKey: VFC = () => {
pgpPrivateKey: e.target.value,
privateKeyIsDecrypted: privateKey.isDecrypted()
});
setValidation({ success: 'valid PGP private key' });
setValidation({ success: t('pgp.validPrivateKey') });
} catch (error) {
if (error.message === 'Misformed armored text') {
setValidation({ error: i18n.t('pgp.error.misformedArmoredText') });
Expand All @@ -84,7 +86,7 @@ export const EnterPgpPrivateKey: VFC = () => {
}
}
},
[setValidation, pgpInfo, setPgpInfo]
[setValidation, pgpInfo, setPgpInfo, t]
);

const handleNext = () => {
Expand Down Expand Up @@ -127,7 +129,7 @@ export const EnterPgpPrivateKey: VFC = () => {
pgpPrivateKey: fileinfo as string,
privateKeyIsDecrypted: pk.isDecrypted()
});
setValidation({ error: null, success: 'valid PGP private key' });
setValidation({ error: null, success: t('pgp.validPrivateKey') });
setPrivateKeyFile(keyFile?.name);
})
.catch((error) => {
Expand All @@ -145,7 +147,7 @@ export const EnterPgpPrivateKey: VFC = () => {
setValidation({ error: error.message });
}
},
[setValidation, setPrivateKeyFile, setPgpInfo, pgpInfo]
[setValidation, setPrivateKeyFile, setPgpInfo, pgpInfo, t]
);

useEffect(() => {
Expand Down
Loading
Loading