Skip to content

Commit

Permalink
fix: allow virtuoso to resize dynamically, ref #5242
Browse files Browse the repository at this point in the history
  • Loading branch information
pete-watters committed Apr 22, 2024
1 parent 773421b commit ab6dd7b
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 98 deletions.
23 changes: 23 additions & 0 deletions src/app/common/hooks/use-media-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect, useState } from 'react';

import { BreakpointToken, token } from 'leather-styles/tokens';

function useMediaQuery(query: string) {
const [matches, setMatches] = useState(false);

useEffect(() => {
const media = window.matchMedia(query);
if (media.matches !== matches) {
setMatches(media.matches);
}
const listener = () => setMatches(media.matches);
window.addEventListener('resize', listener);
return () => window.removeEventListener('resize', listener);
}, [matches, query]);

return matches;
}

export function useViewportMinWidth(viewport: BreakpointToken) {
return useMediaQuery(`(min-width: ${token(`breakpoints.${viewport}`)})`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import { useWalletType } from '@app/common/use-wallet-type';
import { useCurrentAccountIndex } from '@app/store/accounts/account';
import { useFilteredBitcoinAccounts } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger';
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { useHasLedgerKeys } from '@app/store/ledger/ledger.selectors';
import { Button } from '@app/ui/components/button/button';
import { Dialog, DialogProps, getHeightOffset } from '@app/ui/components/containers/dialog/dialog';
import { Dialog, DialogProps } from '@app/ui/components/containers/dialog/dialog';
import { Footer } from '@app/ui/components/containers/footers/footer';
import { Header } from '@app/ui/components/containers/headers/header';
import { virtuosoHeight, virtuosoStyles } from '@app/ui/shared/virtuoso';
import { VirtuosoWrapper } from '@app/ui/components/virtuoso';

import { AccountListUnavailable } from './components/account-list-unavailable';
import { SwitchAccountListItem } from './components/switch-account-list-item';
Expand All @@ -22,8 +21,6 @@ export const SwitchAccountDialog = memo(({ isShowing, onClose }: DialogProps) =>
const currentAccountIndex = useCurrentAccountIndex();
const createAccount = useCreateAccount();
const { whenWallet } = useWalletType();
const isLedger = useHasLedgerKeys();

const stacksAccounts = useStacksAccounts();
const bitcoinAccounts = useFilteredBitcoinAccounts();
const btcAddressesNum = bitcoinAccounts.length / 2;
Expand All @@ -43,13 +40,13 @@ export const SwitchAccountDialog = memo(({ isShowing, onClose }: DialogProps) =>
if (!isShowing) return null;

const accountNum = stacksAddressesNum || btcAddressesNum;
const maxAccountsShown = accountNum > 10 ? 10 : accountNum;

return (
<Dialog
header={<Header variant="dialog" title="Select account" />}
isShowing={isShowing}
onClose={onClose}
wrapChildren={false}
footer={whenWallet({
software: (
<Footer>
Expand All @@ -61,24 +58,24 @@ export const SwitchAccountDialog = memo(({ isShowing, onClose }: DialogProps) =>
ledger: null,
})}
>
<Virtuoso
height={virtuosoHeight}
style={{
...virtuosoStyles,
height: `calc(${virtuosoHeight * maxAccountsShown}px + ${getHeightOffset(true, !isLedger)}px)`,
}}
initialTopMostItemIndex={whenWallet({ ledger: 0, software: currentAccountIndex })}
totalCount={stacksAddressesNum || btcAddressesNum}
itemContent={index => (
<Box key={index} my="space.05" px="space.05">
<SwitchAccountListItem
handleClose={onClose}
currentAccountIndex={currentAccountIndex}
index={index}
/>
</Box>
)}
/>
<VirtuosoWrapper hasFooter={whenWallet({ ledger: false, software: true })}>
<Virtuoso
style={{
height: '100%',
}}
initialTopMostItemIndex={whenWallet({ ledger: 0, software: currentAccountIndex })}
totalCount={accountNum}
itemContent={index => (
<Box key={index} my="space.05" px="space.05">
<SwitchAccountListItem
handleClose={onClose}
currentAccountIndex={currentAccountIndex}
index={index}
/>
</Box>
)}
/>
</VirtuosoWrapper>
</Dialog>
);
});
4 changes: 2 additions & 2 deletions src/app/features/message-signer/message-preview-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export function MessagePreviewBox({ message, hash }: MessageBoxProps) {
py="space.05"
overflowX="auto"
>
{message.split(/\r?\n/).map(line => (
<styled.span key={line} textStyle="label.01">
{message.split(/\r?\n/).map((line, index) => (
<styled.span key={`${line}_${index}`} textStyle="label.01">
{line}
</styled.span>
))}
Expand Down
17 changes: 12 additions & 5 deletions src/app/pages/choose-account/choose-account.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useEffect } from 'react';
import { useEffect } from 'react';
import { Outlet } from 'react-router-dom';

import { Flex, Stack, styled } from 'leather-styles/jsx';
Expand All @@ -10,10 +10,13 @@ import { useAppDetails } from '@app/common/hooks/auth/use-app-details';
import { RequesterFlag } from '@app/components/requester-flag';
import { ChooseAccountsList } from '@app/pages/choose-account/components/accounts';
import { useOnOriginTabClose } from '@app/routes/hooks/use-on-tab-closed';
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { LogomarkIcon } from '@app/ui/icons/logomark-icon';

export const ChooseAccount = memo(() => {
export function ChooseAccount() {
const { url } = useAppDetails();
const accounts = useStacksAccounts();
const hasConnectedStacksAccounts = accounts.length > 0;

const cancelAuthentication = useCancelAuthRequest();

Expand All @@ -34,12 +37,16 @@ export const ChooseAccount = memo(() => {
{url && <RequesterFlag requester={url.toString()} />}
<LogomarkIcon width="248px" height="58px" />
<Stack gap="space.04">
<styled.h1 textStyle="heading.05">Choose an account to connect</styled.h1>
<styled.h1 textStyle="heading.05">
{hasConnectedStacksAccounts
? 'Choose an account to connect'
: 'No connected accounts found'}
</styled.h1>
</Stack>
</Stack>
<ChooseAccountsList />
{hasConnectedStacksAccounts && <ChooseAccountsList />}
</Flex>
<Outlet />
</>
);
});
}
48 changes: 22 additions & 26 deletions src/app/pages/choose-account/components/accounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import { useNativeSegwitAccountIndexAddressIndexZero } from '@app/store/accounts
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
import { AccountAvatar } from '@app/ui/components/account/account-avatar/account-avatar';
import { VirtuosoWrapper } from '@app/ui/components/virtuoso';
import { PlusIcon } from '@app/ui/icons/plus-icon';
import { virtuosoHeight, virtuosoStyles } from '@app/ui/shared/virtuoso';

interface AccountTitlePlaceholderProps {
account: StacksAccount;
Expand Down Expand Up @@ -71,7 +71,7 @@ const ChooseAccountItem = memo(
}
);

const AddAccountAction = memo(() => {
function AddAccountAction() {
const [component, bind] = usePressable(true);
const createAccount = useCreateAccount();

Expand All @@ -88,9 +88,9 @@ const AddAccountAction = memo(() => {
{component}
</Box>
);
});
}

export const ChooseAccountsList = memo(() => {
export function ChooseAccountsList() {
const finishSignIn = useFinishAuthRequest();
const { whenWallet } = useWalletType();
const accounts = useStacksAccounts();
Expand All @@ -110,31 +110,27 @@ export const ChooseAccountsList = memo(() => {
};

if (!accounts) return null;
const accountNum = accounts.length;

const maxAccountsShown = accountNum > 10 ? 10 : accountNum;

return (
<Box mt="space.05" mb="space.06" width="100%">
{whenWallet({ software: <AddAccountAction />, ledger: <></> })}
<Virtuoso
height={virtuosoHeight}
style={{
...virtuosoStyles,
height: `calc(${virtuosoHeight * maxAccountsShown}px + 50px)`,
background: token('colors.ink.background-primary'),
}}
data={accounts}
itemContent={(index, account) => (
<Box key={index} my="space.05" px="space.05">
<ChooseAccountItem
account={account}
isLoading={whenWallet({ software: selectedAccount === index, ledger: false })}
onSelectAccount={signIntoAccount}
/>
</Box>
)}
/>
<VirtuosoWrapper>
<Virtuoso
style={{
background: token('colors.ink.background-primary'),
}}
data={accounts}
itemContent={(index, account) => (
<Box key={index} my="space.05" px="space.05">
<ChooseAccountItem
account={account}
isLoading={whenWallet({ software: selectedAccount === index, ledger: false })}
onSelectAccount={signIntoAccount}
/>
</Box>
)}
/>
</VirtuosoWrapper>
</Box>
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { Box } from 'leather-styles/jsx';

import { useFilteredBitcoinAccounts } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger';
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { Dialog, getHeightOffset } from '@app/ui/components/containers/dialog/dialog';
import { Dialog } from '@app/ui/components/containers/dialog/dialog';
import { Header } from '@app/ui/components/containers/headers/header';
import { virtuosoHeight, virtuosoStyles } from '@app/ui/shared/virtuoso';
import { VirtuosoWrapper } from '@app/ui/components/virtuoso';

import { AccountListItem } from './account-list-item';

Expand All @@ -23,27 +23,31 @@ export function RecipientAccountsDialog() {

if (stacksAddressesNum === 0 && btcAddressesNum === 0) return null;
const accountNum = stacksAddressesNum || btcAddressesNum;
const maxAccountsShown = accountNum > 10 ? 10 : accountNum;

return (
<Dialog header={<Header variant="dialog" title="My accounts" />} isShowing onClose={onGoBack}>
<Virtuoso
height={virtuosoHeight}
style={{
...virtuosoStyles,
height: `calc(${virtuosoHeight * maxAccountsShown}px + ${getHeightOffset(true, true)}px)`,
}}
itemContent={index => (
<Box key={index} my="space.05" px="space.05">
<AccountListItem
stacksAccount={stacksAccounts[index]}
onClose={onGoBack}
index={index}
/>
</Box>
)}
totalCount={stacksAddressesNum || btcAddressesNum}
/>
<Dialog
header={<Header variant="dialog" title="My accounts" />}
isShowing
onClose={onGoBack}
wrapChildren={false}
>
<VirtuosoWrapper>
<Virtuoso
style={{
height: '100%',
}}
itemContent={index => (
<Box key={index} my="space.05" px="space.05">
<AccountListItem
stacksAccount={stacksAccounts[index]}
onClose={onGoBack}
index={index}
/>
</Box>
)}
totalCount={accountNum}
/>
</VirtuosoWrapper>
</Dialog>
);
}
36 changes: 24 additions & 12 deletions src/app/ui/components/containers/dialog/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ interface RadixDialogProps extends DialogProps {
footer?: ReactNode;
header?: ReactElement<any, string | JSXElementConstructor<any>>;
onGoBack?(): void;
wrapChildren?: boolean;
}

export function getHeightOffset(header: ReactNode, footer: ReactNode) {
function getHeightOffset(header: ReactNode, footer: ReactNode) {
const headerHeight = header ? pxStringToNumber(token('sizes.headerHeight')) : 0;
const footerHeight = footer ? pxStringToNumber(token('sizes.footerHeight')) : 0;
return headerHeight + footerHeight;
Expand All @@ -30,7 +31,14 @@ function getContentMaxHeight(maxHeightOffset: number) {
return `calc(${virtualHeight}vh - ${maxHeightOffset}px)`;
}

export function Dialog({ children, footer, header, onClose, isShowing }: RadixDialogProps) {
export function Dialog({
children,
footer,
header,
onClose,
isShowing,
wrapChildren = true,
}: RadixDialogProps) {
if (!isShowing) return null;

const maxHeightOffset = getHeightOffset(header, footer);
Expand Down Expand Up @@ -68,16 +76,20 @@ export function Dialog({ children, footer, header, onClose, isShowing }: RadixDi
>
{header && cloneElement(header, { onClose })}

<Box
style={{
height: '100%',
maxHeight: contentMaxHeight,
marginBottom: footer ? token('sizes.footerHeight') : token('spacing.space.04'),
overflowY: 'auto',
}}
>
{children}
</Box>
{wrapChildren ? (
<Box
style={{
height: '100%',
maxHeight: contentMaxHeight,
marginBottom: footer ? token('sizes.footerHeight') : token('spacing.space.04'),
overflowY: 'auto',
}}
>
{children}
</Box>
) : (
children
)}
{footer}
</RadixDialog.Content>
</RadixDialog.Overlay>
Expand Down
36 changes: 36 additions & 0 deletions src/app/ui/components/virtuoso.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type React from 'react';

import { Box } from 'leather-styles/jsx';

import { useViewportMinWidth } from '@app/common/hooks/use-media-query';

interface VirtuosoWrapper {
children: React.ReactNode;
hasFooter?: boolean;
}

function vhToPixels(vh: string) {
const numericHeight = +vh.replace('vh', '');
return (numericHeight * window.innerHeight) / 100;
}

export function VirtuosoWrapper({ children, hasFooter }: VirtuosoWrapper) {
const isAtLeastMd = useViewportMinWidth('md');
const virtualHeight = isAtLeastMd ? '70vh' : '100vh';
const headerHeight = 75;
const footerHeight = hasFooter ? 125 : 0;
const heightOffset = headerHeight + footerHeight;
const height = vhToPixels(virtualHeight) - heightOffset;

return (
<Box
style={{
height: height,
overflow: 'hidden',
marginBottom: hasFooter ? '100px' : '10px',
}}
>
{children}
</Box>
);
}
Loading

0 comments on commit ab6dd7b

Please sign in to comment.