diff --git a/.gitignore b/.gitignore index 7c68bc4670a5..1429728761ce 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ common/autoinstallers/*/.npmrc /package-lock.json /yarn.lock quick-lint-js.config +.aider* diff --git a/packages/backup-format/src/version-3/index.ts b/packages/backup-format/src/version-3/index.ts index 5dfac57fc913..2f50229a95bf 100644 --- a/packages/backup-format/src/version-3/index.ts +++ b/packages/backup-format/src/version-3/index.ts @@ -14,10 +14,11 @@ export async function encryptBackup(password: BufferSource, binaryBackup: Buffer export async function decryptBackup(password: BufferSource, data: ArrayBuffer | ArrayLike) { const container = await parseEncryptedJSONContainer(SupportedVersions.Version0, data) - const _ = decode(container) - if (!Array.isArray(_) || _.length !== 3) throw new TypeError(BackupErrors.UnknownFormat) - if (!_.every((x): x is Uint8Array => x instanceof Uint8Array)) throw new TypeError(BackupErrors.UnknownFormat) - const [pbkdf2IV, encryptIV, encrypted] = _ + const payloadTuple = decode(container) + if (!Array.isArray(payloadTuple) || payloadTuple.length !== 3) throw new TypeError(BackupErrors.UnknownFormat) + if (!payloadTuple.every((x): x is Uint8Array => x instanceof Uint8Array)) + throw new TypeError(BackupErrors.UnknownFormat) + const [pbkdf2IV, encryptIV, encrypted] = payloadTuple const aes = await getAESFromPassword(password, pbkdf2IV) diff --git a/packages/icons/brands/GoogleDrive.svg b/packages/icons/brands/GoogleDrive.svg new file mode 100644 index 000000000000..bef62df4b6ce --- /dev/null +++ b/packages/icons/brands/GoogleDrive.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/icons/icon-generated-as-jsx.js b/packages/icons/icon-generated-as-jsx.js index 2045693a7a8b..e410ca933542 100644 --- a/packages/icons/icon-generated-as-jsx.js +++ b/packages/icons/icon-generated-as-jsx.js @@ -239,6 +239,11 @@ export const Gnosis = /*#__PURE__*/ __createIcon('Gnosis', [ u: () => new URL('./brands/Gnosis.svg', import.meta.url).href, }, ]) +export const GoogleDrive = /*#__PURE__*/ __createIcon('GoogleDrive', [ + { + u: () => new URL('./brands/GoogleDrive.svg', import.meta.url).href, + }, +]) export const Highlight = /*#__PURE__*/ __createIcon('Highlight', [ { u: () => new URL('./brands/Highlight.svg', import.meta.url).href, diff --git a/packages/icons/icon-generated-as-url.js b/packages/icons/icon-generated-as-url.js index a8498ee101db..e6553edb637a 100644 --- a/packages/icons/icon-generated-as-url.js +++ b/packages/icons/icon-generated-as-url.js @@ -39,6 +39,7 @@ export function git_hub_url() { return new URL("./brands/GitHub.svg", import.met export function github_dark_url() { return new URL("./brands/GithubDark.svg", import.meta.url).href } export function git_hub_gray_url() { return new URL("./brands/GitHubGray.svg", import.meta.url).href } export function gnosis_url() { return new URL("./brands/Gnosis.svg", import.meta.url).href } +export function google_drive_url() { return new URL("./brands/GoogleDrive.svg", import.meta.url).href } export function highlight_url() { return new URL("./brands/Highlight.svg", import.meta.url).href } export function instagram_url() { return new URL("./brands/Instagram.svg", import.meta.url).href } export function instagram_round_colored_url() { return new URL("./brands/InstagramRoundColored.svg", import.meta.url).href } diff --git a/packages/icons/plugins/GoPlus.svg b/packages/icons/plugins/GoPlus.svg index de636d7e89f9..770ce2c875e8 100644 --- a/packages/icons/plugins/GoPlus.svg +++ b/packages/icons/plugins/GoPlus.svg @@ -1,7 +1,7 @@ - + - + diff --git a/packages/icons/plugins/ScamSniffer.dark.svg b/packages/icons/plugins/ScamSniffer.dark.svg index fdca736c3267..ed1a2b941ff6 100644 --- a/packages/icons/plugins/ScamSniffer.dark.svg +++ b/packages/icons/plugins/ScamSniffer.dark.svg @@ -1,17 +1,15 @@ - - - - - - - - - + + + + + + + - - + + diff --git a/packages/icons/plugins/ScamSniffer.light.svg b/packages/icons/plugins/ScamSniffer.light.svg index 0664681e4498..6442efd8fb57 100644 --- a/packages/icons/plugins/ScamSniffer.light.svg +++ b/packages/icons/plugins/ScamSniffer.light.svg @@ -1,17 +1,15 @@ - - - - - - - - - + + + + + + + - - + + diff --git a/packages/icons/utils/internal.d.ts b/packages/icons/utils/internal.d.ts index 8628c7720774..07dbf9d12a13 100644 --- a/packages/icons/utils/internal.d.ts +++ b/packages/icons/utils/internal.d.ts @@ -20,7 +20,7 @@ export interface GeneratedIconProps export type GeneratedIcon = ComponentType | ComponentType> export interface GeneratedIconNonSquareProps - extends React.HTMLProps { + extends Omit, 'children'> { variant?: Variants[] | Variants height?: number | string width?: number | string diff --git a/packages/mask/.webpack/config.ts b/packages/mask/.webpack/config.ts index 80ac17d8617c..014094cae787 100644 --- a/packages/mask/.webpack/config.ts +++ b/packages/mask/.webpack/config.ts @@ -238,6 +238,7 @@ export async function createConfiguration( NEXT_PUBLIC_FIREFLY_API_URL: process.env.NEXT_PUBLIC_FIREFLY_API_URL || '', SOLANA_DEFAULT_RPC_URL: process.env.SOLANA_DEFAULT_RPC_URL || '', MASK_ENABLE_EXCHANGE: process.env.MASK_ENABLE_EXCHANGE || '', + GOOGLE_CLIENT_ID: JSON.stringify(process.env.GOOGLE_CLIENT_ID) || '', }), new (rspack?.DefinePlugin || webpack.default.DefinePlugin)({ 'process.browser': 'true', @@ -312,7 +313,6 @@ export async function createConfiguration( // Focus on performance optimization. Not for download size/cache stability optimization. optimization: { // we don't need deterministic, and we also don't have chunk request at init we don't need "size" - // @ts-expect-error chunkIds: productionLike ? rspack ? 'deterministic' diff --git a/packages/mask/.webpack/manifest/manifest-mv3.json b/packages/mask/.webpack/manifest/manifest-mv3.json index af0c5a6a1f07..d2f288b563f8 100644 --- a/packages/mask/.webpack/manifest/manifest-mv3.json +++ b/packages/mask/.webpack/manifest/manifest-mv3.json @@ -3,7 +3,7 @@ "version": "0", "manifest_version": 3, "permissions": ["storage", "webNavigation", "activeTab", "scripting"], - "optional_permissions": ["notifications", "clipboardRead"], + "optional_permissions": ["notifications", "clipboardRead", "identity", "tabs"], "optional_host_permissions": [""], "background": { "service_worker": "/manifest-v3.entry.js" }, "icons": { @@ -32,5 +32,9 @@ "gecko": { "id": "{0e57e2ae-3e13-4d5d-9243-6159219852a6}" } + }, + "oauth2": { + "client_id": "884348965548-npd1mffii0tbrr87eb2fm73ue022k3t3.apps.googleusercontent.com", + "scopes": ["https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/userinfo.email"] } } diff --git a/packages/mask/.webpack/manifest/manifest.json b/packages/mask/.webpack/manifest/manifest.json index 0c629071825b..4d6e1069c215 100644 --- a/packages/mask/.webpack/manifest/manifest.json +++ b/packages/mask/.webpack/manifest/manifest.json @@ -2,8 +2,8 @@ "name": "Mask Network", "version": "0", "manifest_version": 2, - "permissions": ["storage", "webNavigation", "activeTab"], - "optional_permissions": ["", "notifications", "clipboardRead"], + "permissions": ["storage", "webNavigation", "activeTab", "identity"], + "optional_permissions": ["", "notifications", "clipboardRead", "tabs"], "background": { "page": "background.html" }, "icons": { "16": "assets/16x16.png", @@ -14,5 +14,9 @@ "browser_action": { "default_popup": "popups.html" }, "homepage_url": "https://mask.io", "description": "The portal to the new & open Internet. Send encrypted message and decentralized Apps right on top of social networks.", - "web_accessible_resources": ["js/*", "bundled/*", "entry/*", "*.svg", "*.png", "*.jpg", "*.css", "build-info.json"] + "web_accessible_resources": ["js/*", "bundled/*", "entry/*", "*.svg", "*.png", "*.jpg", "*.css", "build-info.json"], + "oauth2": { + "client_id": "884348965548-npd1mffii0tbrr87eb2fm73ue022k3t3.apps.googleusercontent.com", + "scopes": ["https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/userinfo.email"] + } } diff --git a/packages/mask/background/services/backup/google_drive.ts b/packages/mask/background/services/backup/google_drive.ts new file mode 100644 index 000000000000..0f0bcdcab6ac --- /dev/null +++ b/packages/mask/background/services/backup/google_drive.ts @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +export async function getAccessToken() { + const contained = await browser.permissions.contains({ permissions: ['identity'] }) + if (!contained) { + const granted = await browser.permissions.request({ origins: ['identity'] }) + if (!granted) return + } + return new Promise((resolve, reject) => { + // @ts-expect-error + chrome.identity.getAuthToken({ interactive: true }, (token, error) => { + // @ts-expect-error + if (chrome.runtime.lastError) { + // @ts-expect-error + reject(new Error(chrome.runtime.lastError.message)) + return + } + resolve(token) + }) + }) +} + +export async function clearAccessToken() { + const token = await getAccessToken() + return new Promise((resolve) => { + // @ts-expect-error + chrome.identity.removeCachedAuthToken({ token }, () => resolve()) + }) +} diff --git a/packages/mask/background/services/backup/index.ts b/packages/mask/background/services/backup/index.ts index f76aa6aed5b6..b384616dee4e 100644 --- a/packages/mask/background/services/backup/index.ts +++ b/packages/mask/background/services/backup/index.ts @@ -1,3 +1,4 @@ export { generateBackupPreviewInfo, createBackupFile, type BackupOptions } from './create.js' export { generateBackupSummary, restoreBackup } from './restore.js' export { backupPersonaPrivateKey } from './persona.js' +export { getAccessToken, clearAccessToken } from './google_drive.js' diff --git a/packages/mask/background/tasks/NotCancellable/OnInstall.ts b/packages/mask/background/tasks/NotCancellable/OnInstall.ts index 290f8c7ca1b2..31acb91a330e 100644 --- a/packages/mask/background/tasks/NotCancellable/OnInstall.ts +++ b/packages/mask/background/tasks/NotCancellable/OnInstall.ts @@ -28,6 +28,7 @@ browser.runtime.onInstalled.addListener(async (detail) => { const localStorage = (globalThis as any).localStorage if (localStorage) { const backupPassword = localStorage.getItem('backupPassword') + // Migration if (backupPassword) { const backupMethod = localStorage.getItem('backupMethod') base.PersistentStorages.Settings.storage.backupConfig.setValue({ @@ -37,6 +38,8 @@ browser.runtime.onInstalled.addListener(async (detail) => { cloudBackupAt: backupMethod && backupMethod === 'cloud' ? localStorage.getItem('backupAt') : null, localBackupAt: backupMethod && backupMethod === 'local' ? localStorage.getItem('backupAt') : null, cloudBackupMethod: null, + googleToken: null, + googleAccount: null, }) } // remove old data after migrate diff --git a/packages/mask/content-script/components/CompositionDialog/Composition.tsx b/packages/mask/content-script/components/CompositionDialog/Composition.tsx index 81eeacc2ed86..a66d5a4d66e5 100644 --- a/packages/mask/content-script/components/CompositionDialog/Composition.tsx +++ b/packages/mask/content-script/components/CompositionDialog/Composition.tsx @@ -1,8 +1,7 @@ -import { useCallback, useEffect, useRef, useState } from 'react' -import { useAsync } from 'react-use' -import { DialogContent, alpha } from '@mui/material' -import { makeStyles } from '@masknet/theme' -import { useCurrentPersonaConnectStatus, InjectedDialog, PersonaAction } from '@masknet/shared' +import Services from '#services' +import { Trans } from '@lingui/react/macro' +import type { CompositionType } from '@masknet/plugin-infra/content-script' +import { InjectedDialog, PersonaAction, useCurrentPersonaConnectStatus } from '@masknet/shared' import { CrossIsolationMessages, EMPTY_OBJECT, @@ -12,19 +11,20 @@ import { currentPersonaIdentifier, } from '@masknet/shared-base' import { useValueRef } from '@masknet/shared-base-ui' +import { makeStyles } from '@masknet/theme' import { Telemetry } from '@masknet/web3-telemetry' import { EventID, EventType } from '@masknet/web3-telemetry/types' -import type { CompositionType } from '@masknet/plugin-infra/content-script' -import Services from '#services' +import { DialogContent, alpha } from '@mui/material' +import { useCallback, useEffect, useRef, useState } from 'react' +import { useAsync } from 'react-use' +import { useCurrentPersona, usePersonasFromDB } from '../../../shared-ui/hooks/index.js' import { activatedSiteAdaptorUI } from '../../site-adaptor-infra/index.js' import { useCurrentIdentity, useLastRecognizedIdentity } from '../DataSource/useActivatedUI.js' import { CompositionDialogUI, type CompositionRef, E2EUnavailableReason } from './CompositionUI.js' +import { EncryptionMethodType } from './EncryptionMethodSelector.js' import { useCompositionClipboardRequest } from './useCompositionClipboardRequest.js' import { useRecipientsList } from './useRecipientsList.js' import { useSubmit } from './useSubmit.js' -import { usePersonasFromDB, useCurrentPersona } from '../../../shared-ui/hooks/index.js' -import { EncryptionMethodType } from './EncryptionMethodSelector.js' -import { Trans } from '@lingui/react/macro' const useStyles = makeStyles()((theme) => ({ dialogRoot: { diff --git a/packages/mask/dashboard/components/BackupPreview/index.tsx b/packages/mask/dashboard/components/BackupPreview/index.tsx index 7a9db487d147..d07a33d04b9d 100644 --- a/packages/mask/dashboard/components/BackupPreview/index.tsx +++ b/packages/mask/dashboard/components/BackupPreview/index.tsx @@ -83,18 +83,25 @@ const useStyles = makeStyles()((theme) => ({ list: { padding: 0, }, + grid: { + display: 'grid', + gridTemplateColumns: 'repeat(2, 1fr)', + }, walletHeaderIcon: { backgroundColor: '#1C68F3', boxShadow: '0px 6px 12px rgba(28, 104, 243, 0.2)', }, wallets: { margin: 0, - display: 'grid', - gridTemplateColumns: 'repeat(3, 1fr)', + display: 'flex', + flexWrap: 'wrap', + gap: 8, }, wallet: { + gap: 10, width: 'auto', flexWrap: 'nowrap', + paddingRight: 0, }, listItemIcon: { marginRight: theme.spacing(1), @@ -103,12 +110,12 @@ const useStyles = makeStyles()((theme) => ({ minWidth: 'unset', }, walletIcon: { - marginRight: theme.spacing(1), color: theme.palette.maskColor.second, minWidth: 'unset', }, listText: { fontSize: 14, + color: theme.palette.maskColor.main, }, link: { color: theme.palette.maskColor.main, @@ -167,7 +174,7 @@ export const PersonasBackupPreview = memo(function P } /> - + {info.accounts}}> @@ -253,6 +260,7 @@ export const WalletsBackupPreview = memo(function Wal pluginID={NetworkPluginID.PLUGIN_EVM} address={wallet} mr="10px" + fontWeight={400} /> diff --git a/packages/mask/dashboard/components/MoreMenu/index.tsx b/packages/mask/dashboard/components/MoreMenu/index.tsx new file mode 100644 index 000000000000..71692372e1cc --- /dev/null +++ b/packages/mask/dashboard/components/MoreMenu/index.tsx @@ -0,0 +1,43 @@ +import { Icons, type GeneratedIconProps } from '@masknet/icons' +import { makeStyles } from '@masknet/theme' +import { Popover } from '@mui/material' +import { memo, useState, type ReactNode } from 'react' + +const useStyles = makeStyles()((theme) => ({ + paper: { + padding: theme.spacing(2), + borderRadius: 24, + background: theme.palette.maskColor.bottom, + boxShadow: + theme.palette.mode === 'dark' ? + '0px 4px 30px 0px rgba(255, 255, 255, 0.15)' + : '0px 4px 30px 0px rgba(0, 0, 0, 0.10)', + }, +})) + +interface Props extends GeneratedIconProps { + children?: ReactNode | ((props: { close: () => void }) => ReactNode) +} + +export const MoreMenu = memo(function MoreMenu({ children, ...rest }) { + const { classes } = useStyles() + const [anchorEl, setAnchorEl] = useState(null) + return ( + <> + setAnchorEl(e.currentTarget)} /> + setAnchorEl(null)} + classes={{ paper: classes.paper }} + anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} + transformOrigin={{ + vertical: 'top', + horizontal: 'right', + }}> + {typeof children === 'function' ? children({ close: () => setAnchorEl(null) }) : children} + + + ) +}) diff --git a/packages/mask/dashboard/components/SetupFrame/index.tsx b/packages/mask/dashboard/components/SetupFrame/index.tsx index b7e4cd3cc5da..4077a1e605c6 100644 --- a/packages/mask/dashboard/components/SetupFrame/index.tsx +++ b/packages/mask/dashboard/components/SetupFrame/index.tsx @@ -1,9 +1,9 @@ -import { lazy, memo, Suspense, useState, type PropsWithChildren } from 'react' -import { Box, Typography } from '@mui/material' import { Icons } from '@masknet/icons' -import { Welcome } from '../../assets/index.js' import { LoadingBase, makeStyles } from '@masknet/theme' +import { Box, Typography } from '@mui/material' +import { lazy, memo, Suspense, useState, type PropsWithChildren } from 'react' import { Outlet } from 'react-router-dom' +import { Welcome } from '../../assets/index.js' const Spline = lazy(() => import('./spline.js')) interface SetupFrameProps { diff --git a/packages/mask/dashboard/contexts/CloudBackupFormContext.tsx b/packages/mask/dashboard/contexts/CloudBackupFormContext.tsx index 6b7c686ab5b1..15d881b63dce 100644 --- a/packages/mask/dashboard/contexts/CloudBackupFormContext.tsx +++ b/packages/mask/dashboard/contexts/CloudBackupFormContext.tsx @@ -1,11 +1,11 @@ +import { zodResolver } from '@hookform/resolvers/zod' +import { useLingui } from '@lingui/react/macro' import { createContainer } from '@masknet/shared-base-ui' +import { useTabs } from '@masknet/theme' +import guessCallingCode from 'guess-calling-code' import { useForm } from 'react-hook-form' -import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' -import { useTabs } from '@masknet/theme' import { emailRegexp, phoneRegexp } from '../utils/regexp.js' -import guessCallingCode from 'guess-calling-code' -import { useLingui } from '@lingui/react/macro' export interface CloudBackupFormInputs { email: string @@ -70,4 +70,5 @@ function useCloudBackupFormContext() { } } +/** @deprecated */ export const CloudBackupFormContext = createContainer(useCloudBackupFormContext) diff --git a/packages/mask/dashboard/hooks/useBackupFormState.ts b/packages/mask/dashboard/hooks/useBackupFormState.ts index f5e08a412ccb..aa1fa092e0e7 100644 --- a/packages/mask/dashboard/hooks/useBackupFormState.ts +++ b/packages/mask/dashboard/hooks/useBackupFormState.ts @@ -1,12 +1,12 @@ +import Services from '#services' import { zodResolver } from '@hookform/resolvers/zod' +import { useLingui } from '@lingui/react/macro' import { useState } from 'react' import { useForm } from 'react-hook-form' import { useAsync } from 'react-use' import { z } from 'zod' import { UserContext } from '../../shared-ui/index.js' -import Services from '#services' import { passwordRegexp } from '../utils/regexp.js' -import { useLingui } from '@lingui/react/macro' export type BackupFormInputs = { backupPassword: string @@ -16,7 +16,6 @@ export type BackupFormInputs = { export function useBackupFormState() { const { t } = useLingui() const { value: hasPassword } = useAsync(Services.Wallet.hasPassword, []) - const { value: previewInfo, loading } = useAsync(Services.Backup.generateBackupPreviewInfo, []) const { user } = UserContext.useContainer() const [backupWallets, setBackupWallets] = useState(false) @@ -24,7 +23,6 @@ export function useBackupFormState() { mode: 'onBlur', context: { user, - backupWallets, hasPassword, }, @@ -38,7 +36,7 @@ export function useBackupFormState() { .string() .min(8, t`Incorrect Password`) .max(20, t`Incorrect Password`) - .refine((password) => password === user.backupPassword, t`Incorrect Password`) + .refine((password) => password === user.backupPassword, t`Incorrect Backup Password`) .refine((password) => passwordRegexp.test(password), t`Incorrect Password`), paymentPassword: backupWallets && hasPassword ? @@ -55,8 +53,6 @@ export function useBackupFormState() { return { hasPassword, - previewInfo, - loading, backupWallets, setBackupWallets, formState, diff --git a/packages/mask/dashboard/hooks/useBackupPreviewInfo.ts b/packages/mask/dashboard/hooks/useBackupPreviewInfo.ts new file mode 100644 index 000000000000..524ad737c5c3 --- /dev/null +++ b/packages/mask/dashboard/hooks/useBackupPreviewInfo.ts @@ -0,0 +1,11 @@ +import { useQuery } from '@tanstack/react-query' +import Services from '#services' + +export function useBackupPreviewInfo() { + return useQuery({ + queryKey: ['backup', 'preview-info'], + queryFn: () => Services.Backup.generateBackupPreviewInfo(), + refetchInterval: 60_0000, + refetchOnWindowFocus: true, + }) +} diff --git a/packages/mask/dashboard/hooks/useGoogleDriveFiles.ts b/packages/mask/dashboard/hooks/useGoogleDriveFiles.ts new file mode 100644 index 000000000000..aa7437b0a393 --- /dev/null +++ b/packages/mask/dashboard/hooks/useGoogleDriveFiles.ts @@ -0,0 +1,23 @@ +import { useInfiniteQuery } from '@tanstack/react-query' +import { GoogleDriveClient } from '@masknet/web3-providers' +import { compact } from 'lodash-es' +import { clearGoogleDriveAccessToken, getGoogleDriveAccessToken } from '../pages/SetupPersona/Backup/helpers.js' + +const defaultClient = new GoogleDriveClient(getGoogleDriveAccessToken, clearGoogleDriveAccessToken) +export function useGoogleDriveFiles(client: GoogleDriveClient = defaultClient) { + return useInfiniteQuery({ + enabled: client.hasLogin, + queryKey: ['google-drive', 'files'], + initialPageParam: '', + queryFn: (param) => { + return client.listBackupFiles({ + pageSize: '100', + pageToken: param.pageParam, + }) + }, + getNextPageParam: () => '', + select(data) { + return compact(data.pages.flatMap((x) => x)) + }, + }) +} diff --git a/packages/mask/dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx b/packages/mask/dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx index 239c540a82da..f2f6047e55ad 100644 --- a/packages/mask/dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +++ b/packages/mask/dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx @@ -1,25 +1,24 @@ -import { InjectedDialog, LoadingStatus } from '@masknet/shared' -import { memo, useCallback, useMemo, useRef } from 'react' -import { Box, DialogActions, DialogContent, Typography } from '@mui/material' -import { useBackupFormState, type BackupFormInputs } from '../../hooks/useBackupFormState.js' -import { ActionButton, makeStyles, useCustomSnackbar } from '@masknet/theme' -import { Icons } from '@masknet/icons' -import { useAsyncFn, useUpdateEffect } from 'react-use' import Services from '#services' -import type { BackupAccountType } from '@masknet/shared-base' -import { fetchDownloadLink, fetchUploadLink, uploadBackupValue } from '../../utils/api.js' +import { msg } from '@lingui/core/macro' +import { useLingui } from '@lingui/react' +import { Trans } from '@lingui/react/macro' import { encryptBackup } from '@masknet/backup-format' +import { Icons } from '@masknet/icons' +import { InjectedDialog, LoadingStatus } from '@masknet/shared' +import type { BackupAccountType } from '@masknet/shared-base' +import { DashboardRoutes } from '@masknet/shared-base' +import { ActionButton, makeStyles, useCustomSnackbar } from '@masknet/theme' import { encode } from '@msgpack/msgpack' +import { Box, DialogActions, DialogContent, Typography } from '@mui/material' +import { memo, useCallback, useMemo, useRef } from 'react' import { Controller } from 'react-hook-form' +import { useNavigate } from 'react-router-dom' +import { useAsyncFn, useUpdateEffect } from 'react-use' +import { UserContext } from '../../../shared-ui/index.js' import { PersonasBackupPreview, WalletsBackupPreview } from '../../components/BackupPreview/index.js' import PasswordField from '../../components/PasswordField/index.js' -import { useNavigate, useSearchParams } from 'react-router-dom' -import { DashboardRoutes } from '@masknet/shared-base' -import { format as formatDateTime } from 'date-fns' -import { UserContext } from '../../../shared-ui/index.js' -import { msg } from '@lingui/core/macro' -import { Trans } from '@lingui/react/macro' -import { useLingui } from '@lingui/react' +import { useBackupFormState, type BackupFormInputs } from '../../hooks/useBackupFormState.js' +import { useBackupPreviewInfo } from '../../hooks/useBackupPreviewInfo.js' const useStyles = makeStyles()((theme) => ({ container: { @@ -57,34 +56,35 @@ const useStyles = makeStyles()((theme) => ({ }, })) -interface BackupPreviewDialogProps { +export interface BackupPreviewDialogProps { open: boolean - onClose: () => void - isOverwrite: boolean + isUpload?: boolean code: string type: BackupAccountType account: string abstract?: string + onClose: () => void + uploadButtonLabel?: React.ReactNode | string + onUpload?: (content: ArrayBuffer, signal: AbortSignal) => Promise } export const BackupPreviewDialog = memo(function BackupPreviewDialog({ open, - onClose, - isOverwrite, + isUpload, code, type, account, abstract, + onClose, + uploadButtonLabel, + onUpload, }) { const { _ } = useLingui() const controllerRef = useRef(null) const { classes, theme } = useStyles() - const [params, setParams] = useSearchParams() const navigate = useNavigate() const { updateUser } = UserContext.useContainer() const { hasPassword, - previewInfo, - loading, backupWallets, setBackupWallets, formState: { @@ -96,6 +96,7 @@ export const BackupPreviewDialog = memo(function Backu formState: { errors, isDirty, isValid }, }, } = useBackupFormState() + const { data: previewInfo, isLoading: loading } = useBackupPreviewInfo() const { showSnackbar } = useCustomSnackbar() const [{ loading: uploadLoading, value }, handleUploadBackup] = useAsyncFn( @@ -114,44 +115,22 @@ export const BackupPreviewDialog = memo(function Backu excludeWallet: !backupWallets, }) - const name = `mask-network-keystore-backup-${formatDateTime(new Date(), 'yyyy-MM-dd')}` - const uploadUrl = await fetchUploadLink({ - code, - account, - type, - abstract: name, - }) const encrypted = await encryptBackup(encode(account + data.backupPassword), encode(file)) const controller = new AbortController() controllerRef.current = controller - const response = await uploadBackupValue(uploadUrl, encrypted, controller.signal) - - if (response.ok) { - const now = formatDateTime(new Date(), 'yyyy-MM-dd HH:mm') - const downloadLinkResponse = await fetchDownloadLink({ - account, - type, - code, - }) - showSnackbar(You have backed up your data., { variant: 'success' }) - updateUser({ cloudBackupAt: now, cloudBackupMethod: type }) - setParams((params) => { - params.set('size', downloadLinkResponse.size.toString()) - params.set('abstract', downloadLinkResponse.abstract) - params.set('uploadedAt', downloadLinkResponse.uploadedAt.toString()) - params.set('downloadURL', downloadLinkResponse.downloadURL) - return params.toString() - }) - } - return true + await onUpload?.(encrypted, controller.signal) + showSnackbar(Backup Successful, { + variant: 'success', + message: Data backed up successfully!, + }) + onClose() } catch (error) { showSnackbar(Backup Failed, { variant: 'error' }) onClose() - if ((error as any).status === 400) navigate(DashboardRoutes.CloudBackup, { replace: true }) - return false + if ((error as any).status === 400) navigate(DashboardRoutes.BackupCloud, { replace: true }) } }, - [code, hasPassword, backupWallets, abstract, code, account, type, _, navigate, updateUser, params], + [code, hasPassword, backupWallets, abstract, code, account, type, _, navigate, updateUser], ) const handleClose = useCallback(() => { @@ -165,23 +144,6 @@ export const BackupPreviewDialog = memo(function Backu }, [backupWallets, resetField]) const content = useMemo(() => { - if (value) - return ( - - 🎉 - - Congratulations - - - Backup is saved to Mask Cloud Service. - - - ) if (uploadLoading) return ( @@ -233,7 +195,7 @@ export const BackupPreviewDialog = memo(function Backu name="paymentPassword" /> : null} - {isOverwrite ? + {isUpload ? This will overwrite the existing cloud backup with the local data, this cannot be undo. @@ -247,11 +209,11 @@ export const BackupPreviewDialog = memo(function Backu previewInfo, control, _, - // eslint-disable-next-line react-compiler/react-compiler - JSON.stringify(errors), + errors.backupPassword?.message, + errors.paymentPassword?.message, backupWallets, setBackupWallets, - isOverwrite, + isUpload, theme, value, uploadLoading, @@ -275,17 +237,15 @@ export const BackupPreviewDialog = memo(function Backu : } - color={isOverwrite ? 'error' : 'primary'} + startIcon={isUpload ? : } + color="primary" disabled={!isDirty || !isValid}> - {isOverwrite ? - Overwrite Backup - : Backup to the Cloud} + {isUpload ? (uploadButtonLabel ?? Backup) : Backup to the Cloud} ) }, [ backupWallets, - isOverwrite, + isUpload, isDirty, isValid, hasPassword, diff --git a/packages/mask/dashboard/modals/BackupPreviewModal/index.tsx b/packages/mask/dashboard/modals/BackupPreviewModal/index.tsx index 50efe5c71f64..41e5f45ae7c0 100644 --- a/packages/mask/dashboard/modals/BackupPreviewModal/index.tsx +++ b/packages/mask/dashboard/modals/BackupPreviewModal/index.tsx @@ -1,50 +1,22 @@ -import type { BackupAccountType, SingletonModalProps } from '@masknet/shared-base' +import type { SingletonModalProps } from '@masknet/shared-base' import { useSingletonModal } from '@masknet/shared-base-ui' import { useState } from 'react' -import { BackupPreviewDialog } from './BackupPreviewDialog.js' +import { BackupPreviewDialog, type BackupPreviewDialogProps } from './BackupPreviewDialog.js' -export interface BackupPreviewModalOpenProps { - isOverwrite?: boolean - code: string - type: BackupAccountType - account: string - abstract?: string -} +export interface BackupPreviewModalOpenProps extends Omit {} export function BackupPreviewModal({ ref }: SingletonModalProps) { - const [isOverwrite, setIsOverwrite] = useState(false) - const [code, setCode] = useState('') - const [type, setType] = useState() - const [account, setAccount] = useState('') - const [abstract, setAbstract] = useState('') + const [props, setProps] = useState(null) const [open, dispatch] = useSingletonModal(ref, { onOpen(props) { - if (props.isOverwrite) setIsOverwrite(props.isOverwrite) - if (props.abstract) setAbstract(props.abstract) - setCode(props.code) - setType(props.type) - setAccount(props.account) + setProps(props) }, - onClose(props) { - setIsOverwrite(false) - setAbstract('') - setCode('') - setType(undefined) - setAccount('') + onClose() { + setProps(null) }, }) - if (!open || !type) return null - return ( - dispatch?.close()} - isOverwrite={isOverwrite} - code={code} - type={type} - account={account} - abstract={abstract} - /> - ) + if (!props || !open) return null + return dispatch?.close()} {...props} /> } diff --git a/packages/mask/dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx b/packages/mask/dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx index 42b77845b71d..5ca5115cc1cb 100644 --- a/packages/mask/dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +++ b/packages/mask/dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx @@ -3,21 +3,20 @@ import { Trans, useLingui } from '@lingui/react/macro' import { decryptBackup } from '@masknet/backup-format' import { Icons } from '@masknet/icons' import { formatFileSize, InjectedDialog } from '@masknet/shared' -import type { BackupAccountType } from '@masknet/shared-base' -import { DashboardRoutes } from '@masknet/shared-base' import { ActionButton, makeStyles, useCustomSnackbar } from '@masknet/theme' import { decode, encode } from '@msgpack/msgpack' import { Box, DialogActions, DialogContent, LinearProgress, Typography } from '@mui/material' import { format as formatDateTime, fromUnixTime } from 'date-fns' -import { last } from 'lodash-es' -import { memo, useCallback, useMemo, useState, type ReactNode } from 'react' -import { useNavigate } from 'react-router-dom' +import { memo, useCallback, useState, type ReactNode } from 'react' import { useAsync, useAsyncFn } from 'react-use' import PasswordField from '../../components/PasswordField/index.js' import { passwordRegexp } from '../../utils/regexp.js' -import { BackupPreviewModal } from '../modals.js' const useStyles = makeStyles()((theme) => ({ + dialog: { + height: 620, + width: 600, + }, account: { padding: theme.spacing(0.5, 2), fontSize: 14, @@ -38,95 +37,60 @@ const useStyles = makeStyles()((theme) => ({ fontSize: 14, lineHeight: '18px', }, - container: { - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - minHeight: 276, - }, })) -interface MergeBackupDialogProps { - open: boolean - onClose: () => void - downloadLink: string +export interface MergeBackupDialogProps { account: string - uploadedAt: string + /** + * A generator that yield progress of download, + * and return the content of the downloaded file at the end + */ + download: () => AsyncGenerator + fileName: string + onClose: () => void + open: boolean size: string - type?: BackupAccountType - abstract?: string - code: string + /** unix time */ + uploadedAt: string | number } export const MergeBackupDialog = memo(function MergeBackupDialog({ open, onClose, - downloadLink, + fileName, + download, account, uploadedAt, size, - type, - code, - abstract, }) { const { t } = useLingui() const { classes, theme } = useStyles() - const [process, setProcess] = useState(0) + const [progress, setProgress] = useState(0) const [backupPassword, setBackupPassword] = useState('') const [backupPasswordError, setBackupPasswordError] = useState() - const [showCongratulation, setShowCongratulation] = useState(false) - const navigate = useNavigate() const { showSnackbar } = useCustomSnackbar() const handleClose = useCallback(() => { setBackupPassword('') setBackupPasswordError('') - setShowCongratulation(false) onClose() }, [onClose]) const { value: encrypted } = useAsync(async () => { - if (!downloadLink || !open) return - - const response = await fetch(downloadLink, { method: 'GET', cache: 'no-store' }) - - if (!response.ok || response.status !== 200) { - showSnackbar(The download link is expired, { variant: 'error' }) - handleClose() - navigate(DashboardRoutes.CloudBackup, { replace: true }) - return - } - const reader = response.body?.getReader() - const contentLength = response.headers.get('Content-Length') - - if (!contentLength || !reader) return - let received = 0 - const chunks: number[] = [] - while (true) { - const { done, value } = await reader.read() - - if (done || !value) { - setProcess(100) - break - } - chunks.push(...value) - received += value.length - - setProcess((received / Number(contentLength)) * 100) - } - return Uint8Array.from(chunks).buffer - }, [downloadLink, handleClose, open]) - - const fileName = useMemo(() => { + if (!open) return + const generator = download() try { - if (!downloadLink) return '' - const url = new URL(downloadLink) - return last(url.pathname.split('/')) - } catch { - return '' + let step: IteratorResult + while (!(step = await generator.next()).done) { + setProgress(step.value) + } + return step.value + } catch (err) { + showSnackbar((err as Error).message, { variant: 'error' }) + handleClose() + throw err } - }, [downloadLink]) + }, [handleClose, open, download]) const [{ loading }, handleClickMerge] = useAsyncFn(async () => { try { @@ -145,76 +109,44 @@ export const MergeBackupDialog = memo(function MergeBack if (!hasPassword) await Services.Wallet.setDefaultPassword() } await Services.Backup.restoreBackup(backupText) - showSnackbar(Download backup, { + showSnackbar(Merge Completed, { variant: 'success', - message: Backup downloaded and merged to local successfully., + message: Your file has been successfully merged into the browser data., }) - setShowCongratulation(true) - } catch { - showSnackbar(Failed to download and merge the backup.) + } catch (err) { + showSnackbar(Failed to download and merge the backup: {(err as Error).message}) } }, [encrypted, backupPassword, account]) - const handleClickBackup = useCallback(async () => { - if (!type) return - BackupPreviewModal.open({ - isOverwrite: true, - code, - abstract, - type, - account, - }) - handleClose() - }, [code, abstract, type, account, handleClose]) - - if (showCongratulation) - return ( - Merge data to local database} open={open} onClose={handleClose}> - - - 🎉 - - Congratulations - - - - Data merged from Mask Cloud Service to local successfully. Re-enter your password to - encrypt and upload the new backup to Mask Cloud Service. - - - - - - - Backup to Mask Cloud Service - - - - ) - return ( - Merge data to local database} open={open} onClose={onClose}> + Merge data to local database} + open={open} + onClose={onClose}> {account} {fileName} - + - {process !== 100 ? + {progress !== 100 ? Downloading : <> - + {formatFileSize(Number(size))} (function MergeBack } onClick={handleClickMerge} loading={loading} disabled={!!backupPasswordError || !backupPassword || !encrypted}> - Merge to local + Merge to Browser diff --git a/packages/mask/dashboard/modals/MergeBackupModal/index.tsx b/packages/mask/dashboard/modals/MergeBackupModal/index.tsx index ffdc3dbb0906..857b3714f8eb 100644 --- a/packages/mask/dashboard/modals/MergeBackupModal/index.tsx +++ b/packages/mask/dashboard/modals/MergeBackupModal/index.tsx @@ -1,57 +1,21 @@ -import type { BackupAccountType, SingletonModalProps } from '@masknet/shared-base' +import type { SingletonModalProps } from '@masknet/shared-base' import { useSingletonModal } from '@masknet/shared-base-ui' import { useState } from 'react' -import { MergeBackupDialog } from './MergeBackupDialog.js' +import { MergeBackupDialog, type MergeBackupDialogProps } from './MergeBackupDialog.js' -export interface MergeBackupModalOpenProps { - downloadLink: string - account: string - uploadedAt: string - size: string - code: string - abstract?: string - type: BackupAccountType -} +export interface MergeBackupModalOpenProps extends Omit {} export function MergeBackupModal({ ref }: SingletonModalProps) { - const [downloadLink, setDownloadLink] = useState('') - const [code, setCode] = useState('') - const [type, setType] = useState() - const [account, setAccount] = useState('') - const [abstract, setAbstract] = useState('') - const [uploadedAt, setUploadedAt] = useState('') - const [size, setSize] = useState('') + const [props, setProps] = useState(null) const [open, dispatch] = useSingletonModal(ref, { onOpen(props) { - if (props.abstract) setAbstract(props.abstract) - setCode(props.code) - setType(props.type) - setDownloadLink(props.downloadLink) - setAccount(props.account) - setUploadedAt(props.uploadedAt) - setSize(props.size) + setProps(props) }, onClose(props) { - setCode('') - setType(undefined) - setDownloadLink('') - setAccount('') - setSize('') - setUploadedAt('') + setProps(null) }, }) + if (!props) return null - return ( - dispatch?.close()} - account={account} - downloadLink={downloadLink} - size={size} - uploadedAt={uploadedAt} - /> - ) + return dispatch?.close()} {...props} /> } diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts new file mode 100644 index 000000000000..6494465c13d2 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts @@ -0,0 +1,67 @@ +import { zodResolver } from '@hookform/resolvers/zod' +import { useLingui } from '@lingui/react/macro' +import { BackupAccountType } from '@masknet/shared-base' +import { createContainer } from '@masknet/shared-base-ui' +import guessCallingCode from 'guess-calling-code' +import { useState } from 'react' +import { useForm } from 'react-hook-form' +import { z } from 'zod' +import { phoneRegexp } from '../../../../utils/regexp.js' + +export interface CloudBackupFormInputs { + email: string + phone: string + code: string + countryCode: string +} + +export function useCloudBackupForm() { + const { t } = useLingui() + + const [backupType, setBackupType] = useState(BackupAccountType.Email) + const isEmail = backupType === BackupAccountType.Email + + const form = useForm({ + mode: 'onSubmit', + context: { + backupType, + }, + defaultValues: { + email: '', + phone: '', + code: '', + countryCode: (guessCallingCode.default || guessCallingCode)(), + }, + resolver: zodResolver( + z + .object({ + email: isEmail ? z.string().email(t`Invalid email address format.`) : z.string().optional(), + countryCode: isEmail ? z.string().optional() : z.string(), + phone: isEmail ? z.string().optional() : z.string().regex(phoneRegexp), + code: z + .string() + .min(1, t`The code is incorrect.`) + .max(6, t`The code is incorrect.`), + }) + .refine( + (data) => { + if (isEmail) return true + if (!data.countryCode || !data.phone) return false + return phoneRegexp.test(`+${data.countryCode} ${data.phone}`) + }, + { + message: t`The phone number is incorrect.`, + path: ['phone'], + }, + ), + ), + }) + + return { + form, + backupType, + setBackupType, + } +} + +export const CloudBackupFormContext = createContainer(useCloudBackupForm) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx new file mode 100644 index 000000000000..768e02af6158 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx @@ -0,0 +1,109 @@ +import { Trans, useLingui } from '@lingui/react/macro' +import { BackupAccountType } from '@masknet/shared-base' +import { CountdownButton, makeStyles, useCustomSnackbar } from '@masknet/theme' +import { Box, TextField } from '@mui/material' +import { memo, useCallback } from 'react' +import { Controller } from 'react-hook-form' +import { UserContext, useLanguage } from '../../../../../shared-ui/index.js' +import { sendCode } from '../../../../utils/api.js' +import { Locale, Scenario } from '../../../../utils/type.js' +import { CloudBackupFormContext } from './CloudBackupFormContext.js' + +const useStyles = makeStyles()((theme) => ({ + send: { + color: theme.palette.maskColor.main, + fontSize: 14, + }, +})) + +export const EmailForm = memo(function EmailForm() { + const { t } = useLingui() + const language = useLanguage() + const { classes } = useStyles() + const { user } = UserContext.useContainer() + const { showSnackbar } = useCustomSnackbar() + const { + form: { + clearErrors, + control, + watch, + trigger, + formState: { errors }, + }, + } = CloudBackupFormContext.useContainer() + + const email = watch('email') + + const handleSendVerificationCode = useCallback(async () => { + const response = await sendCode({ + account: email, + type: BackupAccountType.Email, + scenario: user.email ? Scenario.change : Scenario.create, + locale: language.includes('zh') ? Locale.zh : Locale.en, + }).catch((error) => { + showSnackbar( + error.message.includes('SendTemplatedEmail') ? + Invalid email address format. + : error.message, + { variant: 'error' }, + ) + }) + + if (response) { + showSnackbar(Verification code sent, { variant: 'success' }) + } + }, [email, user, language]) + + return ( + + ( + clearErrors('email')} + onBlur={() => trigger('email')} + fullWidth + placeholder={t`Email`} + type="email" + error={!!errors.email?.message} + helperText={errors.email?.message} + /> + )} + /> + ( + clearErrors('code')} + fullWidth + placeholder={t`Email verification code`} + error={!!errors.code?.message} + helperText={errors.code?.message} + InputProps={{ + disableUnderline: true, + endAdornment: ( + Resend}> + Send + + ), + }} + /> + )} + /> + + ) +}) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx new file mode 100644 index 000000000000..2d30815c02b3 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx @@ -0,0 +1,369 @@ +import { t } from '@lingui/core/macro' +import { formatFileSize } from '@masknet/shared' +import { Trans } from '@lingui/react/macro' +import { Icons } from '@masknet/icons' +import { BackupAccountType, EMPTY_LIST } from '@masknet/shared-base' +import { format } from 'date-fns' +import { makeStyles, useCustomSnackbar } from '@masknet/theme' +import { GoogleDriveClient, type DriveFile } from '@masknet/web3-providers' +import { + Box, + Button, + Paper, + Portal, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + Tooltip, + TableRow, + Typography, + Skeleton, +} from '@mui/material' +import { compact, range, uniqBy } from 'lodash-es' +import { memo, useMemo, useState } from 'react' +import { useOutletContext } from 'react-router-dom' +import { useAsyncFn } from 'react-use' +import { UserContext } from '../../../../../shared-ui/index.js' +import { PrimaryButton } from '../../../../components/PrimaryButton/index.js' +import { useGoogleDriveFiles } from '../../../../hooks/useGoogleDriveFiles.js' +import type { PortalContainerProps } from '../types.js' +import { BackupPreviewModal, MergeBackupModal } from '../../../../modals/modals.js' +import { + clearGoogleDriveAccessToken, + createBackupName, + downloadBackup, + getGoogleDriveAccessToken, + progressDownload, +} from '../helpers.js' +import { MoreMenu } from '../../../../components/MoreMenu/index.js' + +const useStyles = makeStyles()((theme) => ({ + container: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1.5), + paddingBottom: theme.spacing(6), + }, + header: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + user: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1), + }, + providerName: { + fontWeight: 400, + fontSize: 14, + lineHeight: '18px', + height: 18, + color: theme.palette.maskColor.second, + }, + userAccount: { + fontWeight: 700, + fontSize: 14, + lineHeight: '18px', + height: 18, + color: theme.palette.maskColor.main, + }, + title: { + fontSize: 16, + fontWeight: 700, + lineHeight: '20px', + }, + subtitle: { + fontSize: 14, + fontWeight: 400, + lineHeight: '18px', + }, + folder: { + fontSize: 14, + fontWeight: 700, + lineHeight: '18px', + }, + tableContainer: { + border: `1px solid ${theme.palette.maskColor.line}`, + borderRadius: 8, + overflow: 'hidden', + height: 340, + marginTop: theme.spacing(2), + }, + table: { + borderRadius: 8, + borderCollapse: 'collapse', + borderSpacing: 0, + }, + tableHeadCell: { + borderBottom: `1px solid ${theme.palette.maskColor.line}`, + fontSize: 14, + fontWeight: 700, + lineHeight: '18px', + padding: theme.spacing(1.5), + height: 18, + }, + bodyCell: { + padding: theme.spacing(0, 1.5), + fontSize: 14, + height: 42, + lineHeight: '18px', + fontWeight: 400, + color: theme.palette.maskColor.main, + verticalAlign: 'middle', + }, + cellText: { + fontSize: 14, + }, + actionButton: { + color: theme.palette.maskColor.second, + '&:hover': { + color: theme.palette.maskColor.main, + }, + }, + actions: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1.5), + }, + action: { + padding: 6, + height: 30, + display: 'flex', + gap: 4, + alignItems: 'center', + cursor: 'pointer', + borderRadius: 8, + '&:hover': { + backgroundColor: theme.palette.maskColor.bg, + }, + }, + actionLabel: { + fontSize: 14, + fontWeight: 700, + lineHeight: '18px', + color: theme.palette.maskColor.main, + }, +})) + +export const Component = memo(function GoogleDriveBackup() { + const { classes } = useStyles() + const { user, updateUser } = UserContext.useContainer() + const { showSnackbar } = useCustomSnackbar() + const { portalContainerRef } = useOutletContext() + const googleDriveClient = useMemo( + () => new GoogleDriveClient(getGoogleDriveAccessToken, clearGoogleDriveAccessToken), + [], + ) + const { data: files = EMPTY_LIST, refetch, isLoading } = useGoogleDriveFiles(googleDriveClient) + + const login = async () => { + try { + const userInfo = await googleDriveClient.getUserInfo() + updateUser({ + googleAccount: userInfo.email || '', + }) + } catch (err) { + showSnackbar(t`Failed to login: ${(err as Error).message}`, { variant: 'error' }) + } + } + + const [uploadedFile, setUploadedFile] = useState(null) + + const [{ loading }, uploadFile] = useAsyncFn( + async (content: ArrayBuffer) => { + const name = createBackupName() + const file = new File([content], name, { type: 'application/octet-stream' }) + const result = await googleDriveClient.uploadFile(file) + const date = new Date() + setUploadedFile({ + ...result, + name, + size: file.size.toString(), + createdTime: date.toISOString(), + modifiedTime: date.toISOString(), + }) + refetch() + }, + [googleDriveClient], + ) + + const mergedFiles = useMemo(() => uniqBy(compact([...files, uploadedFile]), (x) => x.id), [files, uploadedFile]) + + if (!user.googleAccount) { + return ( + + + Add google Drive + + + + when you click Add Google Drive button,you will be forwarded to Google authorization pages. + + + + + + + ) + } + + const downloadAndMerge = async (file: DriveFile) => { + await MergeBackupModal.openAndWaitForClose({ + download: () => { + return progressDownload(() => googleDriveClient.requestFile(file.id), file.size ? +file.size : 0) + }, + fileName: file.name, + account: user.googleAccount!, + size: file.size || '0', + uploadedAt: new Date(file.modifiedTime).getTime(), + }) + } + return ( + + + + + Google Drive + + {user.googleAccount} + + + + + MaskBackup file + + + + + + File name + + + Size + + + Date & Time + + + Actions + + + + {isLoading ? + + {range(3).map((index) => ( + + + + + + + + + + + + + + + ))} + + : + {mergedFiles.map((file, index) => ( + + {file.name} + + {file.size ? formatFileSize(+file.size) : '--'} + + + + + {format(file.modifiedTime, 'LLL d, yyyy')} + + + + + + {({ close }) => ( +
+
{ + close() + downloadAndMerge(file) + }}> + + + Merge to Browser + +
+
{ + const blob = await googleDriveClient.downloadFile( + file.id, + ) + const url = URL.createObjectURL(blob) + downloadBackup(url, file.name) + URL.revokeObjectURL(url) + close() + }}> + + + Download + +
+
+ )} +
+
+
+ ))} +
+ } +
+ + + portalContainerRef.current}> + } + size="large" + color="primary" + loading={loading} + disabled={loading} + onClick={() => { + if (!user.googleAccount) return + BackupPreviewModal.open({ + code: 'google-drive', + type: BackupAccountType.Email, + account: user.googleAccount!, + isUpload: true, + onUpload: uploadFile, + uploadButtonLabel: Back Up to Google Drive, + }) + }}> + Back Up to Google Drive + + + + ) +}) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx new file mode 100644 index 000000000000..c4b90b7b5700 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx @@ -0,0 +1,138 @@ +import { t } from '@lingui/core/macro' +import { Trans } from '@lingui/react/macro' +import { Alert, useParamTab } from '@masknet/shared' +import { BackupAccountType, DashboardRoutes } from '@masknet/shared-base' +import { makeStyles } from '@masknet/theme' +import { Box, Portal, Radio, RadioGroup, Typography } from '@mui/material' +import { memo, useState } from 'react' +import { useNavigate, useOutletContext } from 'react-router-dom' +import { useAsyncFn } from 'react-use' +import urlcat from 'urlcat' +import { UserContext } from '../../../../../shared-ui/index.js' +import { PrimaryButton } from '../../../../components/PrimaryButton/index.js' +import { fetchDownloadLink } from '../../../../utils/api.js' +import type { PortalContainerProps } from '../types.js' +import { CloudBackupFormContext, type CloudBackupFormInputs } from './CloudBackupFormContext.js' +import { EmailForm } from './EmailForm.js' +import { PhoneForm } from './PhoneForm.js' + +const useStyles = makeStyles()((theme) => ({ + container: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + }, + radios: { + flexDirection: 'row', + display: 'flex', + flexWrap: 'nowrap', + justifyContent: 'space-evenly', + }, + radioContainer: { + flexGrow: 1, + '& label': { + cursor: 'pointer', + }, + }, +})) + +export const Component = memo(function MaskNetworkBackup() { + const { classes } = useStyles() + const { portalContainerRef } = useOutletContext() + const { user, updateUser } = UserContext.useContainer() + const navigate = useNavigate() + const [showAlert, setShowAlert] = useState(true) + + const [accountType, setAccountType] = useParamTab(BackupAccountType.Email, 'account-type') + const { form } = CloudBackupFormContext.useContainer() + const { setError, formState, reset } = form + + const isEmail = accountType === BackupAccountType.Email + const incorrectCodeMsg = t`The code is incorrect.` + const [{ loading }, handleSubmit] = useAsyncFn( + async (data: CloudBackupFormInputs) => { + const response = await fetchDownloadLink({ + account: isEmail ? data.email : `+${data.countryCode} ${data.phone}`, + type: isEmail ? BackupAccountType.Email : BackupAccountType.Phone, + code: data.code, + }).catch((error) => { + if (error.status === 400) { + setError('code', { + type: 'custom', + message: incorrectCodeMsg, + }) + } else if (error.status === 404) { + reset() + // No cloud backup file + navigate( + urlcat(DashboardRoutes.BackupPreview, { + type: isEmail ? BackupAccountType.Email : BackupAccountType.Phone, + account: isEmail ? data.email : `+${data.countryCode} ${data.phone}`, + code: data.code, + }), + ) + } + }) + + if (!response) return + + updateUser({ + email: data.email || user.email, + phone: data.phone ? `${data.countryCode} ${data.phone}` : user.phone, + }) + reset() + navigate( + urlcat(DashboardRoutes.BackupPreview, { + ...response, + type: isEmail ? BackupAccountType.Email : BackupAccountType.Phone, + account: isEmail ? data.email : `+${data.countryCode} ${data.phone}`, + code: data.code, + }), + ) + }, + [isEmail, setError, reset, navigate, updateUser, user], + ) + + return ( + + { + setAccountType(undefined, isEmail ? BackupAccountType.Phone : BackupAccountType.Email) + }}> +
+ +
+
+ +
+
+ {isEmail ? + + : } + setShowAlert(false)}> + + The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative + cloud backup services or local backup solutions. + + + portalContainerRef.current}> + + Continue + + +
+ ) +}) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx new file mode 100644 index 000000000000..530cd6572f6c --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx @@ -0,0 +1,106 @@ +import { Trans, useLingui } from '@lingui/react/macro' +import { PhoneNumberField } from '@masknet/shared' +import { BackupAccountType } from '@masknet/shared-base' +import { CountdownButton, makeStyles, useCustomSnackbar } from '@masknet/theme' +import { Box, TextField } from '@mui/material' +import { memo, useCallback } from 'react' +import { Controller } from 'react-hook-form' +import { UserContext, useLanguage } from '../../../../../shared-ui/index.js' +import { sendCode } from '../../../../utils/api.js' +import { Locale, Scenario } from '../../../../utils/type.js' +import { CloudBackupFormContext } from './CloudBackupFormContext.js' + +const useStyles = makeStyles()((theme) => ({ + send: { + color: theme.palette.maskColor.main, + fontSize: 14, + }, +})) + +export const PhoneForm = memo(function PhoneForm() { + const { t } = useLingui() + const { classes } = useStyles() + const { user } = UserContext.useContainer() + const lang = useLanguage() + const { showSnackbar } = useCustomSnackbar() + + const { + form: { + control, + clearErrors, + watch, + setValue, + formState: { errors }, + }, + } = CloudBackupFormContext.useContainer() + + const [countryCode, phone] = watch(['countryCode', 'phone']) + + const handleSendVerificationCode = useCallback(async () => { + const response = await sendCode({ + account: `+${countryCode}${phone}`, + type: BackupAccountType.Phone, + scenario: user.phone ? Scenario.change : Scenario.create, + locale: lang.includes('zh') ? Locale.zh : Locale.en, + }).catch((error) => { + showSnackbar(error.message, { variant: 'error' }) + }) + + if (response) { + showSnackbar(Verification code sent, { variant: 'success' }) + } + }, [phone, user, lang, countryCode]) + + return ( + + ( + setValue('countryCode', code)} + onFocus={() => clearErrors('phone')} + fullWidth + placeholder={t`Mobile number`} + error={!!errors.phone?.message} + helperText={errors.phone?.message} + /> + )} + /> + ( + clearErrors('code')} + fullWidth + placeholder={t`Phone verification code`} + error={!!errors.code?.message} + helperText={errors.code?.message} + InputProps={{ + disableUnderline: true, + endAdornment: ( + Resend}> + Send + + ), + }} + /> + )} + /> + + ) +}) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx new file mode 100644 index 000000000000..f036fc163db9 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx @@ -0,0 +1,201 @@ +import { Trans } from '@lingui/react/macro' +import { Icons } from '@masknet/icons' +import { EmptyStatus, formatFileSize } from '@masknet/shared' +import type { BackupAccountType } from '@masknet/shared-base' +import { DashboardRoutes } from '@masknet/shared-base' +import { ActionButton, TextOverflowTooltip, makeStyles } from '@masknet/theme' +import { Box, Button, Portal, Typography } from '@mui/material' +import { format as formatDateTime, fromUnixTime } from 'date-fns' +import { memo, useCallback, useMemo } from 'react' +import { useNavigate, useOutletContext, useSearchParams } from 'react-router-dom' +import { useAsyncFn } from 'react-use' +import { BackupPreviewModal, MergeBackupModal } from '../../../../modals/modals.js' +import { createBackupName, downloadBackup, getFileName, progressDownload } from '../helpers.js' +import type { PortalContainerProps } from '../types.js' + +const useStyles = makeStyles()((theme) => ({ + header: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + user: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1), + }, + providerName: { + fontWeight: 400, + fontSize: 14, + lineHeight: '18px', + height: 18, + color: theme.palette.maskColor.second, + }, + userAccount: { + fontWeight: 700, + fontSize: 14, + lineHeight: '18px', + height: 18, + color: theme.palette.maskColor.main, + }, + text: { + fontSize: 14, + lineHeight: '18px', + fontWeight: 700, + }, + content: { + borderRadius: 8, + border: `1px solid ${theme.palette.maskColor.line}`, + padding: theme.spacing(2), + marginTop: theme.spacing(3), + display: 'flex', + columnGap: 8, + alignItems: 'center', + }, + container: { + padding: theme.spacing(2), + borderRadius: 8, + border: `1px solid ${theme.palette.maskColor.line}`, + marginTop: theme.spacing(3), + }, + button: { + whiteSpace: 'nowrap', + }, +})) + +export const Component = memo(function CloudBackupPreview() { + const { classes, theme } = useStyles() + const [params] = useSearchParams() + const { portalContainerRef } = useOutletContext() + + const navigate = useNavigate() + + const previewInfo = useMemo(() => { + return { + account: params.get('account'), + downloadLink: params.get('downloadURL'), + abstract: params.get('abstract'), + uploadedAt: params.get('uploadedAt'), + size: params.get('size'), + type: params.get('type'), + code: params.get('code'), + } + }, [params]) + + const [{ loading: mergeLoading }, handleMergeClick] = useAsyncFn(async () => { + if (!previewInfo.downloadLink || !previewInfo.account || !previewInfo.size || !previewInfo.uploadedAt) return + await MergeBackupModal.openAndWaitForClose({ + download: () => progressDownload(previewInfo.downloadLink), + fileName: getFileName(previewInfo.downloadLink) || createBackupName(), + account: previewInfo.account, + size: previewInfo.size, + uploadedAt: previewInfo.uploadedAt, + }) + }, [previewInfo]) + + const handleBackupClick = useCallback(() => { + if (!previewInfo.type || !previewInfo.account || !previewInfo.code) return + BackupPreviewModal.open({ + isUpload: false, + code: previewInfo.code, + abstract: previewInfo.abstract ? previewInfo.abstract : undefined, + type: previewInfo.type as BackupAccountType, + account: previewInfo.account, + }) + }, [previewInfo]) + + return ( + <> + + {previewInfo.downloadLink ? + <> + + + + Mask Network Cloud + + {previewInfo.account} + + + + + + + + + {previewInfo.abstract} + + + + + + {formatFileSize(Number(previewInfo.size))} + + + {formatDateTime( + fromUnixTime(Number(previewInfo.uploadedAt)), + 'yyyy-MM-dd HH:mm', + )} + + + + + + } + color="primary" + variant="roundedContained" + loading={mergeLoading} + onClick={handleMergeClick}> + Merge data to local database + + { + downloadBackup(previewInfo.downloadLink!) + }} + startIcon={} + color="primary" + variant="roundedContained"> + Download + + + + + : + + {previewInfo.account} + navigate(DashboardRoutes.BackupCloud, { replace: true })}> + Switch other account + + + + No backups found + + + } + + {!previewInfo.downloadLink ? + portalContainerRef.current}> + + Back + + + : null} + + ) +}) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx new file mode 100644 index 000000000000..11747dd10c30 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx @@ -0,0 +1,82 @@ +import { Trans } from '@lingui/react/macro' +import { Icons } from '@masknet/icons' +import { DashboardRoutes } from '@masknet/shared-base' +import { makeStyles } from '@masknet/theme' +import { Box, Typography } from '@mui/material' +import { memo } from 'react' +import { Outlet, useMatch, useNavigate, useOutletContext } from 'react-router-dom' +import type { PortalContainerProps } from '../types.js' +import { CloudBackupFormContext } from './CloudBackupFormContext.js' + +const useStyles = makeStyles()((theme, _, refs) => ({ + container: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + }, + providers: { + display: 'flex', + gap: theme.spacing(1.5), + }, + providerName: { + fontWeight: 700, + fontSize: 14, + lineHeight: '18px', + height: 18, + color: theme.palette.maskColor.main, + }, + activeButton: { + backgroundColor: theme.palette.maskColor.input, + }, + toggleButton: { + backgroundColor: theme.palette.maskColor.bottom, + cursor: 'pointer', + boxSizing: 'border-box', + borderRadius: 18, + height: 34, + gap: theme.spacing(1), + border: `1px solid ${theme.palette.maskColor.line}`, + display: 'inline-flex', + padding: theme.spacing(1, 1.5), + [`&.${refs.activeButton}`]: { + backgroundColor: theme.palette.maskColor.input, + }, + }, +})) + +export const Component = memo(function CloudBackup() { + const outletContext = useOutletContext() + const { classes, cx } = useStyles() + + const navigate = useNavigate() + const match = useMatch(DashboardRoutes.BackupCloudGoogleDrive) // MaskBook is index + const isGoogleDrive = !!match + + return ( + + + + + + + + + + ) +}) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/LocalBackup.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/LocalBackup.tsx new file mode 100644 index 000000000000..10d0c10351ca --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/LocalBackup.tsx @@ -0,0 +1,130 @@ +import Services from '#services' +import { Trans, useLingui } from '@lingui/react/macro' +import { encryptBackup } from '@masknet/backup-format' +import { Icons } from '@masknet/icons' +import { LoadingStatus } from '@masknet/shared' +import { MimeType } from '@masknet/shared-base' +import { encode } from '@msgpack/msgpack' +import { Box, Portal } from '@mui/material' +import { format as formatDateTime } from 'date-fns' +import { memo } from 'react' +import { Controller } from 'react-hook-form' +import { useOutletContext } from 'react-router-dom' +import { useAsyncFn } from 'react-use' +import { UserContext } from '../../../../shared-ui/index.js' +import { PersonasBackupPreview, WalletsBackupPreview } from '../../../components/BackupPreview/index.js' +import PasswordField from '../../../components/PasswordField/index.js' +import { PrimaryButton } from '../../../components/PrimaryButton/index.js' +import { useBackupFormState, type BackupFormInputs } from '../../../hooks/useBackupFormState.js' +import { useBackupPreviewInfo } from '../../../hooks/useBackupPreviewInfo.js' +import type { PortalContainerProps } from './types.js' +import { downloadBackup } from './helpers.js' + +export const Component = memo(function LocalBackup() { + const { portalContainerRef } = useOutletContext() + const { t } = useLingui() + const { user, updateUser } = UserContext.useContainer() + const { + hasPassword, + backupWallets, + setBackupWallets, + formState: { + setError, + control, + handleSubmit, + clearErrors, + formState: { errors, isDirty, isValid }, + }, + } = useBackupFormState() + const { data: previewInfo, isLoading: loading } = useBackupPreviewInfo() + + const [{ loading: downloadLoading }, handleFormSubmit] = useAsyncFn( + async (data: BackupFormInputs) => { + if (backupWallets && hasPassword) { + const verified = await Services.Wallet.verifyPassword(data.paymentPassword || '') + if (!verified) { + setError('paymentPassword', { type: 'custom', message: t`Incorrect Password` }) + return + } + } + const { file } = await Services.Backup.createBackupFile({ + excludeBase: false, + excludeWallet: !backupWallets, + }) + + const encrypted = await encryptBackup(encode(data.backupPassword), encode(file)) + const now = formatDateTime(new Date(), 'yyyy-MM-dd HH:mm') + const blob = new Blob([encrypted], { type: MimeType.Binary }) + const url = URL.createObjectURL(blob) + downloadBackup(url, `mask-network-keystore-backup-${now}.bin`) + + await updateUser({ + localBackupAt: now, + }) + + window.close() + }, + [backupWallets, hasPassword, setError, updateUser, user], + ) + + return ( +
+ {!loading && previewInfo ? + + + + ( + clearErrors()} + sx={{ mb: 2 }} + placeholder={t`Backup Password`} + error={!!errors.backupPassword?.message} + helperText={errors.backupPassword?.message} + /> + )} + name="backupPassword" + /> + + + + {backupWallets ? + ( + clearErrors()} + sx={{ mb: 2 }} + placeholder={t`Payment Password`} + error={!!errors.paymentPassword?.message} + helperText={errors.paymentPassword?.message} + /> + )} + name="paymentPassword" + /> + : null} + + : } + portalContainerRef.current}> + } + size="large" + color="primary" + variant="roundedContained" + loading={downloadLoading} + disabled={!isDirty || !isValid} + onClick={handleSubmit(handleFormSubmit)}> + Download Backup + + + + ) +}) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/constants.ts b/packages/mask/dashboard/pages/SetupPersona/Backup/constants.ts new file mode 100644 index 000000000000..293c44bc10a6 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/constants.ts @@ -0,0 +1,8 @@ +import { DashboardRoutes } from '@masknet/shared-base' + +export const CloudBackupRoutes: string[] = [ + DashboardRoutes.BackupCloud, + DashboardRoutes.BackupCloudGoogleDrive, + DashboardRoutes.BackupCloudMaskNetwork, + DashboardRoutes.BackupPreview, +] diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/helpers.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/helpers.tsx new file mode 100644 index 000000000000..460fdac9e33e --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/helpers.tsx @@ -0,0 +1,94 @@ +import Services from '#services' +import { t } from '@lingui/core/macro' +import { fetchJSON } from '@masknet/web3-providers/helpers' +import { format as formatDateTime } from 'date-fns' + +interface Options { + code: string + clientId: string + redirectUri: string +} + +interface TokenResponse { + access_token: string + error_description: string +} + +export async function requestGoogleDriveAccessToken(options: Options) { + return fetchJSON('https://oauth2.googleapis.com/token', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + code: options.code, + client_id: options.clientId, + redirect_uri: options.redirectUri, + grant_type: 'authorization_code', + }), + }) +} + +export function downloadBackup(url: string, name?: string) { + const a = document.createElement('a') + a.href = url + if (name) a.download = name + a.click() +} + +export function getGoogleDriveAccessToken() { + return Services.Backup.getAccessToken() +} +export function clearGoogleDriveAccessToken() { + return Services.Backup.clearAccessToken() +} + +export function createBackupName() { + return `mask-network-keystore-backup-${formatDateTime(new Date(), 'yyyy-MM-dd')}.bin` +} + +export function getFileName(url: string) { + try { + const urlObj = new URL(url) + return urlObj.pathname.split('/').pop() + } catch { + return '' + } +} + +/** + * Download general backup file (no need to authenticate) + */ +export async function* progressDownload(request: string | null | (() => Promise), size?: number) { + if (!request) return + const response = + typeof request === 'function' ? + await request() + : await fetch(request, { + method: 'GET', + cache: 'no-store', + }) + + if (!response.ok || response.status !== 200) { + throw new Error(t`The download link is expired`) + } + if (!response.body) return + const reader = response.body.getReader() + const contentLength = response.headers.get('Content-Length') || size + + if (!contentLength || !reader) return + + let received = 0 + const chunks: number[] = [] + while (true) { + const { done, value } = await reader.read() + + if (done || !value) { + yield 100 + break + } + chunks.push(...value) + received += value.length + + yield (received / Number(contentLength)) * 100 + } + return Uint8Array.from(chunks).buffer +} diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/index.tsx b/packages/mask/dashboard/pages/SetupPersona/Backup/index.tsx new file mode 100644 index 000000000000..6bd737cbb996 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/index.tsx @@ -0,0 +1,97 @@ +import { Trans } from '@lingui/react/macro' +import { MaskTabList, makeStyles } from '@masknet/theme' +import { TabContext } from '@mui/lab' +import { Box, Tab, Typography } from '@mui/material' +import { memo, useRef } from 'react' +import { Outlet, useLocation, useNavigate } from 'react-router-dom' +import { CloudBackupRoutes } from './constants.js' +import { StorageType } from './types.js' + +const useStyles = makeStyles()((theme) => ({ + title: { + fontSize: 36, + lineHeight: 1.2, + fontWeight: 700, + }, + description: { + color: theme.palette.maskColor.second, + fontSize: 14, + marginTop: theme.spacing(1.5), + }, + tabContainer: { + border: `1px solid ${theme.palette.maskColor.line}`, + marginTop: theme.spacing(3), + borderRadius: theme.spacing(1, 1, 0, 0), + overflow: 'hidden', + }, + tabList: { + background: + theme.palette.mode === 'light' ? + 'linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 100%), linear-gradient(90deg, rgba(98, 152, 234, 0.2) 1.03%, rgba(98, 152, 234, 0.2) 1.04%, rgba(98, 126, 234, 0.2) 100%)' + : 'linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.06) 100%)', + padding: theme.spacing('14px', 2, 0), + }, + tab: { + fontSize: 16, + fontWeight: 700, + }, + panelContainer: { + padding: theme.spacing(2), + }, + exclaveActions: { + marginTop: 136, + }, +})) + +export const Backup = memo(function Backup() { + const { classes } = useStyles() + + const location = useLocation() + const { pathname } = location + const isCloud = CloudBackupRoutes.includes(pathname) + const tab = isCloud ? StorageType.Cloud : StorageType.Local + const navigate = useNavigate() + + const portalContainerRef = useRef(null) + + return ( + + + Back Up Your Data Your Way + + + + Choose from multiple backup options, now including encrypted storage via your authorized Google + Drive for added security and flexibility. + + + +
+ + { + navigate(value === StorageType.Cloud ? '/setup/backup/cloud' : '/setup/backup/local') + }} + aria-label="Cloud Backup Methods"> + Locale Backup} + value={StorageType.Local} + /> + Cloud Backup} + value={StorageType.Cloud} + /> + + +
+
+ +
+
+ +
+ ) +}) diff --git a/packages/mask/dashboard/pages/SetupPersona/Backup/types.ts b/packages/mask/dashboard/pages/SetupPersona/Backup/types.ts new file mode 100644 index 000000000000..ae301402a683 --- /dev/null +++ b/packages/mask/dashboard/pages/SetupPersona/Backup/types.ts @@ -0,0 +1,8 @@ +export interface PortalContainerProps { + portalContainerRef: React.RefObject +} + +export enum StorageType { + Local = 'local', + Cloud = 'cloud', +} diff --git a/packages/mask/dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx b/packages/mask/dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx index 3166edb410e1..5be0bab4e62f 100644 --- a/packages/mask/dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +++ b/packages/mask/dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx @@ -11,6 +11,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom' import { useAsyncFn } from 'react-use' import { SetupFrameController } from '../../../components/SetupFrame/index.js' import { BackupPreviewModal, ConfirmDialog, MergeBackupModal } from '../../../modals/modals.js' +import { createBackupName, getFileName, progressDownload } from '../Backup/helpers.js' const useStyles = makeStyles()((theme) => ({ title: { @@ -78,20 +79,18 @@ export const Component = memo(function CloudBackupPreview() { ) return await MergeBackupModal.openAndWaitForClose({ - downloadLink: previewInfo.downloadLink, + download: () => progressDownload(previewInfo.downloadLink), + fileName: getFileName(previewInfo.downloadLink) || createBackupName(), account: previewInfo.account, size: previewInfo.size, uploadedAt: previewInfo.uploadedAt, - code: previewInfo.code, - abstract: previewInfo.abstract ? previewInfo.abstract : undefined, - type: previewInfo.type as BackupAccountType, }) }, [previewInfo]) const handleBackupClick = useCallback(() => { if (!previewInfo.type || !previewInfo.account || !previewInfo.code) return BackupPreviewModal.open({ - isOverwrite: false, + isUpload: false, code: previewInfo.code, abstract: previewInfo.abstract ? previewInfo.abstract : undefined, type: previewInfo.type as BackupAccountType, @@ -113,7 +112,7 @@ export const Component = memo(function CloudBackupPreview() { if (!previewInfo.type || !previewInfo.account || !previewInfo.code) return BackupPreviewModal.open({ - isOverwrite: true, + isUpload: true, code: previewInfo.code, abstract: previewInfo.abstract ? previewInfo.abstract : undefined, type: previewInfo.type as BackupAccountType, @@ -127,10 +126,13 @@ export const Component = memo(function CloudBackupPreview() { <> - Welcome to Mask Cloud Services + Back Up Your Data Your Way - Please select the appropriate method to restore your personal data. + + Choose from multiple backup options, now including encrypted storage via your authorized Google + Drive for added security and flexibility. + {previewInfo.downloadLink ? <> diff --git a/packages/mask/dashboard/pages/SetupPersona/LocalBackup/index.tsx b/packages/mask/dashboard/pages/SetupPersona/LocalBackup/index.tsx index e8f46ca65270..c4fe949a69ba 100644 --- a/packages/mask/dashboard/pages/SetupPersona/LocalBackup/index.tsx +++ b/packages/mask/dashboard/pages/SetupPersona/LocalBackup/index.tsx @@ -1,22 +1,23 @@ +import Services from '#services' +import { Trans, useLingui } from '@lingui/react/macro' +import { encryptBackup } from '@masknet/backup-format' +import { Icons } from '@masknet/icons' +import { LoadingStatus } from '@masknet/shared' +import { MimeType } from '@masknet/shared-base' import { makeStyles } from '@masknet/theme' +import { encode } from '@msgpack/msgpack' import { Box, Typography } from '@mui/material' +import { format as formatDateTime } from 'date-fns' import { memo } from 'react' +import { Controller } from 'react-hook-form' import { useAsyncFn } from 'react-use' -import Services from '#services' -import { LoadingStatus } from '@masknet/shared' -import PasswordField from '../../../components/PasswordField/index.js' +import { UserContext } from '../../../../shared-ui/index.js' import { PersonasBackupPreview, WalletsBackupPreview } from '../../../components/BackupPreview/index.js' -import { Controller } from 'react-hook-form' -import { SetupFrameController } from '../../../components/SetupFrame/index.js' +import PasswordField from '../../../components/PasswordField/index.js' import { PrimaryButton } from '../../../components/PrimaryButton/index.js' -import { Icons } from '@masknet/icons' -import { encryptBackup } from '@masknet/backup-format' -import { encode } from '@msgpack/msgpack' -import { format as formatDateTime } from 'date-fns' -import { MimeType } from '@masknet/shared-base' +import { SetupFrameController } from '../../../components/SetupFrame/index.js' import { useBackupFormState, type BackupFormInputs } from '../../../hooks/useBackupFormState.js' -import { UserContext } from '../../../../shared-ui/index.js' -import { Trans, useLingui } from '@lingui/react/macro' +import { useBackupPreviewInfo } from '../../../hooks/useBackupPreviewInfo.js' const useStyles = makeStyles()((theme) => ({ title: { @@ -38,8 +39,6 @@ export const Component = memo(function LocalBackup() { const { user, updateUser } = UserContext.useContainer() const { hasPassword, - previewInfo, - loading, backupWallets, setBackupWallets, formState: { @@ -50,6 +49,7 @@ export const Component = memo(function LocalBackup() { formState: { errors, isDirty, isValid }, }, } = useBackupFormState() + const { data: previewInfo, isLoading: loading } = useBackupPreviewInfo() const [{ loading: downloadLoading }, handleFormSubmit] = useAsyncFn( async (data: BackupFormInputs) => { diff --git a/packages/mask/dashboard/pages/SetupPersona/index.tsx b/packages/mask/dashboard/pages/SetupPersona/index.tsx index 46a9612124ca..aca0fa84e8bf 100644 --- a/packages/mask/dashboard/pages/SetupPersona/index.tsx +++ b/packages/mask/dashboard/pages/SetupPersona/index.tsx @@ -1,19 +1,58 @@ import { useMatch, type RouteObject } from 'react-router-dom' -import { DashboardRoutes, relativeRouteOf } from '@masknet/shared-base' +import { DashboardRoutes, relativeRoute as rr, relativeRouteOf } from '@masknet/shared-base' import { SetupFrame } from '../../components/SetupFrame/index.js' +import { Backup } from './Backup/index.js' const r = relativeRouteOf(DashboardRoutes.Setup) +const Routes = DashboardRoutes export const personaRoutes: RouteObject[] = [ - { path: r(DashboardRoutes.Welcome), lazy: () => import('./Welcome/index.js') }, - { path: r(DashboardRoutes.Permissions), lazy: () => import('./Permissions/index.js') }, - { path: r(DashboardRoutes.PermissionsOnboarding), lazy: () => import('./PermissionOnboarding/index.js') }, - { path: r(DashboardRoutes.SignUpPersona), lazy: () => import('./SignUp/index.js') }, - { path: r(DashboardRoutes.RecoveryPersona), lazy: () => import('./Recovery/index.js') }, - { path: r(DashboardRoutes.SignUpPersonaMnemonic), lazy: () => import('./Mnemonic/index.js') }, - { path: r(DashboardRoutes.SignUpPersonaOnboarding), lazy: () => import('./Onboarding/index.js') }, - { path: r(DashboardRoutes.LocalBackup), lazy: () => import('./LocalBackup/index.js') }, - { path: r(DashboardRoutes.CloudBackup), lazy: () => import('./CloudBackup/index.js') }, - { path: r(DashboardRoutes.CloudBackupPreview), lazy: () => import('./CloudBackupPreview/index.js') }, + { path: r(Routes.Welcome), lazy: () => import('./Welcome/index.js') }, + { path: r(Routes.Permissions), lazy: () => import('./Permissions/index.js') }, + { path: r(Routes.PermissionsOnboarding), lazy: () => import('./PermissionOnboarding/index.js') }, + { path: r(Routes.SignUpPersona), lazy: () => import('./SignUp/index.js') }, + { path: r(Routes.RecoveryPersona), lazy: () => import('./Recovery/index.js') }, + { path: r(Routes.SignUpPersonaMnemonic), lazy: () => import('./Mnemonic/index.js') }, + { path: r(Routes.SignUpPersonaOnboarding), lazy: () => import('./Onboarding/index.js') }, + { + path: r(Routes.Backup), + element: , + children: [ + { + index: true, + lazy: () => import('./Backup/LocalBackup.js'), + }, + { + path: rr(Routes.Backup, Routes.BackupLocal), + lazy: () => import('./Backup/LocalBackup.js'), + index: true, + }, + { + path: rr(Routes.Backup, Routes.BackupCloud), + lazy: () => import('./Backup/CloudBackup/index.js'), + children: [ + { + index: true, + lazy: () => import('./Backup/CloudBackup/MaskNetworkBackup.js'), + }, + { + path: rr(Routes.BackupCloud, Routes.BackupCloudMaskNetwork), + lazy: () => import('./Backup/CloudBackup/MaskNetworkBackup.js'), + }, + { + path: rr(Routes.BackupCloud, Routes.BackupCloudGoogleDrive), + lazy: () => import('./Backup/CloudBackup/GoogleDriveBackup.js'), + }, + { + path: rr(Routes.BackupCloud, Routes.BackupPreview), + lazy: () => import('./Backup/CloudBackup/Preview.js'), + }, + ], + }, + ], + }, + { path: r(Routes.LocalBackup), lazy: () => import('./LocalBackup/index.js') }, + { path: r(Routes.CloudBackup), lazy: () => import('./CloudBackup/index.js') }, + { path: r(Routes.CloudBackupPreview), lazy: () => import('./CloudBackupPreview/index.js') }, ] export function PersonaFrame() { const matchPersonaOnboarding = useMatch(DashboardRoutes.SignUpPersonaOnboarding) diff --git a/packages/mask/dashboard/utils/regexp.ts b/packages/mask/dashboard/utils/regexp.ts index 6dc401584d23..e0dbc6c212a3 100644 --- a/packages/mask/dashboard/utils/regexp.ts +++ b/packages/mask/dashboard/utils/regexp.ts @@ -1,5 +1,8 @@ export const passwordRegexp = /^(?=.{8,20}$)(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[^\dA-Za-z]).*/ +/** + * @deprecated use z.string().email() instead + */ export const emailRegexp = /^([\w!#$%&*+./=?^`{|}~’-]{1,64}@([\dA-Za-z-]{1,255}.[\dA-Za-z-]{2,}))$/ export const phoneRegexp = /^(\+?([ .-])?\d{1,2}([ .-])?)?(\(?\d{3}\)?|\d{3})([ .-])?(\d{3}([ .-])?\d{4})/ diff --git a/packages/mask/popups/pages/Settings/index.tsx b/packages/mask/popups/pages/Settings/index.tsx index 3b21d61efe98..fa0ba35994b1 100644 --- a/packages/mask/popups/pages/Settings/index.tsx +++ b/packages/mask/popups/pages/Settings/index.tsx @@ -245,7 +245,7 @@ export const Component = memo(function SettingsPage() { if (!user.backupPassword) { modalNavigate(PopupModalRoutes.SetBackupPassword) } else { - handleOpenDashboard(DashboardRoutes.CloudBackup) + handleOpenDashboard(DashboardRoutes.BackupCloud) } }}> ) => { + async (userConfig: Partial) => { await PersistentStorages.Settings.storage.backupConfig.setValue({ ...user, - ...obj, - backupPassword: obj.backupPassword ? btoa(obj.backupPassword) : user.backupPassword, + ...userConfig, + backupPassword: userConfig.backupPassword ? btoa(userConfig.backupPassword) : user.backupPassword, }) }, [user], @@ -30,7 +30,7 @@ function useUserContext() { // Maybe `backupPassword` is not base64-encoded. return user } - }, [user, updateUser]) + }, [user]) return { user: result, diff --git a/packages/mask/shared-ui/initialization/fetch.ts b/packages/mask/shared-ui/initialization/fetch.ts index 393375f9cc78..64e3073e0560 100644 --- a/packages/mask/shared-ui/initialization/fetch.ts +++ b/packages/mask/shared-ui/initialization/fetch.ts @@ -51,9 +51,14 @@ function fetchingInsResource(target: URL) { return location.origin.endsWith('instagram.com') && target.origin.match(/(fbcdn\.net|cdninstagram\.com)$/) } +function fetchingGoogleDriveResource(target: URL) { + return target.origin === 'https://www.googleapis.com' +} + function canAccessAsContent(url: string) { const target = new URL(url, location.href) - if (fetchingTwitterResource(target) || fetchingInsResource(target)) return true + if (fetchingTwitterResource(target) || fetchingInsResource(target) || fetchingGoogleDriveResource(target)) + return true // eg: https://maskbook-backup-server-staging.s3.ap-east-1.amazonaws.com/backups/xxx.zip // The content-length needs to be used in the client request in order to realize the progress of the download. diff --git a/packages/mask/shared-ui/initialization/storage.ts b/packages/mask/shared-ui/initialization/storage.ts index d3bc387f97a8..5b2563a0a605 100644 --- a/packages/mask/shared-ui/initialization/storage.ts +++ b/packages/mask/shared-ui/initialization/storage.ts @@ -25,3 +25,5 @@ const indexedDB: KVStorageBackend = { } setupMaskKVStorageBackend(indexedDB, memory) setupLegacySettingsAtNonBackground(Services.Settings.getLegacySettingsInitialValue) + +indexedDB diff --git a/packages/mask/shared-ui/locale/en-US.json b/packages/mask/shared-ui/locale/en-US.json index 7cc6ffaef207..69b623c7f48d 100644 --- a/packages/mask/shared-ui/locale/en-US.json +++ b/packages/mask/shared-ui/locale/en-US.json @@ -61,6 +61,7 @@ "5YfW3S": ["Add an encrypted comment..."], "aTzTyw": ["Add Assets"], "M0FgOk": ["Add Contact"], + "FAKSZG": ["Add google Drive"], "LP+1Z7": ["Add Network"], "68m9Nl": ["Add new address to an existing group"], "1LkMSp": ["Add Suggested Token"], @@ -100,6 +101,8 @@ "7QptC5": ["Auto-lock Time"], "nTFx7o": ["Avatar set successfully"], "iH8pgl": ["Back"], + "npsHM5": ["Back Up to Google Drive"], + "V+1pjj": ["Back Up Your Data Your Way"], "rLgPvm": ["Backup"], "h7+V7K": ["Backup & Recovery"], "QrHM/A": ["Backup downloaded and merged to local successfully."], @@ -113,6 +116,7 @@ ], "hUF4dG": ["Backup password set successfully"], "UtKGok": ["Backup Persona"], + "+m7x4e": ["Backup Successful"], "7Ry254": ["Backup to Mask Cloud Service"], "6IHgRq": ["Backup to the Cloud"], "QQPCqQ": ["Backup Wallet"], @@ -140,6 +144,9 @@ "tcHpvG": ["Change Owner"], "fDP2k4": ["Change Payment Password"], "IXbRbM": ["Choose another wallet"], + "lU6uRb": [ + "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." + ], "xH6cn+": ["Choose Token"], "+adBn3": ["Chrome - external extension"], "EsKgLS": ["Click here to have a quick start."], @@ -211,6 +218,7 @@ "pvnfJD": ["Dark"], "Sp/me1": ["data"], "HKH+W+": ["Data"], + "K8jCIB": ["Data backed up successfully!"], "3B4jmY": ["Data correlation"], "rAcOY1": [ "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." @@ -286,7 +294,9 @@ "SLEDBF": ["Failed to add the wallet, please try again."], "09vHB+": ["Failed to decrypt."], "jK7nvW": ["Failed to disconnect wallet"], + "9hzYBR": ["Failed to download and merge the backup: ", ["0"]], "0tkvsz": ["Failed to download and merge the backup."], + "etATpN": ["Failed to login: ", ["0"]], "mrAv9J": ["Failed to save network"], "ZsjtYe": ["Failed to set Avatar."], "RWoEqV": ["Failed to transfer token: ", ["message"]], @@ -323,6 +333,7 @@ "tdZPpe": ["Generating your"], "CiU2m/": ["Generating your "], "2p7WMm": ["Give permission to access <0/> your ", ["0"], "?"], + "w9Xmw/": ["Google Drive"], "76gPWk": ["Got it"], "HqiAyF": ["Gwei"], "pu3rTn": ["GWEI"], @@ -354,6 +365,7 @@ "fza4cg": ["Import Wallets"], "HvDfH/": ["Imported"], "m3XvO+": ["Imported Wallets"], + "ANk/Zy": ["Incorrect Backup Password"], "a23gJr": ["Incorrect backup password."], "bapItf": ["Incorrect Backup Password."], "bassMa": ["Incorrect cloud backup password, please try again."], @@ -394,9 +406,11 @@ "jygqM9": ["Loading account information..."], "EBC6hj": ["Local Backup"], "shPV5O": ["Local persona or wallet only"], + "crVIc7": ["Locale Backup"], "KGHnSc": ["Lock Wallet"], "FgAxTj": ["Log out"], "kC3Q3d": ["Login to Mask Cloud"], + "nOhz3x": ["Logout"], "NkgvWN": ["Logout failed"], "o4Iu2W": ["Logout successfully"], "nTWWCZ": ["Low"], @@ -406,6 +420,7 @@ ], "lET9my": ["Mask needs the following permissions"], "siroPf": ["Mask Network"], + "jhoAzY": ["Mask Network Cloud"], "lWOC+h": ["Mask Network requires you to authorize the following websites before using it."], "HsoakN": ["masknetwork"], "CK1KXz": ["Max"], @@ -422,7 +437,9 @@ "sQJ3RN": ["Max priority fee is too low for network conditions."], "ITBQO3": ["Max priority fee must be greater than 0 GWEI"], "agPptk": ["Medium"], + "lPsa94": ["Merge Completed"], "RH8jSA": ["Merge data to local database"], + "E6YUtc": ["Merge to Browser"], "QTomF0": ["Merge to local"], "xDAtGP": ["Message"], "pGElS5": ["Mnemonic"], @@ -655,6 +672,9 @@ "VdhBp2": ["The chainID is not equal to the currently connected one."], "h3Vpnw": ["The code is incorrect."], "7w1rTm": ["The download link is expired"], + "oYkran": [ + "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." + ], "o+zDNQ": ["The mnemonic word has been copied, please keep it in a safe place."], "yRuvmU": ["The mnemonic words has been copied, please keep it in a safe place."], "thX7xs": [ @@ -793,6 +813,7 @@ "m74yjp": ["Welcome to Mask Cloud Services"], "hEpXzO": ["Welcome to Mask Network"], "fFYJ8f": ["Welcome to use Mask Network"], + "OLUEnY": ["when you click Add Google Drive button,you will be forwarded to Google authorization pages."], "Qbo7Ev": ["Write down mnemonic words"], "f5AidZ": ["Write down recovery phrase"], "yGyJ1l": [ @@ -810,6 +831,7 @@ "FnQek5": [ "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." ], + "UDKbPj": ["Your file has been successfully merged into the browser data."], "Q68Nuc": [ "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." ], diff --git a/packages/mask/shared-ui/locale/en-US.po b/packages/mask/shared-ui/locale/en-US.po index 1daa31d66fa2..197632f803e1 100644 --- a/packages/mask/shared-ui/locale/en-US.po +++ b/packages/mask/shared-ui/locale/en-US.po @@ -157,6 +157,10 @@ msgstr "" msgid "Add Contact" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Add google Drive" +msgstr "" + #: popups/pages/Wallet/NetworkManagement/index.tsx #: popups/pages/Wallet/EditNetwork/index.tsx msgid "Add Network" @@ -300,13 +304,25 @@ msgid "Avatar set successfully" msgstr "" #: content-script/components/shared/SelectRecipients/SelectRecipientsDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx #: popups/pages/Personas/ExportPrivateKey/index.tsx #: popups/pages/Personas/AccountDetail/UI.tsx msgid "Back" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Back Up to Google Drive" +msgstr "" + #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Back Up Your Data Your Way" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/Logout/index.tsx #: popups/modals/SwitchPersonaModal/index.tsx msgid "Backup" @@ -317,16 +333,16 @@ msgid "Backup & Recovery" msgstr "" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup downloaded and merged to local successfully." -msgstr "" +#~ msgid "Backup downloaded and merged to local successfully." +#~ msgstr "" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup Failed" msgstr "" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "Backup is saved to Mask Cloud Service." -msgstr "" +#~ msgid "Backup is saved to Mask Cloud Service." +#~ msgstr "" #. placeholder {0}: user.localBackupAt #. placeholder {0}: user.cloudBackupAt @@ -341,6 +357,7 @@ msgid "Backup password" msgstr "" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Settings/index.tsx @@ -363,10 +380,14 @@ msgstr "" msgid "Backup Persona" msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup to Mask Cloud Service" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Backup Successful" msgstr "" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Backup to Mask Cloud Service" +#~ msgstr "" + #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup to the Cloud" msgstr "" @@ -505,6 +526,11 @@ msgstr "" msgid "Choose another wallet" msgstr "" +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." +msgstr "" + #: popups/modals/ChooseToken/index.tsx msgid "Choose Token" msgstr "" @@ -530,6 +556,7 @@ msgid "Close" msgstr "" #: dashboard/pages/SetupPersona/Recovery/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx #: dashboard/components/Restore/ConfirmSynchronizePasswordDialog.tsx #: popups/pages/Settings/index.tsx msgid "Cloud Backup" @@ -602,8 +629,6 @@ msgstr "" msgid "Confirm to hide {name}? You can redisplay it by re-adding this NFT at any time." msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/ConnectWallet/index.tsx msgid "Congratulations" msgstr "" @@ -706,6 +731,7 @@ msgstr "" #: dashboard/pages/SetupPersona/SignUp/index.tsx #: dashboard/pages/SetupPersona/Mnemonic/index.tsx #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx #: dashboard/pages/CreateMaskWallet/AddDeriveWallet/index.tsx #: dashboard/components/Restore/RestoreWalletFromLocal.tsx @@ -832,13 +858,17 @@ msgstr "" msgid "Data" msgstr "" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Data backed up successfully!" +msgstr "" + #: popups/pages/Settings/index.tsx msgid "Data correlation" msgstr "" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." -msgstr "" +#~ msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." +#~ msgstr "" #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx @@ -946,14 +976,17 @@ msgid "Done" msgstr "" #: content-script/components/InjectedComponents/AutoPasteFailedDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx msgid "Download" msgstr "" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Download backup" -msgstr "" +#~ msgid "Download backup" +#~ msgstr "" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx msgid "Download Backup" msgstr "" @@ -983,11 +1016,14 @@ msgid "eg: X accounts, persona public keys, wallet addresses or ENS" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email verification code" msgstr "" @@ -1113,8 +1149,18 @@ msgstr "" msgid "Failed to disconnect wallet" msgstr "" +#. placeholder {0}: (err as Error).message +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Failed to download and merge the backup: {0}" +msgstr "" + #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Failed to download and merge the backup." +#~ msgid "Failed to download and merge the backup." +#~ msgstr "" + +#. placeholder {0}: (err as Error).message +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Failed to login: {0}" msgstr "" #: popups/pages/Wallet/EditNetwork/index.tsx @@ -1274,6 +1320,11 @@ msgstr "" msgid "Give permission to access <0/> your {0}?" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Google Drive" +msgstr "" + #: dashboard/pages/CreateMaskWallet/Onboarding/index.tsx msgid "Got it" msgstr "" @@ -1384,6 +1435,10 @@ msgstr "" msgid "Imported Wallets" msgstr "" +#: dashboard/hooks/useBackupFormState.ts +msgid "Incorrect Backup Password" +msgstr "" + #: popups/modals/VerifyBackupPasswordModal/index.tsx #: popups/modals/ChangeBackupPasswordModal/index.tsx msgid "Incorrect backup password." @@ -1413,6 +1468,7 @@ msgid "Incorrect password" msgstr "" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts @@ -1420,7 +1476,6 @@ msgstr "" #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts -#: dashboard/hooks/useBackupFormState.ts msgid "Incorrect Password" msgstr "" @@ -1469,6 +1524,8 @@ msgid "Invalid Block Explorer URL." msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "Invalid email address format." msgstr "" @@ -1571,6 +1628,10 @@ msgstr "" msgid "Local persona or wallet only" msgstr "" +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Locale Backup" +msgstr "" + #: popups/pages/Wallet/SwitchWallet/index.tsx msgid "Lock Wallet" msgstr "" @@ -1585,6 +1646,11 @@ msgstr "" msgid "Login to Mask Cloud" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Logout" +msgstr "" + #: popups/pages/Personas/Logout/index.tsx msgid "Logout failed" msgstr "" @@ -1613,9 +1679,14 @@ msgstr "" #: content-script/components/InjectedComponents/ToolboxUnstyled.tsx #: content-script/components/InjectedComponents/PostDialogHint.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx msgid "Mask Network" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +msgid "Mask Network Cloud" +msgstr "" + #: content-script/components/InjectedComponents/PermissionBoundary.tsx msgid "Mask Network requires you to authorize the following websites before using it." msgstr "" @@ -1687,16 +1758,25 @@ msgstr "" msgid "Medium" msgstr "" -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Merge Completed" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx msgid "Merge data to local database" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Merge to local" +msgid "Merge to Browser" msgstr "" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Merge to local" +#~ msgstr "" + #: popups/components/SignRequestInfo/index.tsx msgid "Message" msgstr "" @@ -1711,10 +1791,12 @@ msgid "Mnemonic word" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx msgid "Mobile" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Mobile number" msgstr "" @@ -1832,6 +1914,7 @@ msgid "No back up" msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "No backups found" msgstr "" @@ -1944,7 +2027,6 @@ msgid "Other social networking platforms, such as <0>Instagram, <1>Facebook, msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Overwrite Backup" msgstr "" @@ -1963,6 +2045,7 @@ msgid "Paste manually" msgstr "" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Wallet/SetPaymentPassword/index.tsx #: popups/modals/WalletRemoveModal/index.tsx @@ -2044,6 +2127,7 @@ msgid "Personas" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Phone verification code" msgstr "" @@ -2099,7 +2183,6 @@ msgstr "" #: dashboard/pages/SetupPersona/Recovery/index.tsx #: dashboard/pages/SetupPersona/LocalBackup/index.tsx -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx msgid "Please select the appropriate method to restore your personal data." msgstr "" @@ -2294,6 +2377,8 @@ msgstr "" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Resend" msgstr "" @@ -2408,6 +2493,8 @@ msgstr "" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: popups/pages/Wallet/components/ActivityList/ActivityItem.tsx #: popups/pages/Wallet/components/ActionGroup/index.tsx #: popups/pages/Wallet/Transfer/index.tsx @@ -2611,6 +2698,7 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "Switch other account" msgstr "" @@ -2648,15 +2736,22 @@ msgid "The chainID is not equal to the currently connected one." msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The code is incorrect." msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#: dashboard/pages/SetupPersona/Backup/helpers.tsx msgid "The download link is expired" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +msgid "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx msgid "The mnemonic word has been copied, please keep it in a safe place." msgstr "" @@ -2695,6 +2790,7 @@ msgstr "" msgid "The persona name already exists." msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The phone number is incorrect." msgstr "" @@ -2981,6 +3077,8 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Verification code sent" msgstr "" @@ -3091,8 +3189,8 @@ msgid "Welcome Back" msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -msgid "Welcome to Mask Cloud Services" -msgstr "" +#~ msgid "Welcome to Mask Cloud Services" +#~ msgstr "" #: popups/pages/Personas/Home/UI.tsx msgid "Welcome to Mask Network" @@ -3102,6 +3200,10 @@ msgstr "" msgid "Welcome to use Mask Network" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "when you click Add Google Drive button,you will be forwarded to Google authorization pages." +msgstr "" + #: popups/pages/Wallet/ExportPrivateKey/index.tsx msgid "Write down mnemonic words" msgstr "" @@ -3115,8 +3217,8 @@ msgid "You could check the verification result on Mask Pop-up after few minutes. msgstr "" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "You have backed up your data." -msgstr "" +#~ msgid "You have backed up your data." +#~ msgstr "" #: dashboard/pages/SetupPersona/Onboarding/index.tsx #~ msgid "You have recovered" @@ -3148,6 +3250,10 @@ msgstr "" msgid "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." msgstr "" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Your file has been successfully merged into the browser data." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx msgid "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." msgstr "" diff --git a/packages/mask/shared-ui/locale/ja-JP.json b/packages/mask/shared-ui/locale/ja-JP.json index 30f542f10f27..11f6c207f7d0 100644 --- a/packages/mask/shared-ui/locale/ja-JP.json +++ b/packages/mask/shared-ui/locale/ja-JP.json @@ -61,6 +61,7 @@ "5YfW3S": ["Add an encrypted comment..."], "aTzTyw": ["資産を追加"], "M0FgOk": ["連絡先を追加する"], + "FAKSZG": ["Add google Drive"], "LP+1Z7": ["ネットワークを追加"], "68m9Nl": ["既存のグループに新しいアドレスを追加"], "1LkMSp": ["提案されたトークンを追加"], @@ -100,6 +101,8 @@ "7QptC5": ["自動ロック時間"], "nTFx7o": ["アバターを設定しました"], "iH8pgl": ["Back"], + "npsHM5": ["Back Up to Google Drive"], + "V+1pjj": ["Back Up Your Data Your Way"], "rLgPvm": ["バックアップ"], "h7+V7K": ["バックアップと復元"], "QrHM/A": ["バックアップがダウンロードされ、ローカルに正常に統合されました。"], @@ -111,6 +114,7 @@ "ERYJcj": ["バックアップパスワードは、大文字、小文字、特殊文字および数字を含む8-20文字でなければなりません。"], "hUF4dG": ["バックアップパスワードの設定が完了しました"], "UtKGok": ["ペルソナをバックアップする"], + "+m7x4e": ["Backup Successful"], "7Ry254": ["マスククラウドサービスにバックアップ"], "6IHgRq": ["Backup to the Cloud"], "QQPCqQ": ["ウォレットをバックアップする"], @@ -138,6 +142,9 @@ "tcHpvG": ["所有者変更"], "fDP2k4": ["支払いパスワードを変更する"], "IXbRbM": ["他のウォレットを選択してください"], + "lU6uRb": [ + "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." + ], "xH6cn+": ["トークンを選択"], "+adBn3": ["Chrome - external extension"], "EsKgLS": ["ここをクリックしてクイックスタートを切りましょう。"], @@ -209,6 +216,7 @@ "pvnfJD": ["ダーク"], "Sp/me1": ["データ"], "HKH+W+": ["データ"], + "K8jCIB": ["Data backed up successfully!"], "3B4jmY": ["データの相関値"], "rAcOY1": [ "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." @@ -284,7 +292,9 @@ "SLEDBF": ["ウォレットを追加できませんでした。もう一度やり直してください。"], "09vHB+": ["Failed to decrypt."], "jK7nvW": ["Failed to disconnect wallet"], + "9hzYBR": ["Failed to download and merge the backup: ", ["0"]], "0tkvsz": ["Failed to download and merge the backup."], + "etATpN": ["Failed to login: ", ["0"]], "mrAv9J": ["ネットワークを保存できませんでした"], "ZsjtYe": ["アバターを設定できませんでした。"], "RWoEqV": ["Failed to transfer token: ", ["message"]], @@ -321,6 +331,7 @@ "tdZPpe": ["Generating your"], "CiU2m/": ["Generating your "], "2p7WMm": ["Give permission to access <0/> your ", ["0"], "?"], + "w9Xmw/": ["Google Drive"], "76gPWk": ["了解"], "HqiAyF": ["Gwei(グワイ)"], "pu3rTn": ["GWEI"], @@ -352,6 +363,7 @@ "fza4cg": ["ウォレットをインポート"], "HvDfH/": ["インポートされました"], "m3XvO+": ["インポートされたウォレット"], + "ANk/Zy": ["Incorrect Backup Password"], "a23gJr": ["バックアップパスワードが正しくありません。"], "bapItf": ["バックアップパスワードが正しくありません。"], "bassMa": ["クラウドバックアップのパスワードが正しくありません。もう一度やり直してください。"], @@ -392,9 +404,11 @@ "jygqM9": ["Loading account information..."], "EBC6hj": ["ローカルバックアップ"], "shPV5O": ["ローカルペルソナまたはウォレットのみ"], + "crVIc7": ["Locale Backup"], "KGHnSc": ["ウォレットをロックする"], "FgAxTj": ["ログアウト"], "kC3Q3d": ["Login to Mask Cloud"], + "nOhz3x": ["Logout"], "NkgvWN": ["ログアウトに失敗しました。"], "o4Iu2W": ["ログアウト完了"], "nTWWCZ": ["低い"], @@ -404,6 +418,7 @@ ], "lET9my": ["マスクには次の権限が必要です"], "siroPf": ["Mask Network"], + "jhoAzY": ["Mask Network Cloud"], "lWOC+h": ["Mask Network requires you to authorize the following websites before using it."], "HsoakN": ["masknetwork"], "CK1KXz": ["最大"], @@ -420,7 +435,9 @@ "sQJ3RN": ["最大優先料金がネットワーク状況に対して低すぎます。"], "ITBQO3": ["最大優先手数料は0GWEIより大きくなければなりません"], "agPptk": ["中間"], + "lPsa94": ["Merge Completed"], "RH8jSA": ["ローカルデータベースにデータを統合"], + "E6YUtc": ["Merge to Browser"], "QTomF0": ["ローカルに統合"], "xDAtGP": ["メッセージ"], "pGElS5": ["ニーモニック"], @@ -653,6 +670,9 @@ "VdhBp2": ["chainIDは現在接続されているものと等しくありません。"], "h3Vpnw": ["このコードは正しくありません"], "7w1rTm": ["The download link is expired"], + "oYkran": [ + "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." + ], "o+zDNQ": ["The mnemonic word has been copied, please keep it in a safe place."], "yRuvmU": ["The mnemonic words has been copied, please keep it in a safe place."], "thX7xs": [ @@ -793,6 +813,7 @@ "m74yjp": ["マスククラウドサービスへようこそ"], "hEpXzO": ["Mask Networkへようこそ"], "fFYJ8f": ["Mask Networkへようこそ"], + "OLUEnY": ["when you click Add Google Drive button,you will be forwarded to Google authorization pages."], "Qbo7Ev": ["ニーモニックワードを書き留めてください"], "f5AidZ": ["Write down recovery phrase"], "yGyJ1l": [ @@ -810,6 +831,7 @@ "FnQek5": [ "このサイトへの接続は暗号化されていないため、敵対的な第三者によって変更されます。このリクエストを拒否することを強くお勧めします。" ], + "UDKbPj": ["Your file has been successfully merged into the browser data."], "Q68Nuc": [ "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." ], diff --git a/packages/mask/shared-ui/locale/ja-JP.po b/packages/mask/shared-ui/locale/ja-JP.po index 5c070b9bbbe4..2bc19234edb9 100644 --- a/packages/mask/shared-ui/locale/ja-JP.po +++ b/packages/mask/shared-ui/locale/ja-JP.po @@ -162,6 +162,10 @@ msgstr "資産を追加" msgid "Add Contact" msgstr "連絡先を追加する" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Add google Drive" +msgstr "" + #: popups/pages/Wallet/NetworkManagement/index.tsx #: popups/pages/Wallet/EditNetwork/index.tsx msgid "Add Network" @@ -305,13 +309,25 @@ msgid "Avatar set successfully" msgstr "アバターを設定しました" #: content-script/components/shared/SelectRecipients/SelectRecipientsDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx #: popups/pages/Personas/ExportPrivateKey/index.tsx #: popups/pages/Personas/AccountDetail/UI.tsx msgid "Back" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Back Up to Google Drive" +msgstr "" + #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Back Up Your Data Your Way" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/Logout/index.tsx #: popups/modals/SwitchPersonaModal/index.tsx msgid "Backup" @@ -322,16 +338,16 @@ msgid "Backup & Recovery" msgstr "バックアップと復元" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup downloaded and merged to local successfully." -msgstr "バックアップがダウンロードされ、ローカルに正常に統合されました。" +#~ msgid "Backup downloaded and merged to local successfully." +#~ msgstr "バックアップがダウンロードされ、ローカルに正常に統合されました。" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup Failed" msgstr "バックアップ失敗" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "Backup is saved to Mask Cloud Service." -msgstr "" +#~ msgid "Backup is saved to Mask Cloud Service." +#~ msgstr "" #. placeholder {0}: user.localBackupAt #. placeholder {0}: user.cloudBackupAt @@ -346,6 +362,7 @@ msgid "Backup password" msgstr "バックアップパスワード" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Settings/index.tsx @@ -368,9 +385,13 @@ msgstr "バックアップパスワードの設定が完了しました" msgid "Backup Persona" msgstr "ペルソナをバックアップする" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Backup Successful" +msgstr "" + #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup to Mask Cloud Service" -msgstr "マスククラウドサービスにバックアップ" +#~ msgid "Backup to Mask Cloud Service" +#~ msgstr "マスククラウドサービスにバックアップ" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup to the Cloud" @@ -510,6 +531,11 @@ msgstr "支払いパスワードを変更する" msgid "Choose another wallet" msgstr "他のウォレットを選択してください" +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." +msgstr "" + #: popups/modals/ChooseToken/index.tsx msgid "Choose Token" msgstr "トークンを選択" @@ -535,6 +561,7 @@ msgid "Close" msgstr "閉じる" #: dashboard/pages/SetupPersona/Recovery/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx #: dashboard/components/Restore/ConfirmSynchronizePasswordDialog.tsx #: popups/pages/Settings/index.tsx msgid "Cloud Backup" @@ -607,8 +634,6 @@ msgstr "" msgid "Confirm to hide {name}? You can redisplay it by re-adding this NFT at any time." msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/ConnectWallet/index.tsx msgid "Congratulations" msgstr "おめでとうございます!" @@ -711,6 +736,7 @@ msgstr "コンタクト" #: dashboard/pages/SetupPersona/SignUp/index.tsx #: dashboard/pages/SetupPersona/Mnemonic/index.tsx #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx #: dashboard/pages/CreateMaskWallet/AddDeriveWallet/index.tsx #: dashboard/components/Restore/RestoreWalletFromLocal.tsx @@ -837,13 +863,17 @@ msgstr "データ" msgid "Data" msgstr "データ" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Data backed up successfully!" +msgstr "" + #: popups/pages/Settings/index.tsx msgid "Data correlation" msgstr "データの相関値" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." -msgstr "" +#~ msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." +#~ msgstr "" #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx @@ -951,14 +981,17 @@ msgid "Done" msgstr "完了!" #: content-script/components/InjectedComponents/AutoPasteFailedDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx msgid "Download" msgstr "ダウンロード" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Download backup" -msgstr "バックアップをダウンロード" +#~ msgid "Download backup" +#~ msgstr "バックアップをダウンロード" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx msgid "Download Backup" msgstr "バックアップのダウンロード" @@ -988,11 +1021,14 @@ msgid "eg: X accounts, persona public keys, wallet addresses or ENS" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email" msgstr "メールアドレス" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email verification code" msgstr "電子メール検証コード" @@ -1118,8 +1154,18 @@ msgstr "" msgid "Failed to disconnect wallet" msgstr "" +#. placeholder {0}: (err as Error).message #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Failed to download and merge the backup." +msgid "Failed to download and merge the backup: {0}" +msgstr "" + +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Failed to download and merge the backup." +#~ msgstr "" + +#. placeholder {0}: (err as Error).message +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Failed to login: {0}" msgstr "" #: popups/pages/Wallet/EditNetwork/index.tsx @@ -1279,6 +1325,11 @@ msgstr "" msgid "Give permission to access <0/> your {0}?" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Google Drive" +msgstr "" + #: dashboard/pages/CreateMaskWallet/Onboarding/index.tsx msgid "Got it" msgstr "了解" @@ -1389,6 +1440,10 @@ msgstr "インポートされました" msgid "Imported Wallets" msgstr "インポートされたウォレット" +#: dashboard/hooks/useBackupFormState.ts +msgid "Incorrect Backup Password" +msgstr "" + #: popups/modals/VerifyBackupPasswordModal/index.tsx #: popups/modals/ChangeBackupPasswordModal/index.tsx msgid "Incorrect backup password." @@ -1418,6 +1473,7 @@ msgid "Incorrect password" msgstr "パスワードが間違っています" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts @@ -1425,7 +1481,6 @@ msgstr "パスワードが間違っています" #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts -#: dashboard/hooks/useBackupFormState.ts msgid "Incorrect Password" msgstr "パスワードが間違っています" @@ -1474,6 +1529,8 @@ msgid "Invalid Block Explorer URL." msgstr "無効なブロックエクスプローラのURLです。" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "Invalid email address format." msgstr "メールアドレスが正しくありません。" @@ -1576,6 +1633,10 @@ msgstr "ローカルバックアップ" msgid "Local persona or wallet only" msgstr "ローカルペルソナまたはウォレットのみ" +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Locale Backup" +msgstr "" + #: popups/pages/Wallet/SwitchWallet/index.tsx msgid "Lock Wallet" msgstr "ウォレットをロックする" @@ -1590,6 +1651,11 @@ msgstr "ログアウト" msgid "Login to Mask Cloud" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Logout" +msgstr "" + #: popups/pages/Personas/Logout/index.tsx msgid "Logout failed" msgstr "ログアウトに失敗しました。" @@ -1618,9 +1684,14 @@ msgstr "マスクには次の権限が必要です" #: content-script/components/InjectedComponents/ToolboxUnstyled.tsx #: content-script/components/InjectedComponents/PostDialogHint.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx msgid "Mask Network" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +msgid "Mask Network Cloud" +msgstr "" + #: content-script/components/InjectedComponents/PermissionBoundary.tsx msgid "Mask Network requires you to authorize the following websites before using it." msgstr "" @@ -1692,15 +1763,24 @@ msgstr "最大優先手数料は0GWEIより大きくなければなりません" msgid "Medium" msgstr "中間" -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Merge Completed" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx msgid "Merge data to local database" msgstr "ローカルデータベースにデータを統合" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Merge to local" -msgstr "ローカルに統合" +msgid "Merge to Browser" +msgstr "" + +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Merge to local" +#~ msgstr "ローカルに統合" #: popups/components/SignRequestInfo/index.tsx msgid "Message" @@ -1716,10 +1796,12 @@ msgid "Mnemonic word" msgstr "ニーモニック単語" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx msgid "Mobile" msgstr "モバイル" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Mobile number" msgstr "携帯番号" @@ -1837,6 +1919,7 @@ msgid "No back up" msgstr "バックアップはありません" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "No backups found" msgstr "バックアップが見つかりません" @@ -1949,7 +2032,6 @@ msgid "Other social networking platforms, such as <0>Instagram, <1>Facebook, msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Overwrite Backup" msgstr "バックアップを上書き" @@ -1968,6 +2050,7 @@ msgid "Paste manually" msgstr "手動でペーストする" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Wallet/SetPaymentPassword/index.tsx #: popups/modals/WalletRemoveModal/index.tsx @@ -2049,6 +2132,7 @@ msgid "Personas" msgstr "ペルソナ" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Phone verification code" msgstr "モバイル認証コード" @@ -2104,7 +2188,6 @@ msgstr "" #: dashboard/pages/SetupPersona/Recovery/index.tsx #: dashboard/pages/SetupPersona/LocalBackup/index.tsx -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx msgid "Please select the appropriate method to restore your personal data." msgstr "12単語の復元フレーズは、ペルソナデータを回復するために使用されます。" @@ -2299,6 +2382,8 @@ msgstr "選びなおす" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Resend" msgstr "再送信" @@ -2413,6 +2498,8 @@ msgstr "ウォレットを選択" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: popups/pages/Wallet/components/ActivityList/ActivityItem.tsx #: popups/pages/Wallet/components/ActionGroup/index.tsx #: popups/pages/Wallet/Transfer/index.tsx @@ -2616,6 +2703,7 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "Switch other account" msgstr "他のアカウントを切り替え" @@ -2653,15 +2741,22 @@ msgid "The chainID is not equal to the currently connected one." msgstr "chainIDは現在接続されているものと等しくありません。" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The code is incorrect." msgstr "このコードは正しくありません" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#: dashboard/pages/SetupPersona/Backup/helpers.tsx msgid "The download link is expired" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +msgid "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx msgid "The mnemonic word has been copied, please keep it in a safe place." msgstr "" @@ -2700,6 +2795,7 @@ msgstr "" msgid "The persona name already exists." msgstr "このペルソナ名は既に存在しています." +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The phone number is incorrect." msgstr "電話番号が間違っています。" @@ -2986,6 +3082,8 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Verification code sent" msgstr "認証コードを送信しました" @@ -3096,8 +3194,8 @@ msgid "Welcome Back" msgstr "おかえりなさい" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -msgid "Welcome to Mask Cloud Services" -msgstr "マスククラウドサービスへようこそ" +#~ msgid "Welcome to Mask Cloud Services" +#~ msgstr "マスククラウドサービスへようこそ" #: popups/pages/Personas/Home/UI.tsx msgid "Welcome to Mask Network" @@ -3107,6 +3205,10 @@ msgstr "Mask Networkへようこそ" msgid "Welcome to use Mask Network" msgstr "Mask Networkへようこそ" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "when you click Add Google Drive button,you will be forwarded to Google authorization pages." +msgstr "" + #: popups/pages/Wallet/ExportPrivateKey/index.tsx msgid "Write down mnemonic words" msgstr "ニーモニックワードを書き留めてください" @@ -3120,8 +3222,8 @@ msgid "You could check the verification result on Mask Pop-up after few minutes. msgstr "" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "You have backed up your data." -msgstr "" +#~ msgid "You have backed up your data." +#~ msgstr "" #: dashboard/pages/SetupPersona/Onboarding/index.tsx #~ msgid "You have recovered" @@ -3153,6 +3255,10 @@ msgstr "" msgid "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." msgstr "このサイトへの接続は暗号化されていないため、敵対的な第三者によって変更されます。このリクエストを拒否することを強くお勧めします。" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Your file has been successfully merged into the browser data." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx msgid "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." msgstr "" diff --git a/packages/mask/shared-ui/locale/ko-KR.json b/packages/mask/shared-ui/locale/ko-KR.json index 261484d2096f..01ff9f3c2d71 100644 --- a/packages/mask/shared-ui/locale/ko-KR.json +++ b/packages/mask/shared-ui/locale/ko-KR.json @@ -61,6 +61,7 @@ "5YfW3S": ["Add an encrypted comment..."], "aTzTyw": ["자산 추가"], "M0FgOk": ["연락처 추가"], + "FAKSZG": ["Add google Drive"], "LP+1Z7": ["네트워크 추가"], "68m9Nl": ["현존 그룹에 새로운 주소 추가"], "1LkMSp": ["Add Suggested Token"], @@ -100,6 +101,8 @@ "7QptC5": ["Auto-lock 시간"], "nTFx7o": ["아바타 설정 성공"], "iH8pgl": ["뒤로"], + "npsHM5": ["Back Up to Google Drive"], + "V+1pjj": ["Back Up Your Data Your Way"], "rLgPvm": ["백업"], "h7+V7K": ["백업 맟 복구"], "QrHM/A": ["백업을 다운로드하여 로컬로 병합했습니다."], @@ -111,6 +114,7 @@ "ERYJcj": ["백업 비밀번호는 대문자, 소문자, 특수 문자 및 숫자를 포함하여 8-20자여야 합니다."], "hUF4dG": ["백업 비밀번호가 설정되었습니다."], "UtKGok": ["페르소나 백업"], + "+m7x4e": ["Backup Successful"], "7Ry254": ["Mask Cloud Service 백업"], "6IHgRq": ["Backup to the Cloud"], "QQPCqQ": ["지갑 백업"], @@ -138,6 +142,9 @@ "tcHpvG": ["소유자 변경"], "fDP2k4": ["결제 비밀번호 변경"], "IXbRbM": ["다른 월렛을 선택하세요"], + "lU6uRb": [ + "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." + ], "xH6cn+": ["토큰 선택"], "+adBn3": ["Chrome - external extension"], "EsKgLS": ["퀵 스타트"], @@ -209,6 +216,7 @@ "pvnfJD": ["다크"], "Sp/me1": ["데이터"], "HKH+W+": ["데이터"], + "K8jCIB": ["Data backed up successfully!"], "3B4jmY": ["데이터 수정"], "rAcOY1": [ "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." @@ -284,7 +292,9 @@ "SLEDBF": ["월렛 추가 실패, 다시 시도하기"], "09vHB+": ["Failed to decrypt."], "jK7nvW": ["Failed to disconnect wallet"], + "9hzYBR": ["Failed to download and merge the backup: ", ["0"]], "0tkvsz": ["Failed to download and merge the backup."], + "etATpN": ["Failed to login: ", ["0"]], "mrAv9J": ["네트워크 저장 실패"], "ZsjtYe": ["아바타 설정 실패"], "RWoEqV": ["Failed to transfer token: ", ["message"]], @@ -321,6 +331,7 @@ "tdZPpe": ["Generating your"], "CiU2m/": ["Generating your "], "2p7WMm": ["Give permission to access <0/> your ", ["0"], "?"], + "w9Xmw/": ["Google Drive"], "76gPWk": ["알겠습니다"], "HqiAyF": ["Gwei"], "pu3rTn": ["GWEI"], @@ -352,6 +363,7 @@ "fza4cg": ["월렛 불러오기"], "HvDfH/": ["불러오기 완료됨"], "m3XvO+": ["월렛 불러오기"], + "ANk/Zy": ["Incorrect Backup Password"], "a23gJr": ["잘못된 비밀번호."], "bapItf": ["잘못된 백업 비밀번호."], "bassMa": ["잘못된 클라우드 백업 비밀번호입니다. 다시 시도하세요."], @@ -392,9 +404,11 @@ "jygqM9": ["Loading account information..."], "EBC6hj": ["로컬 백업"], "shPV5O": ["로컬 페르소나나 월렛만 가능합니다."], + "crVIc7": ["Locale Backup"], "KGHnSc": ["로컬 월렛"], "FgAxTj": ["로그아웃"], "kC3Q3d": ["Login to Mask Cloud"], + "nOhz3x": ["Logout"], "NkgvWN": ["로그아웃 실패"], "o4Iu2W": ["로그아웃 성공"], "nTWWCZ": ["낮음"], @@ -404,6 +418,7 @@ ], "lET9my": ["Mask가 아래 권한을 요청합니다."], "siroPf": ["Mask Network"], + "jhoAzY": ["Mask Network Cloud"], "lWOC+h": ["Mask Network requires you to authorize the following websites before using it."], "HsoakN": ["masknetwork"], "CK1KXz": ["최대치"], @@ -420,7 +435,9 @@ "sQJ3RN": ["네트워크 컨디션에 대한 최대 우선 가스비는 매우 낮습니다."], "ITBQO3": ["최대 우선 가스비는 0 GWEI보다 높아야 합니다"], "agPptk": ["보통"], + "lPsa94": ["Merge Completed"], "RH8jSA": ["데이터를 로컬 데이터베이스에 병합하기"], + "E6YUtc": ["Merge to Browser"], "QTomF0": ["로컬로 합병하기"], "xDAtGP": ["메시지"], "pGElS5": ["니모닉"], @@ -651,6 +668,9 @@ "VdhBp2": ["The chainID is not equal to the currently connected one."], "h3Vpnw": ["코드가 잘못되었습니다."], "7w1rTm": ["The download link is expired"], + "oYkran": [ + "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." + ], "o+zDNQ": ["The mnemonic word has been copied, please keep it in a safe place."], "yRuvmU": ["The mnemonic words has been copied, please keep it in a safe place."], "thX7xs": [ @@ -789,6 +809,7 @@ "m74yjp": ["Welcome to Mask Cloud Services"], "hEpXzO": ["환영합니다"], "fFYJ8f": ["환영합니다"], + "OLUEnY": ["when you click Add Google Drive button,you will be forwarded to Google authorization pages."], "Qbo7Ev": ["니모닉 단어를 적어두세요"], "f5AidZ": ["Write down recovery phrase"], "yGyJ1l": [ @@ -806,6 +827,7 @@ "FnQek5": [ "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." ], + "UDKbPj": ["Your file has been successfully merged into the browser data."], "Q68Nuc": [ "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." ], diff --git a/packages/mask/shared-ui/locale/ko-KR.po b/packages/mask/shared-ui/locale/ko-KR.po index 9def83f290ec..40fc919406c1 100644 --- a/packages/mask/shared-ui/locale/ko-KR.po +++ b/packages/mask/shared-ui/locale/ko-KR.po @@ -162,6 +162,10 @@ msgstr "자산 추가" msgid "Add Contact" msgstr "연락처 추가" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Add google Drive" +msgstr "" + #: popups/pages/Wallet/NetworkManagement/index.tsx #: popups/pages/Wallet/EditNetwork/index.tsx msgid "Add Network" @@ -305,13 +309,25 @@ msgid "Avatar set successfully" msgstr "아바타 설정 성공" #: content-script/components/shared/SelectRecipients/SelectRecipientsDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx #: popups/pages/Personas/ExportPrivateKey/index.tsx #: popups/pages/Personas/AccountDetail/UI.tsx msgid "Back" msgstr "뒤로" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Back Up to Google Drive" +msgstr "" + #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Back Up Your Data Your Way" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/Logout/index.tsx #: popups/modals/SwitchPersonaModal/index.tsx msgid "Backup" @@ -322,16 +338,16 @@ msgid "Backup & Recovery" msgstr "백업 맟 복구" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup downloaded and merged to local successfully." -msgstr "백업을 다운로드하여 로컬로 병합했습니다." +#~ msgid "Backup downloaded and merged to local successfully." +#~ msgstr "백업을 다운로드하여 로컬로 병합했습니다." #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup Failed" msgstr "백업 실패" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "Backup is saved to Mask Cloud Service." -msgstr "" +#~ msgid "Backup is saved to Mask Cloud Service." +#~ msgstr "" #. placeholder {0}: user.localBackupAt #. placeholder {0}: user.cloudBackupAt @@ -346,6 +362,7 @@ msgid "Backup password" msgstr "비밀번호 백업" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Settings/index.tsx @@ -368,9 +385,13 @@ msgstr "백업 비밀번호가 설정되었습니다." msgid "Backup Persona" msgstr "페르소나 백업" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Backup Successful" +msgstr "" + #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup to Mask Cloud Service" -msgstr "Mask Cloud Service 백업" +#~ msgid "Backup to Mask Cloud Service" +#~ msgstr "Mask Cloud Service 백업" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup to the Cloud" @@ -510,6 +531,11 @@ msgstr "결제 비밀번호 변경" msgid "Choose another wallet" msgstr "다른 월렛을 선택하세요" +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." +msgstr "" + #: popups/modals/ChooseToken/index.tsx msgid "Choose Token" msgstr "토큰 선택" @@ -535,6 +561,7 @@ msgid "Close" msgstr "닫기" #: dashboard/pages/SetupPersona/Recovery/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx #: dashboard/components/Restore/ConfirmSynchronizePasswordDialog.tsx #: popups/pages/Settings/index.tsx msgid "Cloud Backup" @@ -607,8 +634,6 @@ msgstr "" msgid "Confirm to hide {name}? You can redisplay it by re-adding this NFT at any time." msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/ConnectWallet/index.tsx msgid "Congratulations" msgstr "축하합니다" @@ -711,6 +736,7 @@ msgstr "연락처" #: dashboard/pages/SetupPersona/SignUp/index.tsx #: dashboard/pages/SetupPersona/Mnemonic/index.tsx #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx #: dashboard/pages/CreateMaskWallet/AddDeriveWallet/index.tsx #: dashboard/components/Restore/RestoreWalletFromLocal.tsx @@ -837,13 +863,17 @@ msgstr "데이터" msgid "Data" msgstr "데이터" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Data backed up successfully!" +msgstr "" + #: popups/pages/Settings/index.tsx msgid "Data correlation" msgstr "데이터 수정" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." -msgstr "" +#~ msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." +#~ msgstr "" #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx @@ -951,14 +981,17 @@ msgid "Done" msgstr "완료" #: content-script/components/InjectedComponents/AutoPasteFailedDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx msgid "Download" msgstr "다운로드" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Download backup" -msgstr "백업 다운로드" +#~ msgid "Download backup" +#~ msgstr "백업 다운로드" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx msgid "Download Backup" msgstr "백업 다운로드" @@ -988,11 +1021,14 @@ msgid "eg: X accounts, persona public keys, wallet addresses or ENS" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email" msgstr "이메일" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email verification code" msgstr "메일 인증번호" @@ -1118,8 +1154,18 @@ msgstr "" msgid "Failed to disconnect wallet" msgstr "" +#. placeholder {0}: (err as Error).message #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Failed to download and merge the backup." +msgid "Failed to download and merge the backup: {0}" +msgstr "" + +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Failed to download and merge the backup." +#~ msgstr "" + +#. placeholder {0}: (err as Error).message +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Failed to login: {0}" msgstr "" #: popups/pages/Wallet/EditNetwork/index.tsx @@ -1279,6 +1325,11 @@ msgstr "" msgid "Give permission to access <0/> your {0}?" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Google Drive" +msgstr "" + #: dashboard/pages/CreateMaskWallet/Onboarding/index.tsx msgid "Got it" msgstr "알겠습니다" @@ -1389,6 +1440,10 @@ msgstr "불러오기 완료됨" msgid "Imported Wallets" msgstr "월렛 불러오기" +#: dashboard/hooks/useBackupFormState.ts +msgid "Incorrect Backup Password" +msgstr "" + #: popups/modals/VerifyBackupPasswordModal/index.tsx #: popups/modals/ChangeBackupPasswordModal/index.tsx msgid "Incorrect backup password." @@ -1418,6 +1473,7 @@ msgid "Incorrect password" msgstr "잘못된 비밀번호" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts @@ -1425,7 +1481,6 @@ msgstr "잘못된 비밀번호" #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts -#: dashboard/hooks/useBackupFormState.ts msgid "Incorrect Password" msgstr "잘못된 비밀번호" @@ -1474,6 +1529,8 @@ msgid "Invalid Block Explorer URL." msgstr "무효한 Block Explorer URL." #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "Invalid email address format." msgstr "이메일 주소가 잘못되었습니다." @@ -1576,6 +1633,10 @@ msgstr "로컬 백업" msgid "Local persona or wallet only" msgstr "로컬 페르소나나 월렛만 가능합니다." +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Locale Backup" +msgstr "" + #: popups/pages/Wallet/SwitchWallet/index.tsx msgid "Lock Wallet" msgstr "로컬 월렛" @@ -1590,6 +1651,11 @@ msgstr "로그아웃" msgid "Login to Mask Cloud" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Logout" +msgstr "" + #: popups/pages/Personas/Logout/index.tsx msgid "Logout failed" msgstr "로그아웃 실패" @@ -1618,9 +1684,14 @@ msgstr "Mask가 아래 권한을 요청합니다." #: content-script/components/InjectedComponents/ToolboxUnstyled.tsx #: content-script/components/InjectedComponents/PostDialogHint.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx msgid "Mask Network" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +msgid "Mask Network Cloud" +msgstr "" + #: content-script/components/InjectedComponents/PermissionBoundary.tsx msgid "Mask Network requires you to authorize the following websites before using it." msgstr "" @@ -1692,15 +1763,24 @@ msgstr "최대 우선 가스비는 0 GWEI보다 높아야 합니다" msgid "Medium" msgstr "보통" -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Merge Completed" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx msgid "Merge data to local database" msgstr "데이터를 로컬 데이터베이스에 병합하기" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Merge to local" -msgstr "로컬로 합병하기" +msgid "Merge to Browser" +msgstr "" + +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Merge to local" +#~ msgstr "로컬로 합병하기" #: popups/components/SignRequestInfo/index.tsx msgid "Message" @@ -1716,10 +1796,12 @@ msgid "Mnemonic word" msgstr "니모닉 단어" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx msgid "Mobile" msgstr "모바일" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Mobile number" msgstr "휴대폰 번호" @@ -1837,6 +1919,7 @@ msgid "No back up" msgstr "백업 없음" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "No backups found" msgstr "백업 없음" @@ -1949,7 +2032,6 @@ msgid "Other social networking platforms, such as <0>Instagram, <1>Facebook, msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Overwrite Backup" msgstr "백업 덮어쓰기" @@ -1968,6 +2050,7 @@ msgid "Paste manually" msgstr "수동으로 붙여넣기" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Wallet/SetPaymentPassword/index.tsx #: popups/modals/WalletRemoveModal/index.tsx @@ -2049,6 +2132,7 @@ msgid "Personas" msgstr "나의 페르소나" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Phone verification code" msgstr "" @@ -2104,7 +2188,6 @@ msgstr "" #: dashboard/pages/SetupPersona/Recovery/index.tsx #: dashboard/pages/SetupPersona/LocalBackup/index.tsx -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx msgid "Please select the appropriate method to restore your personal data." msgstr "12개 복구 문구로 페르소나 데어터를 복구할 수 있습니다." @@ -2299,6 +2382,8 @@ msgstr "다시 선택" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Resend" msgstr "다시 보내기" @@ -2413,6 +2498,8 @@ msgstr "월렛 선택" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: popups/pages/Wallet/components/ActivityList/ActivityItem.tsx #: popups/pages/Wallet/components/ActionGroup/index.tsx #: popups/pages/Wallet/Transfer/index.tsx @@ -2616,6 +2703,7 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "Switch other account" msgstr "사용자 계정 전환" @@ -2653,15 +2741,22 @@ msgid "The chainID is not equal to the currently connected one." msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The code is incorrect." msgstr "코드가 잘못되었습니다." -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#: dashboard/pages/SetupPersona/Backup/helpers.tsx msgid "The download link is expired" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +msgid "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx msgid "The mnemonic word has been copied, please keep it in a safe place." msgstr "" @@ -2700,6 +2795,7 @@ msgstr "" msgid "The persona name already exists." msgstr "이미 존재된 페르소나입니다" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The phone number is incorrect." msgstr "전화번호가 잘못되었습니다." @@ -2986,6 +3082,8 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Verification code sent" msgstr "인증 코드가 발송되었습니다" @@ -3096,8 +3194,8 @@ msgid "Welcome Back" msgstr "환영합니다" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -msgid "Welcome to Mask Cloud Services" -msgstr "" +#~ msgid "Welcome to Mask Cloud Services" +#~ msgstr "" #: popups/pages/Personas/Home/UI.tsx msgid "Welcome to Mask Network" @@ -3107,6 +3205,10 @@ msgstr "환영합니다" msgid "Welcome to use Mask Network" msgstr "환영합니다" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "when you click Add Google Drive button,you will be forwarded to Google authorization pages." +msgstr "" + #: popups/pages/Wallet/ExportPrivateKey/index.tsx msgid "Write down mnemonic words" msgstr "니모닉 단어를 적어두세요" @@ -3120,8 +3222,8 @@ msgid "You could check the verification result on Mask Pop-up after few minutes. msgstr "" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "You have backed up your data." -msgstr "" +#~ msgid "You have backed up your data." +#~ msgstr "" #: dashboard/pages/SetupPersona/Onboarding/index.tsx #~ msgid "You have recovered" @@ -3153,6 +3255,10 @@ msgstr "" msgid "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." msgstr "" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Your file has been successfully merged into the browser data." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx msgid "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." msgstr "" diff --git a/packages/mask/shared-ui/locale/zh-CN.json b/packages/mask/shared-ui/locale/zh-CN.json index 7183669cfce1..939507ae5abd 100644 --- a/packages/mask/shared-ui/locale/zh-CN.json +++ b/packages/mask/shared-ui/locale/zh-CN.json @@ -61,6 +61,7 @@ "5YfW3S": ["Add an encrypted comment..."], "aTzTyw": ["添加资产"], "M0FgOk": ["添加联系人"], + "FAKSZG": ["Add google Drive"], "LP+1Z7": ["添加网络"], "68m9Nl": ["向现有组添加新地址"], "1LkMSp": ["Add Suggested Token"], @@ -98,6 +99,8 @@ "7QptC5": ["自动锁定时间"], "nTFx7o": ["新头像设置成功"], "iH8pgl": ["返回"], + "npsHM5": ["Back Up to Google Drive"], + "V+1pjj": ["Back Up Your Data Your Way"], "rLgPvm": ["备份"], "h7+V7K": ["备份恢复"], "QrHM/A": ["备份下载并成功合并到本地。"], @@ -109,6 +112,7 @@ "ERYJcj": ["备份密码必须是 8-20 个字符,包括大写、小写、特殊字符和数字。"], "hUF4dG": ["备份密码设置成功。"], "UtKGok": ["备份身份"], + "+m7x4e": ["Backup Successful"], "7Ry254": ["备份到 Mask 云服务"], "6IHgRq": ["Backup to the Cloud"], "QQPCqQ": ["备份钱包"], @@ -136,6 +140,9 @@ "tcHpvG": ["更改管理账户"], "fDP2k4": ["修改支付密码"], "IXbRbM": ["切换钱包"], + "lU6uRb": [ + "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." + ], "xH6cn+": ["选择代币"], "+adBn3": ["Chrome - external extension"], "EsKgLS": ["点击这里快速开始。"], @@ -207,6 +214,7 @@ "pvnfJD": ["深色"], "Sp/me1": ["数据"], "HKH+W+": ["数据"], + "K8jCIB": ["Data backed up successfully!"], "3B4jmY": ["数据关联"], "rAcOY1": [ "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." @@ -282,7 +290,9 @@ "SLEDBF": ["添加钱包失败,请重试。"], "09vHB+": ["Failed to decrypt."], "jK7nvW": ["Failed to disconnect wallet"], + "9hzYBR": ["Failed to download and merge the backup: ", ["0"]], "0tkvsz": ["Failed to download and merge the backup."], + "etATpN": ["Failed to login: ", ["0"]], "mrAv9J": ["无法保存网络"], "ZsjtYe": ["加载头像失败。"], "RWoEqV": ["Failed to transfer token: ", ["message"]], @@ -319,6 +329,7 @@ "tdZPpe": ["Generating your"], "CiU2m/": ["Generating your "], "2p7WMm": ["Give permission to access <0/> your ", ["0"], "?"], + "w9Xmw/": ["Google Drive"], "76gPWk": ["好的"], "HqiAyF": ["Gwei"], "pu3rTn": ["GWEI"], @@ -350,6 +361,7 @@ "fza4cg": ["导入钱包"], "HvDfH/": ["已导入"], "m3XvO+": ["导入钱包"], + "ANk/Zy": ["Incorrect Backup Password"], "a23gJr": ["密码不正确。"], "bapItf": ["备份密码错误。"], "bassMa": ["云备份密码不正确,请重试。"], @@ -390,9 +402,11 @@ "jygqM9": ["Loading account information..."], "EBC6hj": ["本地备份"], "shPV5O": ["仅限本地身份或钱包"], + "crVIc7": ["Locale Backup"], "KGHnSc": ["锁定钱包"], "FgAxTj": ["登出"], "kC3Q3d": ["Login to Mask Cloud"], + "nOhz3x": ["Logout"], "NkgvWN": ["登出失败"], "o4Iu2W": ["退出成功"], "nTWWCZ": ["低"], @@ -402,6 +416,7 @@ ], "lET9my": ["Mask Network需要以下权限"], "siroPf": ["Mask Network"], + "jhoAzY": ["Mask Network Cloud"], "lWOC+h": ["Mask Network requires you to authorize the following websites before using it."], "HsoakN": ["masknetwork"], "CK1KXz": ["最大"], @@ -418,7 +433,9 @@ "sQJ3RN": ["最大优先费用在当前网络环境下过低。"], "ITBQO3": ["Max priority fee必须大于 0 GWEI"], "agPptk": ["中等"], + "lPsa94": ["Merge Completed"], "RH8jSA": ["合并数据到本地数据库"], + "E6YUtc": ["Merge to Browser"], "QTomF0": ["合并至本地数据"], "xDAtGP": ["信息"], "pGElS5": ["助记词"], @@ -647,6 +664,9 @@ "VdhBp2": ["The chainID is not equal to the currently connected one."], "h3Vpnw": ["验证码不正确"], "7w1rTm": ["The download link is expired"], + "oYkran": [ + "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." + ], "o+zDNQ": ["The mnemonic word has been copied, please keep it in a safe place."], "yRuvmU": ["The mnemonic words has been copied, please keep it in a safe place."], "thX7xs": [ @@ -783,6 +803,7 @@ "m74yjp": ["欢迎使用 Mask 云服务"], "hEpXzO": ["欢迎来到 Mask Network!"], "fFYJ8f": ["欢迎来到 Mask Network!"], + "OLUEnY": ["when you click Add Google Drive button,you will be forwarded to Google authorization pages."], "Qbo7Ev": ["写下助记词"], "f5AidZ": ["Write down recovery phrase"], "yGyJ1l": [ @@ -800,6 +821,7 @@ "FnQek5": [ "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." ], + "UDKbPj": ["Your file has been successfully merged into the browser data."], "Q68Nuc": [ "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." ], diff --git a/packages/mask/shared-ui/locale/zh-CN.po b/packages/mask/shared-ui/locale/zh-CN.po index 61b4a085ad81..149cf850227b 100644 --- a/packages/mask/shared-ui/locale/zh-CN.po +++ b/packages/mask/shared-ui/locale/zh-CN.po @@ -162,6 +162,10 @@ msgstr "添加资产" msgid "Add Contact" msgstr "添加联系人" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Add google Drive" +msgstr "" + #: popups/pages/Wallet/NetworkManagement/index.tsx #: popups/pages/Wallet/EditNetwork/index.tsx msgid "Add Network" @@ -305,13 +309,25 @@ msgid "Avatar set successfully" msgstr "新头像设置成功" #: content-script/components/shared/SelectRecipients/SelectRecipientsDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx #: popups/pages/Personas/ExportPrivateKey/index.tsx #: popups/pages/Personas/AccountDetail/UI.tsx msgid "Back" msgstr "返回" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Back Up to Google Drive" +msgstr "" + #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Back Up Your Data Your Way" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/Logout/index.tsx #: popups/modals/SwitchPersonaModal/index.tsx msgid "Backup" @@ -322,16 +338,16 @@ msgid "Backup & Recovery" msgstr "备份恢复" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup downloaded and merged to local successfully." -msgstr "备份下载并成功合并到本地。" +#~ msgid "Backup downloaded and merged to local successfully." +#~ msgstr "备份下载并成功合并到本地。" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup Failed" msgstr "备份失败" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "Backup is saved to Mask Cloud Service." -msgstr "" +#~ msgid "Backup is saved to Mask Cloud Service." +#~ msgstr "" #. placeholder {0}: user.localBackupAt #. placeholder {0}: user.cloudBackupAt @@ -346,6 +362,7 @@ msgid "Backup password" msgstr "备份密码" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Settings/index.tsx @@ -368,9 +385,13 @@ msgstr "备份密码设置成功。" msgid "Backup Persona" msgstr "备份身份" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Backup Successful" +msgstr "" + #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup to Mask Cloud Service" -msgstr "备份到 Mask 云服务" +#~ msgid "Backup to Mask Cloud Service" +#~ msgstr "备份到 Mask 云服务" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup to the Cloud" @@ -510,6 +531,11 @@ msgstr "修改支付密码" msgid "Choose another wallet" msgstr "切换钱包" +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." +msgstr "" + #: popups/modals/ChooseToken/index.tsx msgid "Choose Token" msgstr "选择代币" @@ -535,6 +561,7 @@ msgid "Close" msgstr "关闭" #: dashboard/pages/SetupPersona/Recovery/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx #: dashboard/components/Restore/ConfirmSynchronizePasswordDialog.tsx #: popups/pages/Settings/index.tsx msgid "Cloud Backup" @@ -607,8 +634,6 @@ msgstr "" msgid "Confirm to hide {name}? You can redisplay it by re-adding this NFT at any time." msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/ConnectWallet/index.tsx msgid "Congratulations" msgstr "恭喜您!" @@ -711,6 +736,7 @@ msgstr "联系人" #: dashboard/pages/SetupPersona/SignUp/index.tsx #: dashboard/pages/SetupPersona/Mnemonic/index.tsx #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx #: dashboard/pages/CreateMaskWallet/AddDeriveWallet/index.tsx #: dashboard/components/Restore/RestoreWalletFromLocal.tsx @@ -837,13 +863,17 @@ msgstr "数据" msgid "Data" msgstr "数据" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Data backed up successfully!" +msgstr "" + #: popups/pages/Settings/index.tsx msgid "Data correlation" msgstr "数据关联" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." -msgstr "" +#~ msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." +#~ msgstr "" #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx @@ -951,14 +981,17 @@ msgid "Done" msgstr "完成" #: content-script/components/InjectedComponents/AutoPasteFailedDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx msgid "Download" msgstr "下载" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Download backup" -msgstr "下载备份" +#~ msgid "Download backup" +#~ msgstr "下载备份" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx msgid "Download Backup" msgstr "下载备份" @@ -988,11 +1021,14 @@ msgid "eg: X accounts, persona public keys, wallet addresses or ENS" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email" msgstr "电子邮箱" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email verification code" msgstr "邮箱验证码" @@ -1118,8 +1154,18 @@ msgstr "" msgid "Failed to disconnect wallet" msgstr "" +#. placeholder {0}: (err as Error).message #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Failed to download and merge the backup." +msgid "Failed to download and merge the backup: {0}" +msgstr "" + +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Failed to download and merge the backup." +#~ msgstr "" + +#. placeholder {0}: (err as Error).message +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Failed to login: {0}" msgstr "" #: popups/pages/Wallet/EditNetwork/index.tsx @@ -1279,6 +1325,11 @@ msgstr "" msgid "Give permission to access <0/> your {0}?" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Google Drive" +msgstr "" + #: dashboard/pages/CreateMaskWallet/Onboarding/index.tsx msgid "Got it" msgstr "好的" @@ -1389,6 +1440,10 @@ msgstr "已导入" msgid "Imported Wallets" msgstr "导入钱包" +#: dashboard/hooks/useBackupFormState.ts +msgid "Incorrect Backup Password" +msgstr "" + #: popups/modals/VerifyBackupPasswordModal/index.tsx #: popups/modals/ChangeBackupPasswordModal/index.tsx msgid "Incorrect backup password." @@ -1418,6 +1473,7 @@ msgid "Incorrect password" msgstr "密码不正确" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts @@ -1425,7 +1481,6 @@ msgstr "密码不正确" #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts -#: dashboard/hooks/useBackupFormState.ts msgid "Incorrect Password" msgstr "密码不正确" @@ -1474,6 +1529,8 @@ msgid "Invalid Block Explorer URL." msgstr "无效的块浏览器URL。" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "Invalid email address format." msgstr "邮箱地址不正确。" @@ -1576,6 +1633,10 @@ msgstr "本地备份" msgid "Local persona or wallet only" msgstr "仅限本地身份或钱包" +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Locale Backup" +msgstr "" + #: popups/pages/Wallet/SwitchWallet/index.tsx msgid "Lock Wallet" msgstr "锁定钱包" @@ -1590,6 +1651,11 @@ msgstr "登出" msgid "Login to Mask Cloud" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Logout" +msgstr "" + #: popups/pages/Personas/Logout/index.tsx msgid "Logout failed" msgstr "登出失败" @@ -1618,9 +1684,14 @@ msgstr "Mask Network需要以下权限" #: content-script/components/InjectedComponents/ToolboxUnstyled.tsx #: content-script/components/InjectedComponents/PostDialogHint.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx msgid "Mask Network" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +msgid "Mask Network Cloud" +msgstr "" + #: content-script/components/InjectedComponents/PermissionBoundary.tsx msgid "Mask Network requires you to authorize the following websites before using it." msgstr "" @@ -1692,15 +1763,24 @@ msgstr "Max priority fee必须大于 0 GWEI" msgid "Medium" msgstr "中等" -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Merge Completed" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx msgid "Merge data to local database" msgstr "合并数据到本地数据库" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Merge to local" -msgstr "合并至本地数据" +msgid "Merge to Browser" +msgstr "" + +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Merge to local" +#~ msgstr "合并至本地数据" #: popups/components/SignRequestInfo/index.tsx msgid "Message" @@ -1716,10 +1796,12 @@ msgid "Mnemonic word" msgstr "助记词" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx msgid "Mobile" msgstr "手机" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Mobile number" msgstr "手机号码" @@ -1837,6 +1919,7 @@ msgid "No back up" msgstr "没有备份" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "No backups found" msgstr "没有发现备份" @@ -1949,7 +2032,6 @@ msgid "Other social networking platforms, such as <0>Instagram, <1>Facebook, msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Overwrite Backup" msgstr "覆盖备份" @@ -1968,6 +2050,7 @@ msgid "Paste manually" msgstr "手动粘贴" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Wallet/SetPaymentPassword/index.tsx #: popups/modals/WalletRemoveModal/index.tsx @@ -2049,6 +2132,7 @@ msgid "Personas" msgstr "身份" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Phone verification code" msgstr "验证码" @@ -2104,7 +2188,6 @@ msgstr "" #: dashboard/pages/SetupPersona/Recovery/index.tsx #: dashboard/pages/SetupPersona/LocalBackup/index.tsx -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx msgid "Please select the appropriate method to restore your personal data." msgstr "请选择合适的方法来恢复个人数据。" @@ -2299,6 +2382,8 @@ msgstr "重新选择" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Resend" msgstr "重新发送" @@ -2413,6 +2498,8 @@ msgstr "选择钱包" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: popups/pages/Wallet/components/ActivityList/ActivityItem.tsx #: popups/pages/Wallet/components/ActionGroup/index.tsx #: popups/pages/Wallet/Transfer/index.tsx @@ -2616,6 +2703,7 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "Switch other account" msgstr "切换帐号" @@ -2653,15 +2741,22 @@ msgid "The chainID is not equal to the currently connected one." msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The code is incorrect." msgstr "验证码不正确" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#: dashboard/pages/SetupPersona/Backup/helpers.tsx msgid "The download link is expired" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +msgid "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx msgid "The mnemonic word has been copied, please keep it in a safe place." msgstr "" @@ -2700,6 +2795,7 @@ msgstr "" msgid "The persona name already exists." msgstr "此身份名称已存在" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The phone number is incorrect." msgstr "此手机号码不正确。" @@ -2986,6 +3082,8 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Verification code sent" msgstr "验证码已发送" @@ -3096,8 +3194,8 @@ msgid "Welcome Back" msgstr "欢迎回来" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -msgid "Welcome to Mask Cloud Services" -msgstr "欢迎使用 Mask 云服务" +#~ msgid "Welcome to Mask Cloud Services" +#~ msgstr "欢迎使用 Mask 云服务" #: popups/pages/Personas/Home/UI.tsx msgid "Welcome to Mask Network" @@ -3107,6 +3205,10 @@ msgstr "欢迎来到 Mask Network!" msgid "Welcome to use Mask Network" msgstr "欢迎来到 Mask Network!" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "when you click Add Google Drive button,you will be forwarded to Google authorization pages." +msgstr "" + #: popups/pages/Wallet/ExportPrivateKey/index.tsx msgid "Write down mnemonic words" msgstr "写下助记词" @@ -3120,8 +3222,8 @@ msgid "You could check the verification result on Mask Pop-up after few minutes. msgstr "" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "You have backed up your data." -msgstr "" +#~ msgid "You have backed up your data." +#~ msgstr "" #: dashboard/pages/SetupPersona/Onboarding/index.tsx #~ msgid "You have recovered" @@ -3153,6 +3255,10 @@ msgstr "" msgid "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." msgstr "" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Your file has been successfully merged into the browser data." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx msgid "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." msgstr "" diff --git a/packages/mask/shared-ui/locale/zh-TW.json b/packages/mask/shared-ui/locale/zh-TW.json index 0944ff73d188..2fa73c62fe40 100644 --- a/packages/mask/shared-ui/locale/zh-TW.json +++ b/packages/mask/shared-ui/locale/zh-TW.json @@ -61,6 +61,7 @@ "5YfW3S": ["Add an encrypted comment..."], "aTzTyw": ["添加资产"], "M0FgOk": ["添加联系人"], + "FAKSZG": ["Add google Drive"], "LP+1Z7": ["添加网络"], "68m9Nl": ["向现有组添加新地址"], "1LkMSp": ["Add Suggested Token"], @@ -98,6 +99,8 @@ "7QptC5": ["自动锁定时间"], "nTFx7o": ["新头像设置成功"], "iH8pgl": ["返回"], + "npsHM5": ["Back Up to Google Drive"], + "V+1pjj": ["Back Up Your Data Your Way"], "rLgPvm": ["备份"], "h7+V7K": ["备份恢复"], "QrHM/A": ["备份下载并成功合并到本地。"], @@ -109,6 +112,7 @@ "ERYJcj": ["备份密码必须是 8-20 个字符,包括大写、小写、特殊字符和数字。"], "hUF4dG": ["备份密码设置成功。"], "UtKGok": ["备份身份"], + "+m7x4e": ["Backup Successful"], "7Ry254": ["备份到 Mask 云服务"], "6IHgRq": ["Backup to the Cloud"], "QQPCqQ": ["备份钱包"], @@ -136,6 +140,9 @@ "tcHpvG": ["更改管理账户"], "fDP2k4": ["修改支付密码"], "IXbRbM": ["切换钱包"], + "lU6uRb": [ + "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." + ], "xH6cn+": ["选择代币"], "+adBn3": ["Chrome - external extension"], "EsKgLS": ["点击这里快速开始。"], @@ -207,6 +214,7 @@ "pvnfJD": ["深色"], "Sp/me1": ["数据"], "HKH+W+": ["数据"], + "K8jCIB": ["Data backed up successfully!"], "3B4jmY": ["数据关联"], "rAcOY1": [ "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." @@ -282,7 +290,9 @@ "SLEDBF": ["添加钱包失败,请重试。"], "09vHB+": ["Failed to decrypt."], "jK7nvW": ["Failed to disconnect wallet"], + "9hzYBR": ["Failed to download and merge the backup: ", ["0"]], "0tkvsz": ["Failed to download and merge the backup."], + "etATpN": ["Failed to login: ", ["0"]], "mrAv9J": ["无法保存网络"], "ZsjtYe": ["加载头像失败。"], "RWoEqV": ["Failed to transfer token: ", ["message"]], @@ -319,6 +329,7 @@ "tdZPpe": ["Generating your"], "CiU2m/": ["Generating your "], "2p7WMm": ["Give permission to access <0/> your ", ["0"], "?"], + "w9Xmw/": ["Google Drive"], "76gPWk": ["好的"], "HqiAyF": ["Gwei"], "pu3rTn": ["GWEI"], @@ -350,6 +361,7 @@ "fza4cg": ["导入钱包"], "HvDfH/": ["已导入"], "m3XvO+": ["导入钱包"], + "ANk/Zy": ["Incorrect Backup Password"], "a23gJr": ["密码不正确。"], "bapItf": ["备份密码错误。"], "bassMa": ["云备份密码不正确,请重试。"], @@ -390,9 +402,11 @@ "jygqM9": ["Loading account information..."], "EBC6hj": ["本地备份"], "shPV5O": ["仅限本地身份或钱包"], + "crVIc7": ["Locale Backup"], "KGHnSc": ["锁定钱包"], "FgAxTj": ["登出"], "kC3Q3d": ["Login to Mask Cloud"], + "nOhz3x": ["Logout"], "NkgvWN": ["登出失败"], "o4Iu2W": ["退出成功"], "nTWWCZ": ["低"], @@ -402,6 +416,7 @@ ], "lET9my": ["Mask Network需要以下权限"], "siroPf": ["Mask Network"], + "jhoAzY": ["Mask Network Cloud"], "lWOC+h": ["Mask Network requires you to authorize the following websites before using it."], "HsoakN": ["masknetwork"], "CK1KXz": ["最大"], @@ -418,7 +433,9 @@ "sQJ3RN": ["最大优先费用在当前网络环境下过低。"], "ITBQO3": ["Max priority fee必须大于 0 GWEI"], "agPptk": ["中等"], + "lPsa94": ["Merge Completed"], "RH8jSA": ["合并数据到本地数据库"], + "E6YUtc": ["Merge to Browser"], "QTomF0": ["合并至本地数据"], "xDAtGP": ["信息"], "pGElS5": ["助记词"], @@ -647,6 +664,9 @@ "VdhBp2": ["The chainID is not equal to the currently connected one."], "h3Vpnw": ["验证码不正确"], "7w1rTm": ["The download link is expired"], + "oYkran": [ + "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." + ], "o+zDNQ": ["The mnemonic word has been copied, please keep it in a safe place."], "yRuvmU": ["The mnemonic words has been copied, please keep it in a safe place."], "thX7xs": [ @@ -783,6 +803,7 @@ "m74yjp": ["欢迎使用 Mask 云服务"], "hEpXzO": ["欢迎来到 Mask Network!"], "fFYJ8f": ["欢迎来到 Mask Network!"], + "OLUEnY": ["when you click Add Google Drive button,you will be forwarded to Google authorization pages."], "Qbo7Ev": ["写下助记词"], "f5AidZ": ["Write down recovery phrase"], "yGyJ1l": [ @@ -800,6 +821,7 @@ "FnQek5": [ "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." ], + "UDKbPj": ["Your file has been successfully merged into the browser data."], "Q68Nuc": [ "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." ], diff --git a/packages/mask/shared-ui/locale/zh-TW.po b/packages/mask/shared-ui/locale/zh-TW.po index 8cbe7a8af2ed..864ffb6872c9 100644 --- a/packages/mask/shared-ui/locale/zh-TW.po +++ b/packages/mask/shared-ui/locale/zh-TW.po @@ -162,6 +162,10 @@ msgstr "" msgid "Add Contact" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Add google Drive" +msgstr "" + #: popups/pages/Wallet/NetworkManagement/index.tsx #: popups/pages/Wallet/EditNetwork/index.tsx msgid "Add Network" @@ -305,13 +309,25 @@ msgid "Avatar set successfully" msgstr "" #: content-script/components/shared/SelectRecipients/SelectRecipientsDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx #: popups/pages/Personas/ExportPrivateKey/index.tsx #: popups/pages/Personas/AccountDetail/UI.tsx msgid "Back" msgstr "返回" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Back Up to Google Drive" +msgstr "" + #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Back Up Your Data Your Way" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/Logout/index.tsx #: popups/modals/SwitchPersonaModal/index.tsx msgid "Backup" @@ -322,16 +338,16 @@ msgid "Backup & Recovery" msgstr "" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup downloaded and merged to local successfully." -msgstr "" +#~ msgid "Backup downloaded and merged to local successfully." +#~ msgstr "" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup Failed" msgstr "備份失敗" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "Backup is saved to Mask Cloud Service." -msgstr "" +#~ msgid "Backup is saved to Mask Cloud Service." +#~ msgstr "" #. placeholder {0}: user.localBackupAt #. placeholder {0}: user.cloudBackupAt @@ -346,6 +362,7 @@ msgid "Backup password" msgstr "備份密碼" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Settings/index.tsx @@ -368,10 +385,14 @@ msgstr "" msgid "Backup Persona" msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Backup to Mask Cloud Service" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Backup Successful" msgstr "" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Backup to Mask Cloud Service" +#~ msgstr "" + #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Backup to the Cloud" msgstr "" @@ -510,6 +531,11 @@ msgstr "" msgid "Choose another wallet" msgstr "" +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Choose from multiple backup options, now including encrypted storage via your authorized Google Drive for added security and flexibility." +msgstr "" + #: popups/modals/ChooseToken/index.tsx msgid "Choose Token" msgstr "" @@ -535,6 +561,7 @@ msgid "Close" msgstr "關閉" #: dashboard/pages/SetupPersona/Recovery/index.tsx +#: dashboard/pages/SetupPersona/Backup/index.tsx #: dashboard/components/Restore/ConfirmSynchronizePasswordDialog.tsx #: popups/pages/Settings/index.tsx msgid "Cloud Backup" @@ -607,8 +634,6 @@ msgstr "" msgid "Confirm to hide {name}? You can redisplay it by re-adding this NFT at any time." msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Personas/ConnectWallet/index.tsx msgid "Congratulations" msgstr "" @@ -711,6 +736,7 @@ msgstr "聯繫人" #: dashboard/pages/SetupPersona/SignUp/index.tsx #: dashboard/pages/SetupPersona/Mnemonic/index.tsx #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx #: dashboard/pages/CreateMaskWallet/AddDeriveWallet/index.tsx #: dashboard/components/Restore/RestoreWalletFromLocal.tsx @@ -837,13 +863,17 @@ msgstr "" msgid "Data" msgstr "" +#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx +msgid "Data backed up successfully!" +msgstr "" + #: popups/pages/Settings/index.tsx msgid "Data correlation" msgstr "" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." -msgstr "" +#~ msgid "Data merged from Mask Cloud Service to local successfully. Re-enter your password to encrypt and upload the new backup to Mask Cloud Service." +#~ msgstr "" #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx #: dashboard/components/Restore/RestoreFromCloud/ConfirmBackupInfo.tsx @@ -951,14 +981,17 @@ msgid "Done" msgstr "完成!" #: content-script/components/InjectedComponents/AutoPasteFailedDialog.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx msgid "Download" msgstr "下載" #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Download backup" -msgstr "" +#~ msgid "Download backup" +#~ msgstr "" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx msgid "Download Backup" msgstr "" @@ -988,11 +1021,14 @@ msgid "eg: X accounts, persona public keys, wallet addresses or ENS" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: dashboard/components/Restore/RestoreFromCloud/EmailField.tsx msgid "Email verification code" msgstr "" @@ -1118,8 +1154,18 @@ msgstr "" msgid "Failed to disconnect wallet" msgstr "" +#. placeholder {0}: (err as Error).message +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Failed to download and merge the backup: {0}" +msgstr "" + #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Failed to download and merge the backup." +#~ msgid "Failed to download and merge the backup." +#~ msgstr "" + +#. placeholder {0}: (err as Error).message +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Failed to login: {0}" msgstr "" #: popups/pages/Wallet/EditNetwork/index.tsx @@ -1279,6 +1325,11 @@ msgstr "" msgid "Give permission to access <0/> your {0}?" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Google Drive" +msgstr "" + #: dashboard/pages/CreateMaskWallet/Onboarding/index.tsx msgid "Got it" msgstr "" @@ -1389,6 +1440,10 @@ msgstr "" msgid "Imported Wallets" msgstr "" +#: dashboard/hooks/useBackupFormState.ts +msgid "Incorrect Backup Password" +msgstr "" + #: popups/modals/VerifyBackupPasswordModal/index.tsx #: popups/modals/ChangeBackupPasswordModal/index.tsx msgid "Incorrect backup password." @@ -1418,6 +1473,7 @@ msgid "Incorrect password" msgstr "" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts @@ -1425,7 +1481,6 @@ msgstr "" #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts #: dashboard/hooks/useBackupFormState.ts -#: dashboard/hooks/useBackupFormState.ts msgid "Incorrect Password" msgstr "" @@ -1474,6 +1529,8 @@ msgid "Invalid Block Explorer URL." msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "Invalid email address format." msgstr "" @@ -1576,6 +1633,10 @@ msgstr "" msgid "Local persona or wallet only" msgstr "" +#: dashboard/pages/SetupPersona/Backup/index.tsx +msgid "Locale Backup" +msgstr "" + #: popups/pages/Wallet/SwitchWallet/index.tsx msgid "Lock Wallet" msgstr "" @@ -1590,6 +1651,11 @@ msgstr "" msgid "Login to Mask Cloud" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "Logout" +msgstr "" + #: popups/pages/Personas/Logout/index.tsx msgid "Logout failed" msgstr "" @@ -1618,9 +1684,14 @@ msgstr "" #: content-script/components/InjectedComponents/ToolboxUnstyled.tsx #: content-script/components/InjectedComponents/PostDialogHint.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/index.tsx msgid "Mask Network" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx +msgid "Mask Network Cloud" +msgstr "" + #: content-script/components/InjectedComponents/PermissionBoundary.tsx msgid "Mask Network requires you to authorize the following websites before using it." msgstr "" @@ -1692,16 +1763,25 @@ msgstr "" msgid "Medium" msgstr "" -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Merge Completed" +msgstr "" + +#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx msgid "Merge data to local database" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx #: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx -msgid "Merge to local" +msgid "Merge to Browser" msgstr "" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#~ msgid "Merge to local" +#~ msgstr "" + #: popups/components/SignRequestInfo/index.tsx msgid "Message" msgstr "" @@ -1716,10 +1796,12 @@ msgid "Mnemonic word" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx msgid "Mobile" msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Mobile number" msgstr "" @@ -1837,6 +1919,7 @@ msgid "No back up" msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "No backups found" msgstr "" @@ -1949,7 +2032,6 @@ msgid "Other social networking platforms, such as <0>Instagram, <1>Facebook, msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -#: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx msgid "Overwrite Backup" msgstr "" @@ -1968,6 +2050,7 @@ msgid "Paste manually" msgstr "手動貼上" #: dashboard/pages/SetupPersona/LocalBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/LocalBackup.tsx #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx #: popups/pages/Wallet/SetPaymentPassword/index.tsx #: popups/modals/WalletRemoveModal/index.tsx @@ -2049,6 +2132,7 @@ msgid "Personas" msgstr "角色" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx msgid "Phone verification code" msgstr "" @@ -2104,7 +2188,6 @@ msgstr "" #: dashboard/pages/SetupPersona/Recovery/index.tsx #: dashboard/pages/SetupPersona/LocalBackup/index.tsx -#: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx msgid "Please select the appropriate method to restore your personal data." msgstr "" @@ -2299,6 +2382,8 @@ msgstr "" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Resend" msgstr "重新傳送" @@ -2413,6 +2498,8 @@ msgstr "" #: content-script/components/InjectedComponents/SetupGuide/VerifyNextID.tsx #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx #: popups/pages/Wallet/components/ActivityList/ActivityItem.tsx #: popups/pages/Wallet/components/ActionGroup/index.tsx #: popups/pages/Wallet/Transfer/index.tsx @@ -2616,6 +2703,7 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/Preview.tsx msgid "Switch other account" msgstr "" @@ -2653,15 +2741,22 @@ msgid "The chainID is not equal to the currently connected one." msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/index.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The code is incorrect." msgstr "" -#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +#: dashboard/pages/SetupPersona/Backup/helpers.tsx msgid "The download link is expired" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/MaskNetworkBackup.tsx +msgid "The Mask Network Cloud Backup feature will be deactivated on April 30, 2025. Please use alternative cloud backup services or local backup solutions." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateMnemonic/index.tsx msgid "The mnemonic word has been copied, please keep it in a safe place." msgstr "" @@ -2700,6 +2795,7 @@ msgstr "" msgid "The persona name already exists." msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/CloudBackupFormContext.ts #: dashboard/contexts/CloudBackupFormContext.tsx msgid "The phone number is incorrect." msgstr "通訊號碼錯誤" @@ -2986,6 +3082,8 @@ msgstr "" #: dashboard/pages/SetupPersona/CloudBackup/PhoneForm.tsx #: dashboard/pages/SetupPersona/CloudBackup/EmailForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/PhoneForm.tsx +#: dashboard/pages/SetupPersona/Backup/CloudBackup/EmailForm.tsx msgid "Verification code sent" msgstr "驗證碼已傳送" @@ -3096,8 +3194,8 @@ msgid "Welcome Back" msgstr "" #: dashboard/pages/SetupPersona/CloudBackupPreview/index.tsx -msgid "Welcome to Mask Cloud Services" -msgstr "" +#~ msgid "Welcome to Mask Cloud Services" +#~ msgstr "" #: popups/pages/Personas/Home/UI.tsx msgid "Welcome to Mask Network" @@ -3107,6 +3205,10 @@ msgstr "" msgid "Welcome to use Mask Network" msgstr "" +#: dashboard/pages/SetupPersona/Backup/CloudBackup/GoogleDriveBackup.tsx +msgid "when you click Add Google Drive button,you will be forwarded to Google authorization pages." +msgstr "" + #: popups/pages/Wallet/ExportPrivateKey/index.tsx msgid "Write down mnemonic words" msgstr "" @@ -3120,8 +3222,8 @@ msgid "You could check the verification result on Mask Pop-up after few minutes. msgstr "" #: dashboard/modals/BackupPreviewModal/BackupPreviewDialog.tsx -msgid "You have backed up your data." -msgstr "" +#~ msgid "You have backed up your data." +#~ msgstr "" #: dashboard/pages/SetupPersona/Onboarding/index.tsx #~ msgid "You have recovered" @@ -3153,6 +3255,10 @@ msgstr "" msgid "Your connection to this site is not encrypted which can be modified by a hostile third party, we strongly suggest you reject this request." msgstr "" +#: dashboard/modals/MergeBackupModal/MergeBackupDialog.tsx +msgid "Your file has been successfully merged into the browser data." +msgstr "" + #: dashboard/pages/CreateMaskWallet/CreateWalletForm/index.tsx msgid "Your payment password encrypts wallet data and is needed to unlocking the wallet, transaction confirmations and signing. The password is never stored, and there is no way to recover it if you forget it." msgstr "" diff --git a/packages/polyfills/types/env.d.ts b/packages/polyfills/types/env.d.ts index 6c3d331fdbf0..154eabfc661c 100644 --- a/packages/polyfills/types/env.d.ts +++ b/packages/polyfills/types/env.d.ts @@ -4,6 +4,7 @@ declare module NodeJS { readonly MASK_SENTRY_DSN: string readonly MASK_SENTRY: 'enabled' | 'disabled' readonly MASK_MIXPANEL: 'enabled' | 'disabled' + readonly GOOGLE_CLIENT_ID: string /** * Run skip tests like * RUN_SKIP_TESTS=1 pnpm test diff --git a/packages/shared-base/src/KVStorage/index.ts b/packages/shared-base/src/KVStorage/index.ts index 3bbd3ef675e2..fe9727a0217f 100644 --- a/packages/shared-base/src/KVStorage/index.ts +++ b/packages/shared-base/src/KVStorage/index.ts @@ -34,6 +34,8 @@ export interface BackupConfig { localBackupAt: string | null cloudBackupAt: string | null cloudBackupMethod: BackupAccountType | null + googleToken: string | null + googleAccount: string | null } export const PersistentStorages = { @@ -53,6 +55,8 @@ export const PersistentStorages = { localBackupAt: '', cloudBackupAt: '', cloudBackupMethod: null, + googleToken: '', + googleAccount: '', }, }), /** diff --git a/packages/shared-base/src/helpers/relativeRouteOf.ts b/packages/shared-base/src/helpers/relativeRouteOf.ts index 9a63dc981db2..2b7d9593b980 100644 --- a/packages/shared-base/src/helpers/relativeRouteOf.ts +++ b/packages/shared-base/src/helpers/relativeRouteOf.ts @@ -8,3 +8,7 @@ export function relativeRouteOf(parent: PopupRoutes | DashboardRoutes) { return child.slice(parent.length).replace(/^\//, '') } } +export function relativeRoute(parent: string, child: string) { + if (!child.startsWith(parent)) throw new Error() + return child.slice(parent.length).replace(/^\//, '') +} diff --git a/packages/shared-base/src/types/Routes.ts b/packages/shared-base/src/types/Routes.ts index bfb4b23743ff..baabdb6839b7 100644 --- a/packages/shared-base/src/types/Routes.ts +++ b/packages/shared-base/src/types/Routes.ts @@ -7,6 +7,13 @@ export enum DashboardRoutes { RecoveryPersona = '/setup/recovery', SignUpPersonaMnemonic = '/setup/sign-up/mnemonic', SignUpPersonaOnboarding = '/setup/sign-up/onboarding', + Backup = '/setup/backup', + BackupLocal = '/setup/backup/local', + BackupCloud = '/setup/backup/cloud', + BackupCloudMaskNetwork = '/setup/backup/cloud/mask-network', + BackupCloudGoogleDrive = '/setup/backup/cloud/google-drive', + BackupPreview = '/setup/backup/cloud/preview', + LocalBackup = '/setup/local-backup', CloudBackup = '/setup/cloud-backup', CloudBackupPreview = '/setup/cloud-backup-preview', diff --git a/packages/shared/src/UI/components/Alert/index.tsx b/packages/shared/src/UI/components/Alert/index.tsx index 322c52a9753e..3a3dcb948528 100644 --- a/packages/shared/src/UI/components/Alert/index.tsx +++ b/packages/shared/src/UI/components/Alert/index.tsx @@ -1,10 +1,11 @@ import { Icons } from '@masknet/icons' import { makeStyles } from '@masknet/theme' -import { Box, Typography } from '@mui/material' +import { Box, Typography, type AlertProps } from '@mui/material' import type { BoxProps } from '@mui/system' import { memo } from 'react' -const useStyles = makeStyles()((theme) => ({ +const useStyles = makeStyles()((theme, _, refs) => ({ + warning: {}, alert: { display: 'flex', borderRadius: 4, @@ -15,21 +16,31 @@ const useStyles = makeStyles()((theme) => ({ color: theme.palette.maskColor.main, backdropFilter: 'blur(5px)', gap: 10, + [`&.${refs.warning}`]: { + backgroundColor: 'rgba(255, 177, 0, 0.1)', + color: '#FFB100', + }, }, })) -interface Props extends BoxProps { +interface Props extends BoxProps, Pick { open?: boolean onClose?: () => void } -export const Alert = memo(function Alert({ className, children, open, onClose, ...rest }: Props) { +export const Alert = memo(function Alert({ className, children, open, onClose, severity, ...rest }: Props) { const { classes, cx } = useStyles() if (!open) return null return ( - - + + {severity === 'warning' ? + + : } {children} diff --git a/packages/shared/src/UI/components/CountryCodePicker/index.tsx b/packages/shared/src/UI/components/CountryCodePicker/index.tsx index 64b4b8d95f32..9669db07d278 100644 --- a/packages/shared/src/UI/components/CountryCodePicker/index.tsx +++ b/packages/shared/src/UI/components/CountryCodePicker/index.tsx @@ -1,18 +1,18 @@ import { List, ListItemButton, ListItemIcon, ListItemText, Popover, TextField, Typography } from '@mui/material' import { memo, useDeferredValue, useMemo, useState } from 'react' +import { Trans, useLingui } from '@lingui/react/macro' import { Icons } from '@masknet/icons' +import { COUNTRIES, useRenderPhraseCallbackOnDepsChange } from '@masknet/shared-base-ui' import { makeStyles } from '@masknet/theme' import Fuse from 'fuse.js' import { EmptyStatus } from '../../../index.js' -import { COUNTRIES, useRenderPhraseCallbackOnDepsChange } from '@masknet/shared-base-ui' import { getCountryFlag } from '../../../utils/getCountryFlag.js' -import { Trans, useLingui } from '@lingui/react/macro' const useStyles = makeStyles()((theme) => ({ paper: { padding: theme.spacing(2), - borderRadius: 8, + borderRadius: 24, width: 320, height: 316, background: theme.palette.maskColor.bottom, @@ -98,10 +98,10 @@ export const CountryCodePicker = memo(({ open, anchorEl, onClose() }} classes={{ paper: classes.paper }} - anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }} transformOrigin={{ vertical: 'top', - horizontal: 'center', + horizontal: 'left', }}> ({ + button: { + padding: 0, + }, +})) export interface PhoneNumberFieldProps extends Omit { code: string @@ -12,6 +19,7 @@ export interface PhoneNumberFieldProps extends Omit(null) @@ -32,7 +40,10 @@ export function PhoneNumberField({ code, onCodeChange, ...rest }: PhoneNumberFie ...rest.InputProps, disableUnderline: true, startAdornment: ( -