Skip to content

Commit

Permalink
fix: header alignment and request pw
Browse files Browse the repository at this point in the history
  • Loading branch information
fbwoolf authored and pete-watters committed Mar 21, 2024
1 parent 88fc50c commit b9552b9
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 14 deletions.
26 changes: 26 additions & 0 deletions src/app/common/hooks/use-interval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect, useRef } from 'react';

import { noop } from '@shared/utils';

export function useInterval(callback: () => void, delay: number | null) {
const savedCallback = useRef(noop);

// Remember the latest callback.
useEffect(() => {
if (savedCallback) {
savedCallback.current = callback;
}
}, [callback]);

// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
return;
}, [delay]);
}
39 changes: 39 additions & 0 deletions src/app/common/hooks/use-waiting-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useMemo, useRef, useState } from 'react';

import { useInterval } from './use-interval';

// Keys are the seconds to wait before showing the message
export type WaitingMessages = Record<number, string>;

function messageForSecondsPassed(waitingMessages: WaitingMessages, seconds: number) {
return waitingMessages[seconds as keyof typeof waitingMessages];
}

export const useWaitingMessage = (
waitingMessages: WaitingMessages,
{ waitingMessageInterval } = {
waitingMessageInterval: 1000,
}
): [boolean, string, () => void, () => void] => {
const [isRunning, setIsRunning] = useState(false);
const [waitingMessage, setWaitingMessage] = useState(messageForSecondsPassed(waitingMessages, 0));
const handlers = useMemo(
() => ({
startWaitingMessage: () => setIsRunning(true),
stopWaitingMessage: () => setIsRunning(false),
}),
[]
);
const secondsPassed = useRef(0);

useInterval(
() => {
secondsPassed.current += waitingMessageInterval / 1000;
const newMessage = messageForSecondsPassed(waitingMessages, secondsPassed.current);
if (newMessage) setWaitingMessage(newMessage);
},
isRunning ? waitingMessageInterval : null
);

return [isRunning, waitingMessage, handlers.startWaitingMessage, handlers.stopWaitingMessage];
};
26 changes: 20 additions & 6 deletions src/app/components/request-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Box, Stack, styled } from 'leather-styles/jsx';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useKeyActions } from '@app/common/hooks/use-key-actions';
import { buildEnterKeyEvent } from '@app/common/hooks/use-modifier-key';
import { WaitingMessages, useWaitingMessage } from '@app/common/hooks/use-waiting-message';
import { Button } from '@app/ui/components/button/button';
import { Footer } from '@app/ui/components/containers/footers/footer';
import { Logo } from '@app/ui/components/logo';
Expand All @@ -14,6 +15,15 @@ import { Page } from '@app/ui/layout/page/page.layout';

import { ErrorLabel } from './error-label';

const waitingMessages: WaitingMessages = {
'2': 'Verifying password…',
'10': 'Still working…',
'20': 'Almost there',
};

const caption =
'Your password is used to secure your Secret Key and is only used locally on your device.';

interface RequestPasswordProps {
onSuccess(): void;
}
Expand All @@ -22,21 +32,26 @@ export function RequestPassword({ onSuccess }: RequestPasswordProps) {
const [error, setError] = useState('');
const { unlockWallet } = useKeyActions();
const analytics = useAnalytics();
const [isRunning, waitingMessage, startWaitingMessage, stopWaitingMessage] =
useWaitingMessage(waitingMessages);

const submit = useCallback(async () => {
const startUnlockTimeMs = performance.now();
void analytics.track('start_unlock');
startWaitingMessage();
setError('');
try {
await unlockWallet(password);
onSuccess?.();
} catch (error) {
setError('The password you entered is invalid');
}
stopWaitingMessage();
const unlockSuccessTimeMs = performance.now();
void analytics.track('complete_unlock', {
durationMs: unlockSuccessTimeMs - startUnlockTimeMs,
});
}, [analytics, unlockWallet, password, onSuccess]);
}, [analytics, startWaitingMessage, stopWaitingMessage, unlockWallet, password, onSuccess]);

return (
<Page>
Expand All @@ -52,7 +67,8 @@ export function RequestPassword({ onSuccess }: RequestPasswordProps) {
<Footer variant="card">
<Button
data-testid={SettingsSelectors.UnlockWalletBtn}
disabled={!!error}
disabled={isRunning || !!error}
aria-busy={isRunning}
onClick={submit}
variant="solid"
>
Expand All @@ -61,11 +77,9 @@ export function RequestPassword({ onSuccess }: RequestPasswordProps) {
</Footer>
}
>
<Stack gap="space.05" px="space.05" minHeight="300px">
<Stack gap="space.05" px="space.05" minHeight="330px">
<styled.h3 textStyle="heading.03">Enter your password</styled.h3>
<styled.p textStyle="label.02">
Your password is used to secure your Secret Key and is only used locally on your device.
</styled.p>
<styled.p textStyle="label.02">{(isRunning && waitingMessage) || caption}</styled.p>
<styled.input
_focus={{ border: 'focus' }}
autoCapitalize="off"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HStack, styled } from 'leather-styles/jsx';

import { whenPageMode } from '@app/common/utils';
import { ChevronsRightIcon } from '@app/ui/icons/chevrons-right-icon';

interface IncreaseFeeButtonProps {
Expand All @@ -15,7 +16,7 @@ export function IncreaseFeeButton(props: IncreaseFeeButtonProps) {
<styled.button
_hover={{ color: 'ink.text-subdued' }}
bg="ink.background-primary"
minWidth="105px"
maxWidth="110px"
ml="auto"
onClick={e => {
onIncreaseFee();
Expand All @@ -32,7 +33,12 @@ export function IncreaseFeeButton(props: IncreaseFeeButtonProps) {
>
<HStack gap="space.01">
<ChevronsRightIcon color="stacks" variant="small" />
<styled.span textStyle="label.03">Increase fee</styled.span>
<styled.span textStyle="label.03">
{whenPageMode({
popup: 'Fee',
full: 'Increase fee',
})}
</styled.span>
</HStack>
</styled.button>
);
Expand Down
8 changes: 8 additions & 0 deletions src/app/features/container/utils/route-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ function isOnboardingPage(pathname: RouteUrls) {
);
}

function isFundPage(pathname: RouteUrls) {
return (
pathname === RouteUrls.Fund.replace(':currency', 'STX') ||
pathname === RouteUrls.Fund.replace(':currency', 'BTC')
);
}

export function getPageVariant(pathname: RouteUrls) {
if (isFundPage(pathname)) return 'fund';
if (isHomePage(pathname)) return 'home';
if (isOnboardingPage(pathname)) return 'onboarding';
if (isSummaryPage(pathname)) return 'summary';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function IncreaseFeeDialog({ feeForm, onClose, isShowing }: IncreaseFeeDi
<Dialog
isShowing={isShowing}
onClose={onClose}
header={<Header variant="dialog" title="Increase transaction fee" />}
header={<Header variant="dialog" title="Increase fee" />}
>
<Stack gap="space.05" px="space.05" pb="space.05">
<Suspense
Expand Down
2 changes: 1 addition & 1 deletion src/app/ui/components/containers/container.layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function ContainerLayout({ children, header, variant }: ContainerLayoutPr
height={{ base: '100vh', sm: '100%' }}
className={css(radixBaseCSS)}
bg={{
base: 'ink.background-primary',
base: variant === 'fund' ? 'ink.background-secondary' : 'ink.background-primary',
md: variant !== 'home' ? 'ink.background-secondary' : undefined,
}}
>
Expand Down
13 changes: 9 additions & 4 deletions src/app/ui/components/containers/headers/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IconButton } from '../../icon-button/icon-button';
import { BigTitleHeader } from './components/big-title-header';
import { HeaderActionButton } from './components/header-action-button';

type HeaderVariants = 'page' | 'home' | 'onboarding' | 'dialog' | 'bigTitle' | 'summary';
type HeaderVariants = 'page' | 'home' | 'onboarding' | 'dialog' | 'bigTitle' | 'summary' | 'fund';

export interface HeaderProps {
variant: HeaderVariants;
Expand Down Expand Up @@ -43,16 +43,21 @@ export function Header({

return (
<styled.header
justifyContent="center"
margin={{ base: 0, md: 'auto' }}
p={variant === 'bigTitle' ? 'space.05' : 'space.04'}
bg={{ base: 'ink.background-primary', sm: 'transparent' }}
bg={{
base: variant === 'fund' ? 'ink.background-secondary' : 'ink.background-primary',
sm: 'transparent',
}}
maxWidth={{ base: '100vw', md: 'fullPageMaxWidth' }}
width="100%"
>
<Grid
alignItems="center"
gridTemplateColumns={title ? '2fr 4fr 2fr' : 'auto 4fr auto'}
gridAutoFlow="column"
width="100%"
maxWidth={{ base: '100vw', md: 'fullPageMaxWidth' }}
margin={{ base: 0, md: 'auto' }}
hideFrom={variant === 'bigTitle' ? 'md' : undefined}
>
<GridItem justifySelf="start">
Expand Down

0 comments on commit b9552b9

Please sign in to comment.