Skip to content

Commit

Permalink
Merge pull request #4079 from iotaledger/tooling-epic/add-darkmode-su…
Browse files Browse the repository at this point in the history
…pport-to-wallet

feat(wallet): add darkmode/lightmode theme
  • Loading branch information
begonaalvarezd authored Nov 25, 2024
2 parents 28692bf + 08b61b7 commit 15f6ea6
Show file tree
Hide file tree
Showing 67 changed files with 301 additions and 151 deletions.
4 changes: 2 additions & 2 deletions apps/core/src/components/coin/CoinIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface NonIotaCoinProps {
function NonIotaCoin({ coinType, size = ImageIconSize.Full, rounded }: NonIotaCoinProps) {
const { data: coinMeta } = useCoinMetadata(coinType);
return (
<div className="flex h-full w-full items-center justify-center rounded-full bg-neutral-96 dark:bg-neutral-92">
<div className="flex h-full w-full items-center justify-center rounded-full bg-neutral-96 dark:bg-neutral-12">
<ImageIcon
src={coinMeta?.iconUrl}
label={coinMeta?.name || coinType}
Expand Down Expand Up @@ -47,7 +47,7 @@ export function CoinIcon({

return coinType === IOTA_TYPE_ARG ? (
<Component {...coinWrapperProps}>
<div className={cx(size, 'text-neutral-10')}>
<div className={cx(size, 'text-neutral-10 dark:text-neutral-92')}>
<IotaLogoMark className="h-full w-full" />
</div>
</Component>
Expand Down
9 changes: 2 additions & 7 deletions apps/core/src/components/coin/CoinItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,8 @@ export function CoinItem({
return (
<Card type={CardType.Default} onClick={onClick}>
<CardImage type={ImageType.BgTransparent}>
<div className="flex h-10 w-10 items-center justify-center rounded-full">
<CoinIcon
coinType={coinType}
rounded
size={ImageIconSize.Small}
hasCoinWrapper
/>
<div className="flex h-10 w-10 items-center justify-center rounded-full border border-shader-neutral-light-8 text-neutral-10 dark:text-neutral-92">
<CoinIcon coinType={coinType} rounded size={ImageIconSize.Small} />
</div>
</CardImage>
<CardBody
Expand Down
2 changes: 1 addition & 1 deletion apps/core/src/components/coin/CoinSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function CoinSelectOption({
hasCoinWrapper={hasCoinWrapper}
/>
</div>
<span className="text-body-lg text-neutral-10">
<span className="text-body-lg text-neutral-10 dark:text-neutral-92">
{isIota ? (coinMeta?.name || '').toUpperCase() : coinMeta?.name || symbol}
</span>
</div>
Expand Down
5 changes: 2 additions & 3 deletions apps/core/src/components/icon/ImageIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ function FallBackAvatar({ str, rounded, size = ImageIconSize.Large }: FallBackAv
return (
<div
className={cn(
'flex items-center justify-center bg-neutral-96 bg-gradient-to-r capitalize text-neutral-10 dark:bg-neutral-92 dark:text-primary-100',
{ 'rounded-full': rounded, 'rounded-lg': !rounded },
size,
'flex h-full w-full items-center justify-center bg-neutral-96 bg-gradient-to-r capitalize text-neutral-10 dark:bg-neutral-12 dark:text-neutral-92',
{ 'rounded-full': rounded },
generateTextSize(size),
)}
>
Expand Down
4 changes: 2 additions & 2 deletions apps/core/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './KioskClientProvider';

export * from './coin';
export * from './icon';
export * from './Inputs';
export * from './QR';

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

import { PropsWithChildren, useState, useEffect, useCallback } 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 getSystemTheme = () =>
window.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.Dark : Theme.Light;

const getInitialTheme = () => {
if (typeof window === 'undefined') {
return Theme.System;
} else {
const storedTheme = localStorage?.getItem(storageKey);
return storedTheme ? (storedTheme as Theme) : Theme.System;
}
};

const [theme, setTheme] = useState<Theme>(getInitialTheme);

const applyTheme = useCallback((currentTheme: Theme) => {
const selectedTheme = currentTheme === Theme.System ? getSystemTheme() : currentTheme;
const documentElement = document.documentElement.classList;
documentElement.toggle(Theme.Dark, selectedTheme === Theme.Dark);
documentElement.toggle(Theme.Light, selectedTheme === Theme.Light);
}, []);

useEffect(() => {
if (typeof window === 'undefined') return;

localStorage.setItem(storageKey, theme);
applyTheme(theme);

if (theme === Theme.System) {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)');
const handleSystemThemeChange = () => applyTheme(Theme.System);
systemTheme.addEventListener('change', handleSystemThemeChange);
return () => systemTheme.removeEventListener('change', handleSystemThemeChange);
}
}, [theme, applyTheme, 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';
File renamed without changes.
3 changes: 2 additions & 1 deletion apps/core/src/enums/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

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

export enum Theme {
Light = 'light',
Dark = 'dark',
System = 'system',
}
1 change: 1 addition & 0 deletions apps/core/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ export * from './useGasBudgetEstimation';
export * from './useTransactionData';
export * from './useGetStakingValidatorDetails';
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 @@ -8,4 +8,6 @@ export * from './components';
export * from './utils';
export * from './hooks';
export * from './constants';
export * from './contexts';
export * from './enums';
export * from './forms';
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 @@ -6,10 +6,15 @@ import { Notifications } from '@/components/index';
import React, { type PropsWithChildren } from 'react';
import { Button } from '@iota/apps-ui-kit';
import { Sidebar, TopNav } from './components';
import { useTheme } from '@/contexts';
import { Theme, useTheme } from '@iota/core';

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

const toggleTheme = () => {
const newTheme = theme === Theme.Light ? Theme.Dark : Theme.Light;
setTheme(newTheme);
};

return (
<div className="h-full">
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
2 changes: 1 addition & 1 deletion apps/wallet/src/ui/app/components/DAppPermissionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function DAppPermissionList({ permissions }: DAppPermissionListProps) {
{permissions.map((permissionKey) => (
<SummaryListItem
key={permissionKey}
icon={<Checkmark className="h-5 w-5 text-neutral-10" />}
icon={<Checkmark className="h-5 w-5 text-neutral-10 dark:text-neutral-92" />}
text={PERMISSION_TYPE_TO_TEXT[permissionKey]}
/>
))}
Expand Down
6 changes: 5 additions & 1 deletion apps/wallet/src/ui/app/components/NoData.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Theme, useTheme } from '@iota/core';
import NoDataImage from '_assets/images/no_data.svg';
import NoDataDarkImage from '_assets/images/no_data_darkmode.svg';
interface NoDataProps {
message: string;
}

export function NoData({ message }: NoDataProps) {
const { theme } = useTheme();

return (
<div className="flex h-full flex-col items-center justify-center gap-md text-center">
<NoDataImage />
{theme === Theme.Dark ? <NoDataDarkImage /> : <NoDataImage />}
<span className="text-label-lg text-neutral-60">{message}</span>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions apps/wallet/src/ui/app/components/Overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface OverlayProps {
closeOverlay?: () => void;
closeIcon?: ReactNode | null;
setShowModal?: (showModal: boolean) => void;
background?: 'bg-neutral-100';
background?: 'bg-neutral-100 dark:bg-neutral-6';
titleCentered?: boolean;
showBackButton?: boolean;
onBack?: () => void;
Expand Down Expand Up @@ -58,7 +58,7 @@ export function Overlay({
testId="overlay-title"
/>
)}
<div className="flex w-full flex-1 flex-col overflow-hidden bg-neutral-100 p-md">
<div className="flex w-full flex-1 flex-col overflow-hidden bg-neutral-100 p-md dark:bg-neutral-6">
{children}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/wallet/src/ui/app/components/PageTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function PageTemplate({
onClose={onClose}
/>
)}
<div className="w-full flex-1 overflow-y-auto overflow-x-hidden bg-neutral-100 p-md">
<div className="w-full flex-1 overflow-y-auto overflow-x-hidden bg-neutral-100 p-md dark:bg-neutral-6">
{children}
</div>
</div>
Expand Down
Loading

0 comments on commit 15f6ea6

Please sign in to comment.