Skip to content

Commit

Permalink
feat(wallet): add darkmode/lightmode theme selection logic (#4053)
Browse files Browse the repository at this point in the history
* feat: move themecontext to core

* feat: fix styles after using shared context

* feat: add theme to wallet

* feat: clenaup

* feat: add device preferences theme

* fix cleanup

* feat: add darmode video welcome page
  • Loading branch information
evavirseda authored Nov 15, 2024
1 parent 75e54d0 commit a6277f7
Show file tree
Hide file tree
Showing 19 changed files with 145 additions and 76 deletions.
3 changes: 2 additions & 1 deletion apps/core/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './KioskClientProvider';
export * from './QR';

export * from './providers';
44 changes: 44 additions & 0 deletions apps/core/src/components/providers/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { PropsWithChildren, useState, useEffect } from 'react';
import { Theme } from '../../enums';
import { ThemeContext } from '../../contexts';

interface ThemeProviderProps {
appId: string;
}

export function ThemeProvider({ children, appId }: PropsWithChildren<ThemeProviderProps>) {
const storageKey = `theme_${appId}`;
const [theme, setTheme] = useState<Theme>(() => {
if (typeof window !== 'undefined') {
const storedTheme = localStorage.getItem(storageKey);
if (storedTheme) {
return storedTheme as Theme;
} else {
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
return prefersDarkScheme ? Theme.Dark : Theme.Light;
}
}
return Theme.Light;
});

useEffect(() => {
if (typeof window !== 'undefined') {
localStorage.setItem(storageKey, theme);
}
document.documentElement.classList.toggle(Theme.Dark, theme === Theme.Dark);
}, [theme, storageKey]);

return (
<ThemeContext.Provider
value={{
theme,
setTheme,
}}
>
{children}
</ThemeContext.Provider>
);
}
5 changes: 5 additions & 0 deletions apps/core/src/components/providers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './KioskClientProvider';
export * from './ThemeProvider';
15 changes: 15 additions & 0 deletions apps/core/src/contexts/ThemeContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { createContext } from 'react';
import { Theme } from '../enums';

export interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
}

export const ThemeContext = createContext<ThemeContextType>({
theme: Theme.Light,
setTheme: () => {},
});
4 changes: 4 additions & 0 deletions apps/core/src/contexts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './ThemeContext';
4 changes: 4 additions & 0 deletions apps/core/src/enums/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './theme.enums';
7 changes: 7 additions & 0 deletions apps/core/src/enums/theme.enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export enum Theme {
Light = 'light',
Dark = 'dark',
}
1 change: 1 addition & 0 deletions apps/core/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ export * from './useGetAllOwnedObjects';
export * from './useGetTimelockedStakedObjects';
export * from './useGetActiveValidatorsInfo';
export * from './useCursorPagination';
export * from './useTheme';

export * from './stake';
12 changes: 12 additions & 0 deletions apps/core/src/hooks/useTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
import { useContext } from 'react';
import { ThemeContext, ThemeContextType } from '../contexts';

export const useTheme = (): ThemeContextType => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
2 changes: 2 additions & 0 deletions apps/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export * from './components';
export * from './utils';
export * from './hooks';
export * from './constants';
export * from './contexts';
export * from './enums';
9 changes: 7 additions & 2 deletions apps/wallet-dashboard/app/(protected)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ import { Button } from '@iota/apps-ui-kit';
import { redirect } from 'next/navigation';
import { Sidebar } from './components';
import { TopNav } from './components/top-nav/TopNav';
import { useTheme } from '@/contexts';
import { Theme, useTheme } from '@iota/core';

function DashboardLayout({ children }: PropsWithChildren): JSX.Element {
const { connectionStatus } = useCurrentWallet();
const { theme, toggleTheme } = useTheme();
const { theme, setTheme } = useTheme();

const toggleTheme = () => {
const newTheme = theme === Theme.Light ? Theme.Dark : Theme.Light;
setTheme(newTheme);
};
const account = useCurrentAccount();
useEffect(() => {
if (connectionStatus !== 'connected' && !account) {
Expand Down
21 changes: 13 additions & 8 deletions apps/wallet-dashboard/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ import { useEffect } from 'react';
import { redirect } from 'next/navigation';
import { IotaLogoWeb } from '@iota/ui-icons';
import { HOMEPAGE_ROUTE } from '@/lib/constants/routes.constants';
import { Theme, useTheme } from '@iota/core';

function HomeDashboardPage(): JSX.Element {
const { theme } = useTheme();
const { connectionStatus } = useCurrentWallet();
const account = useCurrentAccount();

const CURRENT_YEAR = new Date().getFullYear();
const videoSrc =
theme === Theme.Dark
? 'https://files.iota.org/media/tooling/wallet-dashboard-welcome-dark.mp4'
: 'https://files.iota.org/media/tooling/wallet-dashboard-welcome-light.mp4';

useEffect(() => {
if (connectionStatus === 'connected' && account) {
Expand All @@ -23,31 +29,30 @@ function HomeDashboardPage(): JSX.Element {

return (
<main className="flex h-screen">
<div className="hidden sm:flex md:w-1/4">
<div className="relative hidden sm:flex md:w-1/3">
<video
autoPlay
muted
loop
className="h-full w-full object-cover"
className="absolute right-0 top-0 h-full w-full min-w-fit object-cover"
disableRemotePlayback
>
<source
src="https://files.iota.org/media/tooling/wallet-dashboard-welcome.mp4"
type="video/mp4"
/>
<source src={videoSrc} type="video/mp4" />
</video>
</div>
<div className="flex h-full w-full flex-col items-center justify-between p-md sm:p-2xl">
<IotaLogoWeb width={130} height={32} />
<div className="flex max-w-sm flex-col items-center gap-8 text-center">
<div className="flex flex-col items-center gap-4">
<span className="text-headline-sm text-neutral-40">Welcome to</span>
<h1 className="text-display-lg text-neutral-10">IOTA Wallet</h1>
<h1 className="text-display-lg text-neutral-10 dark:text-neutral-100">
IOTA Wallet
</h1>
<span className="text-title-lg text-neutral-40">
Connecting you to the decentralized web and IOTA network
</span>
</div>
<div className="[&_button]:!bg-neutral-90">
<div className="[&_button]:!bg-neutral-90 [&_button]:dark:!bg-neutral-20">
<ConnectButton connectText="Connect" />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0

import { Button, ButtonSize, ButtonType, Panel } from '@iota/apps-ui-kit';
import { Theme, useTheme } from '@/contexts';
import { useState } from 'react';
import { StakeDialog } from '../Dialogs';
import { Theme, useTheme } from '@iota/core';

export function StartStaking() {
const { theme } = useTheme();
Expand Down
38 changes: 0 additions & 38 deletions apps/wallet-dashboard/contexts/ThemeContext.tsx

This file was deleted.

1 change: 0 additions & 1 deletion apps/wallet-dashboard/contexts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@
// SPDX-License-Identifier: Apache-2.0

export * from './PopupContext';
export * from './ThemeContext';
4 changes: 2 additions & 2 deletions apps/wallet-dashboard/providers/AppProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useState } from 'react';
import { growthbook } from '@/lib/utils';
import { Popup } from '@/components/Popup';
import { Toaster } from 'react-hot-toast';
import { ThemeProvider } from '@/contexts';
import { ThemeProvider } from '@iota/core';

growthbook.init();

Expand All @@ -36,7 +36,7 @@ export function AppProviders({ children }: React.PropsWithChildren) {
},
]}
>
<ThemeProvider>
<ThemeProvider appId="dashboard">
<PopupProvider>
{children}
<Toaster
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ import {
ImageType,
} from '@iota/apps-ui-kit';
import { ampli } from '_src/shared/analytics/ampli';
import { Theme, useTheme } from '@iota/core';

function MenuList() {
const { theme, setTheme } = useTheme();

const navigate = useNavigate();
const activeAccount = useActiveAccount();
const networkUrl = useNextMenuUrl(true, '/network');
Expand Down Expand Up @@ -77,6 +80,10 @@ function MenuList() {
window.open(FAQ_LINK, '_blank', 'noopener noreferrer');
}

function toggleTheme() {
const newTheme = theme === Theme.Light ? Theme.Dark : Theme.Light;
setTheme(newTheme);
}
const autoLockSubtitle = handleAutoLockSubtitle();
const MENU_ITEMS = [
{
Expand All @@ -99,8 +106,7 @@ function MenuList() {
{
title: 'Themes',
icon: <DarkMode />,
onClick: () => {},
isDisabled: true,
onClick: toggleTheme,
},
{
title: 'Reset',
Expand All @@ -114,12 +120,7 @@ function MenuList() {
<div className="flex h-full w-full flex-col justify-between">
<div className="flex flex-col">
{MENU_ITEMS.map((item, index) => (
<Card
key={index}
type={CardType.Default}
onClick={item.onClick}
isDisabled={item.isDisabled}
>
<Card key={index} type={CardType.Default} onClick={item.onClick}>
<CardImage type={ImageType.BgSolid}>
<div className="flex h-10 w-10 items-center justify-center rounded-full text-neutral-10 [&_svg]:h-5 [&_svg]:w-5">
<span className="text-2xl">{item.icon}</span>
Expand Down
32 changes: 17 additions & 15 deletions apps/wallet/src/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { setAttributes } from '_src/shared/experimentation/features';
import initSentry from '_src/ui/app/helpers/sentry';
import store from '_store';
import { thunkExtras } from '_store/thunk-extras';
import { KioskClientProvider } from '@iota/core';
import { KioskClientProvider, ThemeProvider } from '@iota/core';
import { GrowthBookProvider } from '@growthbook/growthbook-react';
import { IotaClientProvider } from '@iota/dapp-kit';
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
Expand Down Expand Up @@ -96,20 +96,22 @@ function AppWrapper() {
>
<KioskClientProvider>
<AccountsFormProvider>
<UnlockAccountProvider>
<div
className={cn(
'relative flex h-screen max-h-popup-height min-h-popup-minimum w-popup-width flex-col flex-nowrap items-center justify-center overflow-hidden',
isFullscreen && 'rounded-xl shadow-lg',
)}
>
<ErrorBoundary>
<App />
</ErrorBoundary>
<div id="overlay-portal-container"></div>
<div id="toaster-portal-container"></div>
</div>
</UnlockAccountProvider>
<ThemeProvider appId="wallet">
<UnlockAccountProvider>
<div
className={cn(
'relative flex h-screen max-h-popup-height min-h-popup-minimum w-popup-width flex-col flex-nowrap items-center justify-center overflow-hidden',
isFullscreen && 'rounded-xl shadow-lg',
)}
>
<ErrorBoundary>
<App />
</ErrorBoundary>
<div id="overlay-portal-container"></div>
<div id="toaster-portal-container"></div>
</div>
</UnlockAccountProvider>
</ThemeProvider>
</AccountsFormProvider>
</KioskClientProvider>
</IotaClientProvider>
Expand Down

0 comments on commit a6277f7

Please sign in to comment.