From a6277f74cb1a83554bacad885fce3e3ac68856bf Mon Sep 17 00:00:00 2001 From: evavirseda Date: Fri, 15 Nov 2024 11:08:09 +0100 Subject: [PATCH] feat(wallet): add darkmode/lightmode theme selection logic (#4053) * 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 --- apps/core/src/components/index.ts | 3 +- .../{ => providers}/KioskClientProvider.tsx | 0 .../components/providers/ThemeProvider.tsx | 44 +++++++++++++++++++ apps/core/src/components/providers/index.ts | 5 +++ apps/core/src/contexts/ThemeContext.tsx | 15 +++++++ apps/core/src/contexts/index.ts | 4 ++ apps/core/src/enums/index.ts | 4 ++ apps/core/src/enums/theme.enums.ts | 7 +++ apps/core/src/hooks/index.ts | 1 + apps/core/src/hooks/useTheme.ts | 12 +++++ apps/core/src/index.ts | 2 + .../app/(protected)/layout.tsx | 9 +++- apps/wallet-dashboard/app/page.tsx | 21 +++++---- .../staking-overview/StartStaking.tsx | 2 +- .../contexts/ThemeContext.tsx | 38 ---------------- apps/wallet-dashboard/contexts/index.ts | 1 - .../providers/AppProviders.tsx | 4 +- .../menu/content/WalletSettingsMenuList.tsx | 17 +++---- apps/wallet/src/ui/index.tsx | 32 +++++++------- 19 files changed, 145 insertions(+), 76 deletions(-) rename apps/core/src/components/{ => providers}/KioskClientProvider.tsx (100%) create mode 100644 apps/core/src/components/providers/ThemeProvider.tsx create mode 100644 apps/core/src/components/providers/index.ts create mode 100644 apps/core/src/contexts/ThemeContext.tsx create mode 100644 apps/core/src/contexts/index.ts create mode 100644 apps/core/src/enums/index.ts create mode 100644 apps/core/src/enums/theme.enums.ts create mode 100644 apps/core/src/hooks/useTheme.ts delete mode 100644 apps/wallet-dashboard/contexts/ThemeContext.tsx diff --git a/apps/core/src/components/index.ts b/apps/core/src/components/index.ts index fac843e4062..ce35b5a07d1 100644 --- a/apps/core/src/components/index.ts +++ b/apps/core/src/components/index.ts @@ -1,5 +1,6 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from './KioskClientProvider'; export * from './QR'; + +export * from './providers'; diff --git a/apps/core/src/components/KioskClientProvider.tsx b/apps/core/src/components/providers/KioskClientProvider.tsx similarity index 100% rename from apps/core/src/components/KioskClientProvider.tsx rename to apps/core/src/components/providers/KioskClientProvider.tsx diff --git a/apps/core/src/components/providers/ThemeProvider.tsx b/apps/core/src/components/providers/ThemeProvider.tsx new file mode 100644 index 00000000000..b028d200e92 --- /dev/null +++ b/apps/core/src/components/providers/ThemeProvider.tsx @@ -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) { + const storageKey = `theme_${appId}`; + const [theme, setTheme] = useState(() => { + 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 ( + + {children} + + ); +} diff --git a/apps/core/src/components/providers/index.ts b/apps/core/src/components/providers/index.ts new file mode 100644 index 00000000000..5bbf1329e64 --- /dev/null +++ b/apps/core/src/components/providers/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './KioskClientProvider'; +export * from './ThemeProvider'; diff --git a/apps/core/src/contexts/ThemeContext.tsx b/apps/core/src/contexts/ThemeContext.tsx new file mode 100644 index 00000000000..3406e50d5c1 --- /dev/null +++ b/apps/core/src/contexts/ThemeContext.tsx @@ -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({ + theme: Theme.Light, + setTheme: () => {}, +}); diff --git a/apps/core/src/contexts/index.ts b/apps/core/src/contexts/index.ts new file mode 100644 index 00000000000..c592171afa8 --- /dev/null +++ b/apps/core/src/contexts/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './ThemeContext'; diff --git a/apps/core/src/enums/index.ts b/apps/core/src/enums/index.ts new file mode 100644 index 00000000000..6e446b406da --- /dev/null +++ b/apps/core/src/enums/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './theme.enums'; diff --git a/apps/core/src/enums/theme.enums.ts b/apps/core/src/enums/theme.enums.ts new file mode 100644 index 00000000000..ab9083c4725 --- /dev/null +++ b/apps/core/src/enums/theme.enums.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export enum Theme { + Light = 'light', + Dark = 'dark', +} diff --git a/apps/core/src/hooks/index.ts b/apps/core/src/hooks/index.ts index fe71839183a..8e68020acc0 100644 --- a/apps/core/src/hooks/index.ts +++ b/apps/core/src/hooks/index.ts @@ -38,5 +38,6 @@ export * from './useGetAllOwnedObjects'; export * from './useGetTimelockedStakedObjects'; export * from './useGetActiveValidatorsInfo'; export * from './useCursorPagination'; +export * from './useTheme'; export * from './stake'; diff --git a/apps/core/src/hooks/useTheme.ts b/apps/core/src/hooks/useTheme.ts new file mode 100644 index 00000000000..72cddc121c3 --- /dev/null +++ b/apps/core/src/hooks/useTheme.ts @@ -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; +}; diff --git a/apps/core/src/index.ts b/apps/core/src/index.ts index bc58d29353b..b89aa6903b2 100644 --- a/apps/core/src/index.ts +++ b/apps/core/src/index.ts @@ -7,3 +7,5 @@ export * from './components'; export * from './utils'; export * from './hooks'; export * from './constants'; +export * from './contexts'; +export * from './enums'; diff --git a/apps/wallet-dashboard/app/(protected)/layout.tsx b/apps/wallet-dashboard/app/(protected)/layout.tsx index c566410af67..2d496916be7 100644 --- a/apps/wallet-dashboard/app/(protected)/layout.tsx +++ b/apps/wallet-dashboard/app/(protected)/layout.tsx @@ -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) { diff --git a/apps/wallet-dashboard/app/page.tsx b/apps/wallet-dashboard/app/page.tsx index 38001d4b184..7626f88bb2c 100644 --- a/apps/wallet-dashboard/app/page.tsx +++ b/apps/wallet-dashboard/app/page.tsx @@ -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) { @@ -23,18 +29,15 @@ function HomeDashboardPage(): JSX.Element { return (
-
+
@@ -42,12 +45,14 @@ function HomeDashboardPage(): JSX.Element {
Welcome to -

IOTA Wallet

+

+ IOTA Wallet +

Connecting you to the decentralized web and IOTA network
-
+
diff --git a/apps/wallet-dashboard/components/staking-overview/StartStaking.tsx b/apps/wallet-dashboard/components/staking-overview/StartStaking.tsx index dd28cca199c..5190cc06867 100644 --- a/apps/wallet-dashboard/components/staking-overview/StartStaking.tsx +++ b/apps/wallet-dashboard/components/staking-overview/StartStaking.tsx @@ -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(); diff --git a/apps/wallet-dashboard/contexts/ThemeContext.tsx b/apps/wallet-dashboard/contexts/ThemeContext.tsx deleted file mode 100644 index 2de1469e42e..00000000000 --- a/apps/wallet-dashboard/contexts/ThemeContext.tsx +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; - -export enum Theme { - Light = 'light', - Dark = 'dark', -} - -interface ThemeContextType { - theme: Theme; - toggleTheme: () => void; -} - -const ThemeContext = createContext(undefined); - -export const ThemeProvider: React.FC<{ children: ReactNode }> = ({ children }) => { - const [theme, setTheme] = useState(Theme.Light); - - useEffect(() => { - document.documentElement.classList.toggle(Theme.Dark, theme === Theme.Dark); - }, [theme]); - - const toggleTheme = () => { - setTheme((prevTheme) => (prevTheme === Theme.Dark ? Theme.Light : Theme.Dark)); - }; - - return {children}; -}; - -export const useTheme = (): ThemeContextType => { - const context = useContext(ThemeContext); - if (!context) { - throw new Error('useTheme must be used within a ThemeProvider'); - } - return context; -}; diff --git a/apps/wallet-dashboard/contexts/index.ts b/apps/wallet-dashboard/contexts/index.ts index fa0a87e74c4..d356e2485ed 100644 --- a/apps/wallet-dashboard/contexts/index.ts +++ b/apps/wallet-dashboard/contexts/index.ts @@ -2,4 +2,3 @@ // SPDX-License-Identifier: Apache-2.0 export * from './PopupContext'; -export * from './ThemeContext'; diff --git a/apps/wallet-dashboard/providers/AppProviders.tsx b/apps/wallet-dashboard/providers/AppProviders.tsx index 48561b1ff27..1cdcf6da752 100644 --- a/apps/wallet-dashboard/providers/AppProviders.tsx +++ b/apps/wallet-dashboard/providers/AppProviders.tsx @@ -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(); @@ -36,7 +36,7 @@ export function AppProviders({ children }: React.PropsWithChildren) { }, ]} > - + {children} , - onClick: () => {}, - isDisabled: true, + onClick: toggleTheme, }, { title: 'Reset', @@ -114,12 +120,7 @@ function MenuList() {
{MENU_ITEMS.map((item, index) => ( - +
{item.icon} diff --git a/apps/wallet/src/ui/index.tsx b/apps/wallet/src/ui/index.tsx index 49dff3a6b7d..8598908f346 100644 --- a/apps/wallet/src/ui/index.tsx +++ b/apps/wallet/src/ui/index.tsx @@ -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'; @@ -96,20 +96,22 @@ function AppWrapper() { > - -
- - - -
-
-
-
+ + +
+ + + +
+
+
+
+