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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/names/tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export async function createWallet(page: Page) {
await page.getByText('Mnemonic', { exact: true }).click();
await page.getByTestId('password.input').fill('iotae2etests');
await page.getByTestId('password.confirmation').fill('iotae2etests');
await page.getByText('I read and agree').click();

await page.getByRole('button', { name: /Create Wallet/ }).click();
await page.waitForURL(new RegExp(/accounts\/backup/));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { ToS_LINK, useZodForm } from '@iota/core';
import { useZodForm } from '@iota/core';
import { useEffect } from 'react';
import { type SubmitHandler } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { z } from 'zod';
import zxcvbn from 'zxcvbn';
import { parseAutoLock, useAutoLockMinutes } from '_hooks';
import { CheckboxField } from '../../shared/forms/CheckboxField';
import { Form } from '../../shared/forms/Form';
import { AutoLockSelector, zodSchema } from './AutoLockSelector';
import { Button, ButtonHtmlType, ButtonType, Input, InputType } from '@iota/apps-ui-kit';
import { ExternalLink } from '_components';

function addDot(str: string | undefined) {
if (str && !str.endsWith('.')) {
Expand Down Expand Up @@ -49,9 +47,6 @@ const formSchema = z
path: ['confirmation'],
message: "Passwords don't match",
}),
acceptedTos: z.literal<boolean>(true, {
errorMap: () => ({ message: 'Please accept Terms of Service to continue' }),
}),
})
.merge(zodSchema);

Expand All @@ -61,22 +56,19 @@ interface ProtectAccountFormProps {
submitButtonText: string;
cancelButtonText?: string;
onSubmit: SubmitHandler<ProtectAccountFormValues>;
hideToS?: boolean;
}

export function ProtectAccountForm({
submitButtonText,
cancelButtonText,
onSubmit,
hideToS,
}: ProtectAccountFormProps) {
const autoLock = useAutoLockMinutes();
const form = useZodForm({
mode: 'all',
schema: formSchema,
values: {
password: { input: '', confirmation: '' },
acceptedTos: !!hideToS,
autoLock: parseAutoLock(autoLock.data || null),
},
});
Expand Down Expand Up @@ -129,26 +121,6 @@ export function ProtectAccountForm({
<AutoLockSelector />
</div>
<div className="flex flex-col gap-4 pt-xxxs">
{hideToS ? null : (
<CheckboxField
name="acceptedTos"
label={
<div className="flex items-center gap-x-0.5 whitespace-nowrap">
<span className="text-label-lg text-iota-neutral-40 dark:text-iota-neutral-60">
I read and agreed to the
</span>
<ExternalLink
href={ToS_LINK}
className="text-label-lg text-iota-primary-30 dark:text-iota-primary-80"
type="legal"
>
Terms of Services
</ExternalLink>
</div>
}
/>
)}

<div className="flex flex-row justify-stretch gap-2.5">
{cancelButtonText ? (
<Button
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Button, ButtonHtmlType, ButtonType, Checkbox } from '@iota/apps-ui-kit';
import { Overlay } from '_components';
import { useMetricsEnabled } from '_src/ui/app/hooks/useMetricsEnabled';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';

export function MetricsSettings() {
const [hasAcceptedMetrics, setHasAcceptedMetrics] = useMetricsEnabled();
const [isToggled, setIsToggled] = useState(hasAcceptedMetrics);
const navigate = useNavigate();

function onSave() {
setHasAcceptedMetrics(isToggled);
navigate('/tokens');
}

return (
<Overlay showModal title="Metrics" closeOverlay={() => navigate('/tokens')} showBackButton>
<div className="flex flex-1 flex-col">
<div className="flex flex-1 flex-col gap-6 p-md [&_label]:cursor-pointer [&_span]:shrink-0">
<Checkbox
name="metrics.enabled"
label="Participate in metrics to help us make the IOTA Wallet better"
onCheckedChange={(e) => setIsToggled(e.target.checked)}
isChecked={isToggled}
/>
</div>
<div className="flex flex-col gap-4 pt-xxxs">
<div className="flex flex-row justify-stretch gap-2.5">
<Button
type={ButtonType.Primary}
disabled={isToggled === hasAcceptedMetrics}
text="Save"
fullWidth
htmlType={ButtonHtmlType.Submit}
onClick={onSave}
/>
</div>
</div>
</div>
</Overlay>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Logout,
Expand,
Discord,
DataStack,
SidePanel as SidePanelIcon,
} from '@iota/apps-ui-icons';
import {
Expand All @@ -35,6 +36,7 @@ import { ampli } from '_src/shared/analytics/ampli';
import { useTheme, getCustomNetwork, FAQ_LINK, ToS_LINK, DISCORD_SUPPORT_LINK } from '@iota/core';
import { useSidePanel } from '_src/ui/app/hooks/useSidePanel';
import { useSidePanelMutation } from '_src/ui/app/hooks/useSidePanelMutation';
import { useMetricsEnabled } from '_src/ui/app/hooks/useMetricsEnabled';
import { SidePanel } from '_src/polyfills/sidepanel';
import { ExtensionViewType } from '_src/ui/app/redux/slices/app/appType';
import { openInNewTab } from '_src/shared/utils';
Expand All @@ -45,13 +47,15 @@ export function MenuList() {
const networkUrl = useNextMenuUrl(true, '/network');
const autoLockUrl = useNextMenuUrl(true, '/auto-lock');
const themeUrl = useNextMenuUrl(true, '/theme');
const metricsUrl = useNextMenuUrl(true, '/metrics');
const network = useAppSelector((state) => state.app.network);
const networkConfig = network === Network.Custom ? getCustomNetwork() : getNetwork(network);
const version = Browser.runtime.getManifest().version;
const autoLockInterval = useAutoLockMinutes();
const sidePanel = useSidePanel();
const sidePanelMutation = useSidePanelMutation();
const extensionType = useAppSelector((state) => state.app.extensionViewType);
const [hasAcceptedMetrics] = useMetricsEnabled();

// Logout
const [isLogoutDialogOpen, setIsLogoutDialogOpen] = useState(false);
Expand Down Expand Up @@ -79,6 +83,11 @@ export function MenuList() {
function onThemeClick() {
navigate(themeUrl);
}

function onMetricsClick() {
navigate(metricsUrl);
}

async function onSidePanelClick(
_isToggled: boolean,
event: React.ChangeEvent<HTMLInputElement>,
Expand Down Expand Up @@ -120,6 +129,7 @@ export function MenuList() {

const autoLockSubtitle = handleAutoLockSubtitle();
const themeSubtitle = themePreference.charAt(0).toUpperCase() + themePreference.slice(1);
const metricsSubtitle = `Metrics ${hasAcceptedMetrics ? 'enabled' : 'disabled'}`;
const MENU_ITEMS = [
{
title: 'Network',
Expand All @@ -139,6 +149,12 @@ export function MenuList() {
subtitle: themeSubtitle,
onClick: onThemeClick,
},
{
title: 'Metrics',
icon: <DataStack />,
subtitle: metricsSubtitle,
onClick: onMetricsClick,
},
{
title: 'Get Support',
icon: <Discord />,
Expand Down
2 changes: 2 additions & 0 deletions apps/wallet/src/ui/app/components/menu/content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { AutoLockAccounts } from './AutoLockAccounts';
import { NetworkSettings } from './NetworkSettings';
import { MenuList } from './WalletSettingsMenuList';
import { ThemeSettings } from './ThemeSettings';
import { MetricsSettings } from './MetricsSettings';

const CLOSE_KEY_CODES: string[] = ['Escape'];

Expand Down Expand Up @@ -51,6 +52,7 @@ export function MenuContent() {
<Route path="/network" element={<NetworkSettings />} />
<Route path="/auto-lock" element={<AutoLockAccounts />} />
<Route path="/theme" element={<ThemeSettings />} />
<Route path="/metrics" element={<MetricsSettings />} />
<Route path="*" element={<Navigate to={menuHomeUrl} replace={true} />} />
</Routes>
</MainLocationContext.Provider>
Expand Down
21 changes: 21 additions & 0 deletions apps/wallet/src/ui/app/hooks/useMetricsEnabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2026 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useState } from 'react';

export const METRICS_STORAGE_KEY = 'metrics_enabled_iota-wallet';
const DEFAULT_METRICS_ENABLED = true;

export function useMetricsEnabled(): [boolean, (enabled: boolean) => void] {
const [isEnabled, setIsEnabled] = useState<boolean>(() => {
const storedValue = localStorage.getItem(METRICS_STORAGE_KEY);
return storedValue ? JSON.parse(storedValue) : DEFAULT_METRICS_ENABLED;
});

const setMetricsEnabled = (enabled: boolean) => {
localStorage.setItem(METRICS_STORAGE_KEY, JSON.stringify(enabled));
setIsEnabled(enabled);
};

return [isEnabled, setMetricsEnabled];
}
63 changes: 40 additions & 23 deletions apps/wallet/src/ui/app/pages/accounts/WelcomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Button, ButtonType } from '@iota/apps-ui-kit';
import { IotaLogoWeb } from '@iota/apps-ui-icons';
import GetStartedImage from '_assets/images/onboarding/get-started.png';
import GetStartedImageDark from '_assets/images/onboarding/get-started-darkmode.png';
import { useTheme, Theme } from '@iota/core';
import { useTheme, Theme, ToS_LINK } from '@iota/core';
import { AmpliSourceFlow } from '_src/shared/analytics';

export function WelcomePage() {
Expand All @@ -22,34 +22,51 @@ export function WelcomePage() {

return (
<Loading loading={isInitializedLoading || isFullscreenGuardLoading}>
<div className="flex h-full w-full flex-col items-center justify-between bg-iota-neutral-100 px-md py-2xl shadow-wallet-content dark:bg-iota-neutral-6">
<div className="flex h-full w-full flex-col items-center justify-between bg-iota-neutral-100 px-md py-lg shadow-wallet-content dark:bg-iota-neutral-6">
<IotaLogoWeb
width={130}
height={32}
className="text-iota-neutral-10 dark:text-iota-neutral-92"
/>
<img
src={theme === Theme.Dark ? GetStartedImageDark : GetStartedImage}
alt="Get Started"
height={246}
width="auto"
className="aspect-[4/3] h-[246px] w-auto object-cover"
/>
<div className="flex flex-col items-center gap-8 text-center">
<img
src={theme === Theme.Dark ? GetStartedImageDark : GetStartedImage}
alt="Get Started"
height={246}
width="auto"
className="aspect-[4/3] h-[246px] w-auto object-cover"
/>
<h1 className="font-alliance-no2 text-[28px] font-medium leading-[120%] text-iota-neutral-10 dark:text-iota-neutral-92">
Your Gateway to the IOTA Ecosystem
</h1>
<Button
type={ButtonType.Primary}
text="Get Started"
onClick={() => {
setSourceFlow(AmpliSourceFlow.Onboarding);
navigate('/accounts/add-account');
}}
/>
</div>
<div className="text-label-lg text-iota-neutral-60 dark:text-iota-neutral-40">
&copy; IOTA Foundation {CURRENT_YEAR}
<div className="flex flex-col gap-y-md">
<h1 className="font-alliance-no2 text-[28px] font-medium leading-[120%] text-iota-neutral-10 dark:text-iota-neutral-92">
Your Gateway to the IOTA Ecosystem
</h1>
<Button
type={ButtonType.Primary}
text="Get Started"
fullWidth
onClick={() => {
setSourceFlow(AmpliSourceFlow.Onboarding);
navigate('/accounts/add-account');
}}
/>
<div className="flex flex-col gap-y-lg">
<div className="px-sm text-center text-label-lg text-iota-neutral-40 dark:text-iota-neutral-60">
By continuing, I agree to IOTA's{' '}
<a
href={ToS_LINK}
target="_blank"
rel="noopener noreferrer"
className="text-label-lg text-iota-primary-30 dark:text-iota-primary-80"
>
Terms of Use
</a>{' '}
and use of Cookies and Analytics.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing link

</div>
<div className="text-label-md text-iota-neutral-60 dark:text-iota-neutral-40">
&copy; IOTA Foundation {CURRENT_YEAR}
</div>
</div>
</div>
</div>
</div>
</Loading>
Expand Down
8 changes: 0 additions & 8 deletions apps/wallet/tests/utils/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export async function createWallet(page: Page, extensionUrl: string) {
await page.getByText('Mnemonic', { exact: true }).click();
await page.getByTestId('password.input').fill(TESTS_PASSWORD);
await page.getByTestId('password.confirmation').fill(TESTS_PASSWORD);
await page.getByText('I read and agree').click();
await page.getByRole('button', { name: /Create Wallet/ }).click();
await page.getByText('I saved my mnemonic').click();
await page.getByRole('button', { name: /Open Wallet/ }).click();
Expand Down Expand Up @@ -43,7 +42,6 @@ export async function importWallet(page: Page, extensionUrl: string, mnemonic: s
await page.getByText('Add profile').click();
await page.getByTestId('password.input').fill(TESTS_PASSWORD);
await page.getByTestId('password.confirmation').fill(TESTS_PASSWORD);
await page.getByText('I read and agree').click();
await page.getByRole('button', { name: /Create Wallet/ }).click();

await page.waitForURL(new RegExp(/^(?!.*protect-account).*$/));
Expand Down Expand Up @@ -106,9 +104,6 @@ export async function createPasskeyWallet(

await page.getByTestId('password.input').fill(TESTS_PASSWORD);
await page.getByTestId('password.confirmation').fill(TESTS_PASSWORD);

await page.getByText('I read and agree').click();

await page.getByRole('button', { name: /Create Wallet/ }).click();

await expect(page.getByText(username)).toBeVisible({ timeout: LONG_TIMEOUT });
Expand All @@ -128,8 +123,5 @@ export async function restorePasskeyAccount(page: Page) {

await page.getByTestId('password.input').fill(TESTS_PASSWORD);
await page.getByTestId('password.confirmation').fill(TESTS_PASSWORD);

await page.getByText('I read and agree').click();

await page.getByRole('button', { name: /Create Wallet/ }).click();
}
Loading