Skip to content

Commit

Permalink
feat(wallet): rebrand export views (#2190)
Browse files Browse the repository at this point in the history
* feat: rebrand export views

* feat: remove unnecessary state
  • Loading branch information
evavirseda authored Sep 3, 2024
1 parent 3206010 commit 2f45616
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 148 deletions.
112 changes: 27 additions & 85 deletions apps/wallet/src/ui/app/components/HideShowDisplayBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,106 +2,48 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Copy16, EyeClose16, EyeOpen16 } from '@iota/icons';
import { cx } from 'class-variance-authority';
import { useEffect, useState } from 'react';

import { useCopyToClipboard } from '../hooks/useCopyToClipboard';
import { Link } from '../shared/Link';
import { Text } from '../shared/text';

const AUTO_HIDE_INTERVAL = 3 * 60 * 1000;
import { Button, ButtonType, TextArea } from '@iota/apps-ui-kit';
import toast from 'react-hot-toast';

export interface HideShowDisplayBoxProps {
value: string | string[];
hideCopy?: boolean;
copiedMessage?: string;
isContentVisible?: boolean;
}

export function HideShowDisplayBox({
value,
hideCopy = false,
copiedMessage,
isContentVisible = false,
}: HideShowDisplayBoxProps) {
const [valueHidden, setValueHidden] = useState(true);
const copyCallback = useCopyToClipboard(
hideCopy ? '' : typeof value === 'string' ? value : value.join(' '),
{
copySuccessMessage: copiedMessage,
},
);
useEffect(() => {
const updateOnVisibilityChange = () => {
if (document.visibilityState === 'hidden') {
setValueHidden(true);
}
};
document.addEventListener('visibilitychange', updateOnVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', updateOnVisibilityChange);
};
}, []);
useEffect(() => {
let timeout: number;
if (!valueHidden) {
timeout = window.setTimeout(() => {
setValueHidden(true);
}, AUTO_HIDE_INTERVAL);
async function handleCopy() {
if (!value) {
return;
}
const textToCopy = Array.isArray(value) ? value.join(' ') : value;
try {
await navigator.clipboard.writeText(textToCopy);
toast.success(copiedMessage || 'Copied');
} catch {
toast.error('Failed to copy');
}
return () => {
if (timeout) {
clearTimeout(timeout);
}
};
}, [valueHidden]);
}

return (
<div className="border-gray-60 flex flex-col flex-nowrap items-stretch gap-2 overflow-hidden rounded-lg border border-solid bg-white px-5 py-4">
<div className="relative break-all">
{valueHidden ? null : (
<Text variant="pBody" weight="medium" color="steel-darker">
{typeof value === 'string'
? value
: value.map((aValue, index) => (
<span key={index}>{(index > 0 ? ' ' : '') + aValue}</span>
))}
</Text>
)}
<div className={cx('flex flex-col gap-1.5', valueHidden ? '' : 'hidden')}>
<div className="bg-gray-40 h-3.5 rounded-md" />
<div className="bg-gray-40 h-3.5 rounded-md" />
<div className="bg-gray-40 h-3.5 rounded-md" />
<div className="bg-gray-40 h-4.5 w-1/2 rounded-md" />
</div>
</div>
<div className="flex flex-row flex-nowrap items-center justify-between">
<div>
{!hideCopy ? (
<Link
color="heroDark"
weight="medium"
size="body"
text="Copy"
before={<Copy16 className="text-base leading-none" />}
onClick={copyCallback}
/>
) : null}
</div>
<div>
<Link
color="steelDark"
size="base"
weight="medium"
text={
valueHidden ? (
<EyeClose16 className="block" />
) : (
<EyeOpen16 className="block" />
)
}
onClick={() => setValueHidden((v) => !v)}
/>
<div className="flex flex-col gap-md">
<TextArea
value={value}
isVisibilityToggleEnabled
isContentVisible={isContentVisible}
rows={5}
/>
{!hideCopy && (
<div className="flex justify-end">
<Button onClick={handleCopy} type={ButtonType.Secondary} text="Copy" />
</div>
</div>
)}
</div>
);
}
40 changes: 5 additions & 35 deletions apps/wallet/src/ui/app/pages/accounts/BackupMnemonicPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,16 @@ import {
InfoBox,
InfoBoxStyle,
InfoBoxType,
TextArea,
} from '@iota/apps-ui-kit';
import { Exclamation, Info } from '@iota/ui-icons';
import { Loading, PageTemplate } from '_components';
import { HideShowDisplayBox, Loading, PageTemplate } from '_components';
import { AccountSourceType } from '_src/background/account-sources/AccountSource';
import { useEffect, useMemo, useState } from 'react';
import toast from 'react-hot-toast';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import { useAccountSources } from '../../hooks/useAccountSources';
import { useExportPassphraseMutation } from '../../hooks/useExportPassphraseMutation';

export function BackupMnemonicPage() {
const [mnemonicCopied, setMnemonicCopied] = useState(false);
const [mnemonicBackedUp, setMnemonicBackedUp] = useState(false);

const { accountSourceID } = useParams();
Expand All @@ -46,21 +43,6 @@ export function BackupMnemonicPage() {
})();
}, [accountSourceID, passphraseMutation]);

async function handleCopy() {
if (!passphraseMutation?.data) {
return;
}
try {
await navigator.clipboard.writeText(passphraseMutation.data.join(' '));
setMnemonicCopied(true);
setTimeout(() => {
setMnemonicCopied(false);
}, 1000);
} catch {
toast.error('Failed to copy');
}
}

return (
<PageTemplate title="Export Mnemonic" isTitleCentered>
<Loading loading={isPending}>
Expand All @@ -81,13 +63,10 @@ export function BackupMnemonicPage() {
<div className="flex flex-grow flex-col flex-nowrap">
<Loading loading={passphraseMutation.isPending}>
{passphraseMutation.data ? (
<>
<TextArea
value={passphraseMutation.data.join(' ')}
isVisibilityToggleEnabled
rows={5}
/>
</>
<HideShowDisplayBox
value={passphraseMutation.data.join(' ')}
copiedMessage="Mnemonic copied"
/>
) : (
<InfoBox
type={InfoBoxType.Default}
Expand All @@ -101,15 +80,6 @@ export function BackupMnemonicPage() {
)}
</Loading>
</div>
{passphraseMutation.data && (
<div className="flex justify-end">
<Button
onClick={handleCopy}
type={ButtonType.Secondary}
text={mnemonicCopied ? 'Copied' : 'Copy'}
/>
</div>
)}
</div>
<div className="flex w-full flex-col">
<div className="flex w-full py-sm--rs">
Expand Down
21 changes: 13 additions & 8 deletions apps/wallet/src/ui/app/pages/accounts/ExportAccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import { useBackgroundClient } from '_src/ui/app/hooks/useBackgroundClient';
import { useMutation } from '@tanstack/react-query';
import { Navigate, useNavigate, useParams } from 'react-router-dom';

import { VerifyPasswordModal, Alert, HideShowDisplayBox, Loading, Overlay } from '_components';
import { VerifyPasswordModal, HideShowDisplayBox, Loading, Overlay } from '_components';
import { useAccounts } from '../../hooks/useAccounts';
import { InfoBox, InfoBoxStyle, InfoBoxType } from '@iota/apps-ui-kit';
import { Info } from '@iota/ui-icons';

export function ExportAccountPage() {
const { accountID } = useParams();
Expand All @@ -33,17 +35,20 @@ export function ExportAccountPage() {
return <Navigate to="/accounts/manage" replace />;
}
return (
<Overlay title="Account Private Key" closeOverlay={() => navigate(-1)} showModal>
<Overlay title="Export Private Key" closeOverlay={() => navigate(-1)} showModal>
<Loading loading={isPending}>
{exportMutation.data ? (
<div className="flex flex-col flex-nowrap items-stretch gap-3">
<Alert>
<div>Do not share your Private Key!</div>
<div>It provides full control of your account.</div>
</Alert>
<div className="flex flex-col gap-md">
<InfoBox
icon={<Info />}
type={InfoBoxType.Default}
title="Do not share your private key"
supportingText="Your account derived from it can be controlled fully."
style={InfoBoxStyle.Default}
/>
<HideShowDisplayBox
value={exportMutation.data}
copiedMessage="Private key copied"
copiedMessage="Mnemonic copied"
/>
</div>
) : (
Expand Down
24 changes: 13 additions & 11 deletions apps/wallet/src/ui/app/pages/accounts/ExportPassphrasePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Alert, HideShowDisplayBox, VerifyPasswordModal, Loading, Overlay } from '_components';
import { HideShowDisplayBox, VerifyPasswordModal, Loading, Overlay } from '_components';
import { Navigate, useNavigate, useParams } from 'react-router-dom';

import { useAccountSources } from '../../hooks/useAccountSources';
import { useExportPassphraseMutation } from '../../hooks/useExportPassphraseMutation';
import { AccountSourceType } from '_src/background/account-sources/AccountSource';
import { InfoBox, InfoBoxType, InfoBoxStyle } from '@iota/apps-ui-kit';
import { Info } from '@iota/ui-icons';

export function ExportPassphrasePage() {
const { accountSourceID } = useParams();
Expand All @@ -22,16 +23,17 @@ export function ExportPassphrasePage() {
<Overlay title="Export Passphrase" closeOverlay={() => navigate(-1)} showModal>
<Loading loading={isPending}>
{exportMutation.data ? (
<div className="flex min-w-0 flex-col gap-3">
<Alert>
<div className="break-normal">Do not share your Passphrase!</div>
<div className="break-normal">
It provides full control of all accounts derived from it.
</div>
</Alert>
<div className="flex flex-col gap-md">
<InfoBox
icon={<Info />}
type={InfoBoxType.Default}
title="Do not share your mnemonic"
supportingText="All accounts derived from it can be controlled fully."
style={InfoBoxStyle.Default}
/>
<HideShowDisplayBox
value={exportMutation.data}
copiedMessage="Passphrase copied"
value={exportMutation.data.join(' ')}
copiedMessage="Mnemonic copied"
/>
</div>
) : (
Expand Down
20 changes: 11 additions & 9 deletions apps/wallet/src/ui/app/pages/accounts/ExportSeedPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Alert, HideShowDisplayBox, VerifyPasswordModal, Loading, Overlay } from '_components';
import { HideShowDisplayBox, VerifyPasswordModal, Loading, Overlay } from '_components';
import { Navigate, useNavigate, useParams } from 'react-router-dom';

import { useAccountSources } from '../../hooks/useAccountSources';
import { useExportSeedMutation } from '../../hooks/useExportSeedMutation';
import { AccountSourceType } from '_src/background/account-sources/AccountSource';
import { InfoBox, InfoBoxType, InfoBoxStyle } from '@iota/apps-ui-kit';
import { Info } from '@iota/ui-icons';

export function ExportSeedPage() {
const { accountSourceID } = useParams();
Expand All @@ -24,13 +25,14 @@ export function ExportSeedPage() {
<Overlay title="Export Seed" closeOverlay={() => navigate(-1)} showModal>
<Loading loading={isPending}>
{exportMutation.data ? (
<div className="flex min-w-0 flex-col gap-3">
<Alert>
<div className="break-normal">Do not share your Seed!</div>
<div className="break-normal">
It provides full control of all accounts derived from it.
</div>
</Alert>
<div className="flex flex-col gap-md">
<InfoBox
icon={<Info />}
type={InfoBoxType.Default}
title="Do not share your seed"
supportingText="All accounts derived from it can be controlled fully."
style={InfoBoxStyle.Default}
/>
<HideShowDisplayBox
value={exportMutation.data}
copiedMessage="Seed copied"
Expand Down

0 comments on commit 2f45616

Please sign in to comment.