Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Glowup/hide balances mode #680

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 5 additions & 2 deletions src/ui/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useMemo } from 'react';
import { AreaProvider } from 'react-area';
import { AreaProvider, RenderArea } from 'react-area';
import { QueryClientProvider, useQuery } from '@tanstack/react-query';
import {
HashRouter as Router,
Expand Down Expand Up @@ -81,6 +81,7 @@ import { BackupPage } from '../pages/Backup/Backup';
import { ProgrammaticNavigationHelper } from '../shared/routing/ProgrammaticNavigationHelper';
import { Invite } from '../features/referral-program';
import { XpDrop } from '../features/xp-drop';
import { HideBalancesFeature } from '../features/hide-balances/HideBalancesFeature';
import { RouteRestoration, registerPersistentRoute } from './RouteRestoration';

const isProd = process.env.NODE_ENV === 'production';
Expand Down Expand Up @@ -453,6 +454,7 @@ function GlobalKeyboardShortcuts() {
openUrl(url, { windowType: 'tab' });
}}
/>
<RenderArea name="global-keyboard-shortcuts" />
</>
);
}
Expand Down Expand Up @@ -502,10 +504,12 @@ export function App({ initialView, inspect }: AppProps) {
<DesignTheme bodyClassList={bodyClassList} />
<Router>
<ErrorBoundary renderError={(error) => <ViewError error={error} />}>
<GlobalKeyboardShortcuts />
<InactivityDetector />
<SessionResetHandler />
<ProgrammaticNavigationHelper />
<ThemeDecoration />
<HideBalancesFeature />
{inspect && !isProd ? (
<UIText
kind="small/regular"
Expand All @@ -517,7 +521,6 @@ export function App({ initialView, inspect }: AppProps) {
{inspect.message}
</UIText>
) : null}
<GlobalKeyboardShortcuts />
<VersionUpgrade>
{!isOnboardingView && !isPageLayout ? (
// Render above <ViewSuspense /> so that it doesn't flicker
Expand Down
71 changes: 71 additions & 0 deletions src/ui/components/HideBalance/BlurWithPixels.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useMemo } from 'react';

interface BlurPixelsProps {
color?: 'neutral' | 'positive' | 'negative' | 'primary';
// kind?: Kind;
height?: number;
pixelsProportion?: number;
}
interface BlurrableTextProps extends BlurPixelsProps {
children: React.ReactNode;
}

function randomIntFromInterval(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1) + min);
}

const getColorArray = (n: number, color: BlurrableTextProps['color']) => {
return [...Array(n * 2).keys()].map(
() => `var(--${color}-${randomIntFromInterval(1, 4) * 100})`
);
};

export function BlurWithPixels({
color = 'neutral',
// kind,
height,
pixelsProportion = 90,
}: BlurPixelsProps) {
const rowLength = useMemo(() => randomIntFromInterval(4, 6), []);
const pixelsColors = useMemo(
() => getColorArray(rowLength, color),
[color, rowLength]
);
// const gridHeight = height || (kind ? textParams[kind][1] : '1em');
const gridHeight = height || '1em';

return (
<span
title="Toggle Balance - ⇧H"
style={{
height: gridHeight,
fontSize: gridHeight,
lineHeight: 1,
display: 'inline-flex',
alignItems: 'center',
position: 'relative',
bottom: -2,
}}
>
<svg
height="1em"
width={`${rowLength * 0.5}em`}
style={{
borderRadius: '0.25em',
fontSize: `${pixelsProportion / 100}em`,
}}
>
{pixelsColors.map((color, index) => (
<rect
key={index}
height="0.5em"
width="0.5em"
fill={color}
x={`${(index % rowLength) * 0.5}em`}
y={index < rowLength ? '0' : '0.5em'}
/>
))}
</svg>
</span>
);
}
85 changes: 85 additions & 0 deletions src/ui/components/HideBalance/HideBalance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useStore } from '@store-unit/react';
import type BigNumber from 'bignumber.js';
import React from 'react';
import {
formatCurrencyToParts,
formatCurrencyValue,
} from 'src/shared/units/formatCurrencyValue';
import { formatTokenValue } from 'src/shared/units/formatTokenValue';
import {
HideBalanceMode,
hideBalancesStore,
} from 'src/ui/features/hide-balances/store';
import { NeutralDecimals } from 'src/ui/ui-kit/NeutralDecimals';
import { BlurWithPixels } from './BlurWithPixels';

function reduceValue(value: number, target = 50) {
do {
value = value / 10;
} while (Math.abs(value) > target);
return value;
}

function shuffle(value: number) {
return value * Math.PI;
}

function hideBalance(
value: number,
currency: string,
mode: HideBalanceMode
): number {
if (mode === HideBalanceMode.poorMode2) {
return value / 10;
} else if (mode === HideBalanceMode.poorMode3) {
return value / 100;
} else {
if (currency === 'btc') {
return shuffle(reduceValue(value, 0.001));
}
return shuffle(reduceValue(value));
}
}

function isPoorMode(mode: HideBalanceMode) {
return (
mode === hideBalancesStore.MODE.poorMode1 ||
mode === hideBalancesStore.MODE.poorMode2 ||
mode === hideBalancesStore.MODE.poorMode3
);
}

export function HideBalance({
value: originalValue,
children,
kind = 'currencyValue',
locale = 'en',
currency = 'usd',
symbol = '',
}: React.PropsWithChildren<{
value: string | number | BigNumber;
locale?: string;
currency?: string;
kind?: 'NeutralDecimals' | 'currencyValue' | 'tokenValue';
symbol?: string;
}>) {
const { mode } = useStore(hideBalancesStore);
if (mode === hideBalancesStore.MODE.default) {
return children;
} else if (isPoorMode(mode)) {
const fakeValue = hideBalance(Number(originalValue), currency, mode);
if (kind === 'NeutralDecimals') {
return (
<NeutralDecimals
parts={formatCurrencyToParts(fakeValue, locale, currency)}
/>
);
} else if (kind === 'currencyValue') {
return <span>{formatCurrencyValue(fakeValue, locale, currency)}</span>;
} else if (kind === 'tokenValue') {
return <span>{formatTokenValue(fakeValue, symbol)}</span>;
}
} else if (mode === hideBalancesStore.MODE.blurred) {
return <BlurWithPixels />;
}
}
1 change: 1 addition & 0 deletions src/ui/components/HideBalance/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { HideBalance } from './HideBalance';
38 changes: 31 additions & 7 deletions src/ui/components/WalletDisplayName/WalletDisplayName.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
import { useStore } from '@store-unit/react';
import React from 'react';
import type { ExternallyOwnedAccount } from 'src/shared/types/ExternallyOwnedAccount';
import { hideBalancesStore } from 'src/ui/features/hide-balances/store';
import { useProfileName } from 'src/ui/shared/useProfileName';

export function WalletDisplayName({
wallet,
padding,
maxCharacters,
render,
}: {
interface Props {
wallet: ExternallyOwnedAccount;
padding?: number;
maxCharacters?: number;
tryEns?: boolean;
render?: (data: ReturnType<typeof useProfileName>) => React.ReactNode;
}) {
}

export function WalletDisplayNameBase({
wallet,
padding,
maxCharacters,
tryEns = true,
render,
}: Props) {
const data = useProfileName(wallet, {
padding,
maxCharacters,
enabled: tryEns,
});
if (render) {
return render(data);
}
return <span style={{ wordBreak: 'break-all' }}>{data.value}</span>;
}

const toFakeAddr = (address: string) => {
const shifted =
BigInt(address) - 99999999999999999999999999999999999999999999999n;
return `0x${shifted.toString(16)}`;
};

export function WalletDisplayName(props: Props) {
const { mode } = useStore(hideBalancesStore);
if (mode !== hideBalancesStore.MODE.default) {
const fakeAddr = toFakeAddr(props.wallet.address);
const wallet = { ...props.wallet, address: fakeAddr };
return <WalletDisplayNameBase {...props} wallet={wallet} tryEns={false} />;
} else {
return <WalletDisplayNameBase {...props} />;
}
}
25 changes: 25 additions & 0 deletions src/ui/features/hide-balances/HideBalancesFeature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { Content } from 'react-area';
import { KeyboardShortcut } from 'src/ui/components/KeyboardShortcut';
import { hideBalancesStore } from './store';

export function HideBalancesFeature() {
return (
<>
<Content name="global-keyboard-shortcuts">
<KeyboardShortcut
combination="shift+h"
onKeyDown={() => {
hideBalancesStore.nextMode();
}}
/>
<KeyboardShortcut
combination="shift+j"
onKeyDown={() => {
hideBalancesStore.nextTemporary();
}}
/>
</Content>
</>
);
}
36 changes: 36 additions & 0 deletions src/ui/features/hide-balances/HideBalancesModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { useStore } from '@store-unit/react';
import EyeIcon from 'jsx:src/ui/assets/eye.svg';
import { Button } from 'src/ui/ui-kit/Button';
import { hideBalancesStore } from './store';

export function HideBalancesModeToggle() {
const { mode } = useStore(hideBalancesStore);
if (mode === hideBalancesStore.MODE.default) {
return null;
}
return (
<Button
kind="text-primary"
size={36}
title="Toggle Balances: Shift+H"
onClick={() => {
hideBalancesStore.nextMode();
}}
style={{
['--button-text-hover' as string]: 'var(--neutral-800)',
padding: 4,
}}
>
<EyeIcon
style={{
display: 'block',
color:
mode === hideBalancesStore.MODE.blurred
? 'var(--neutral-800)'
: 'var(--primary)',
}}
/>
</Button>
);
}
74 changes: 74 additions & 0 deletions src/ui/features/hide-balances/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { PersistentStore } from 'src/modules/persistent-store';

export enum HideBalanceMode {
default,
poorMode1,
poorMode2,
poorMode3,
blurred,
}

const poorModeKinds = new Set([
HideBalanceMode.poorMode1,
HideBalanceMode.poorMode2,
HideBalanceMode.poorMode3,
]);

export function isPoorMode(mode: HideBalanceMode) {
return poorModeKinds.has(mode);
}

interface State {
mode: HideBalanceMode;
poorModeKind: HideBalanceMode;
}

class HideBalancesStore extends PersistentStore<State> {
MODE = HideBalanceMode;

nextMode() {
this.setState((state) => {
const nextMap = {
[HideBalanceMode.default]: state.poorModeKind,
[HideBalanceMode.poorMode1]: HideBalanceMode.blurred,
[HideBalanceMode.poorMode2]: HideBalanceMode.blurred,
[HideBalanceMode.poorMode3]: HideBalanceMode.blurred,
[HideBalanceMode.blurred]: HideBalanceMode.default,
};
return {
...state,
mode: nextMap[state.mode],
};
});
}

nextTemporary() {
this.setState((state) => {
const nextMap = {
[HideBalanceMode.default]: HideBalanceMode.poorMode1,
[HideBalanceMode.poorMode1]: HideBalanceMode.poorMode2,
[HideBalanceMode.poorMode2]: HideBalanceMode.poorMode3,
[HideBalanceMode.poorMode3]: HideBalanceMode.default,
[HideBalanceMode.blurred]: HideBalanceMode.default,
};
const mode = nextMap[state.mode];
return {
mode,
poorModeKind: isPoorMode(mode) ? mode : state.poorModeKind,
};
});
}

setMode(mode: HideBalanceMode) {
this.setState((state) => ({
mode,
poorModeKind: isPoorMode(mode) ? mode : state.poorModeKind,
}));
}
}

const KEY = 'hide-balances-v1';
export const hideBalancesStore = new HideBalancesStore(
{ mode: HideBalanceMode.default, poorModeKind: HideBalanceMode.poorMode1 },
KEY
);
Loading
Loading