Skip to content

Commit

Permalink
feat: Popover for Upgrade icon in the status bar
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-sanderson committed Sep 25, 2024
1 parent f107c42 commit 4019d26
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 62 deletions.
3 changes: 2 additions & 1 deletion packages/components/src/components/Tooltip/TooltipBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ const TooltipContainerStyled = styled.div<TooltipContainerStyledProps>`
border-radius: ${TOOLTIP_BORDER_RADIUS};
text-align: left;
border: solid 1.5px ${palette.darkGray100};
margin: ${spacingsPx.xxxs};
max-width: ${props => props.$maxWidth}px;
${typography.hint}
${typography.hint}
> div {
padding: ${({ $isLarge, $isWithHeader }) => getContainerPadding($isLarge, $isWithHeader)};
}
Expand Down
2 changes: 1 addition & 1 deletion packages/suite/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@trezor/suite",
"suiteVersion": "24.10.0",
"suiteVersion": "0.0.0",
"version": "1.0.0",
"private": true,
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const DebugAndExperimentalTooltip = ({
isEapEnabled,
isExperimental,
}: DebugAndExperimentalTooltipProps) => (
<Column gap={spacings.md} alignItems="start">
<Column gap={spacings.md} alignItems="start" margin={{ right: spacings.xxs }}>
{isExperimental && (
<TooltipRow
circleIconName="check"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import styled from 'styled-components';
import { Column, ElevationContext, Icon, Row, Text } from '@trezor/components';
import { borders, Elevation, mapElevationToBackground, spacingsPx, zIndices } from '@trezor/theme';
import {
UpdateStatus,
UpdateStatusSuite,
UpdateStatusDevice,
mapSuiteUpdateToClick,
mapDeviceUpdateToClick,
} from './updateQuickActionTypes';
import { Translation, TranslationKey } from '../../../../../Translation';
import { useDispatch } from '../../../../../../../hooks/suite';
import { MouseEvent } from 'react';
import { createPortal } from 'react-dom';

type ContainerProps = { $elevation: Elevation };

const Container = styled.div<ContainerProps>`
position: absolute;
bottom: 56px;
left: ${spacingsPx.xs};
z-index: ${zIndices.navigationBar};
display: flex;
flex-direction: column;
width: 262px;
padding: ${spacingsPx.xs} ${spacingsPx.sm};
background: ${mapElevationToBackground};
border-radius: ${borders.radii.sm};
box-shadow: ${({ theme }) => theme.boxShadowElevated};
cursor: ${({ onClick }) => (onClick !== undefined ? 'pointer' : undefined)};
`;

const CloseIconBackground = styled.div`
width: 36px;
height: 36px;
display: flex;
justify-content: center;
align-items: center;
background-color: ${({ theme }) => theme.backgroundSurfaceElevation2};
border-radius: ${borders.radii.full};
cursor: ${({ onClick }) => (onClick !== undefined ? 'pointer' : undefined)};
`;

type UpdateNotificationBannerProps = {
updateStatusDevice: UpdateStatusDevice;
updateStatusSuite: UpdateStatusSuite;
onClose: () => void;
};

const mapDeviceUpdateStatusToTranslation: Record<UpdateStatusDevice, TranslationKey | null> = {
'just-updated': 'TR_QUICK_ACTION_UPDATE_POPOVER_TREZOR_HAS_BEEN_UPDATED',
'up-to-date': null,
'update-available': 'TR_QUICK_ACTION_UPDATE_POPOVER_TREZOR_UPDATE_AVAILABLE',
};

const mapSuiteUpdateStatusToHeaderTranslation: Record<UpdateStatusSuite, TranslationKey | null> = {
'update-downloaded-auto-restart-to-update':
'TR_QUICK_ACTION_UPDATE_POPOVER_APP_HAS_BEEN_UPDATED',
'update-downloaded-manual': 'TR_QUICK_ACTION_UPDATE_POPOVER_APP_DOWNLOADED',
'just-updated': 'TR_QUICK_ACTION_UPDATE_POPOVER_APP_HAS_BEEN_UPDATED',
'up-to-date': null,
'update-available': 'TR_QUICK_ACTION_UPDATE_POPOVER_APP_UPDATE_AVAILABLE',
};

const mapSuiteUpdateStatusToCallToActionTranslation: Record<UpdateStatus, TranslationKey | null> = {
'just-updated': 'TR_QUICK_ACTION_UPDATE_POPOVER_WHATS_NEW',
'up-to-date': null,
'update-available': 'TR_QUICK_ACTION_UPDATE_POPOVER_CLICK_TO_START_UPDATE',
'update-downloaded-auto-restart-to-update':
'TR_QUICK_ACTION_UPDATE_POPOVER_CLICK_TO_START_UPDATE',
'update-downloaded-manual': 'TR_QUICK_ACTION_UPDATE_POPOVER_CLICK_TO_START_UPDATE',
};

export const UpdateNotificationBanner = ({
updateStatusDevice,
updateStatusSuite,
onClose,
}: UpdateNotificationBannerProps) => {
const dispatch = useDispatch();

const translationHeader =
updateStatusSuite !== 'up-to-date' // Update suite first, because it will contain the newest firmware
? mapSuiteUpdateStatusToHeaderTranslation[updateStatusSuite]
: mapDeviceUpdateStatusToTranslation[updateStatusDevice];

const translationCallToAction =
mapSuiteUpdateStatusToCallToActionTranslation[
updateStatusSuite !== 'up-to-date' ? updateStatusSuite : updateStatusDevice
];

if (translationHeader === null || translationCallToAction === null) {
return null;
}

const handleOnClick = () => {
const onClick =
updateStatusSuite !== 'up-to-date'
? mapSuiteUpdateToClick[updateStatusSuite]
: mapDeviceUpdateToClick[updateStatusDevice];

if (onClick !== null) {
onClick({ dispatch });
onClose();
}
};

const handleOnClose = (e: MouseEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
onClose();
};

return createPortal(
<ElevationContext baseElevation={1}>
<Container $elevation={1} onClick={handleOnClick}>
<Row justifyContent="stretch">
<Column flex="1" alignItems="start">
<Text>
<Translation id={translationHeader} />
</Text>
<Text variant="primary">
<Translation id={translationCallToAction} />
</Text>
</Column>
<CloseIconBackground onClick={handleOnClose}>
<Icon name="x" size="medium" />
</CloseIconBackground>
</Row>
</Container>
</ElevationContext>,
document.body,
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import { borders, Color, CSSColor } from '@trezor/theme';
import { useUpdateStatus } from './useUpdateStatus';
import { UpdateTooltip } from './UpdateTooltip';
import {
mapDeviceUpdateToClick,
mapSuiteUpdateToClick,
mapUpdateStatusToIcon,
mapUpdateStatusToVariant,
UpdateStatus,
UpdateVariant,
} from './updateQuickActionTypes';
import { isDesktop } from '@trezor/env-utils';
import { useDispatch } from 'react-redux';
import { goto } from 'src/actions/suite/routerActions';
import { SettingsAnchor } from '../../../../../../../constants/suite/anchors';
import { mapTrezorModelToIcon } from '@trezor/product-components';
import { UpdateNotificationBanner } from './UpdateNotificationBanner';
import { useState } from 'react';

type MapArgs = {
$variant: UpdateVariant;
Expand Down Expand Up @@ -141,6 +143,8 @@ const SuiteUpdateIcon = ({ iconSize, updateStatus, variant }: SuiteUpdateIconPro

export const UpdateStatusActionBarIcon = () => {
const theme = useTheme();
const [closedNotificationDevice, setClosedNotificationDevice] = useState(false);
const [closedNotificationSuite, setClosedNotificationSuite] = useState(false);

const { updateStatus, updateStatusDevice, updateStatusSuite } = useUpdateStatus();
const { device } = useDevice();
Expand All @@ -157,46 +161,74 @@ export const UpdateStatusActionBarIcon = () => {
}

const handleOnClick = () => {
if (updateStatusSuite === 'update-available') {
dispatch(goto('settings-index', { anchor: SettingsAnchor.VersionWithUpdate }));
} else if (updateStatusDevice === 'update-available') {
dispatch(goto('settings-device', { anchor: SettingsAnchor.FirmwareVersion }));
if (updateStatusSuite !== 'up-to-date') {
mapSuiteUpdateToClick[updateStatusSuite]?.({ dispatch });
} else if (updateStatusDevice !== 'up-to-date') {
mapDeviceUpdateToClick[updateStatusDevice]?.({ dispatch });
}
};

const showBannerNotification =
(updateStatusSuite !== 'up-to-date' && !closedNotificationSuite) ||
(updateStatusDevice !== 'up-to-date' && !closedNotificationDevice);

const onNotificationBannerClosed = () => {
console.log('onNotificationBannerClosed');
console.log('updateStatusSuite', updateStatusSuite);
console.log('updateStatusDevice', updateStatusDevice);

if (updateStatusSuite !== 'up-to-date') {
setClosedNotificationSuite(true);
}
if (updateStatusDevice !== 'up-to-date') {
setClosedNotificationDevice(true);
}
};

return (
<QuickActionButton
tooltip={
<UpdateTooltip
<div>
{showBannerNotification && (
<UpdateNotificationBanner
updateStatusDevice={updateStatusDevice}
updateStatusSuite={updateStatusSuite}
onClose={onNotificationBannerClosed}
/>
}
onClick={handleOnClick}
>
<ComponentWithSubIcon
variant={variant}
subIconProps={{
name: updateSubIcon,
color: theme.iconDefaultInverted,
size: iconSizes.extraSmall,
}}
)}
<QuickActionButton
tooltip={
!showBannerNotification && (
<UpdateTooltip
updateStatusDevice={updateStatusDevice}
updateStatusSuite={updateStatusSuite}
/>
)
}
onClick={handleOnClick}
>
<UpdateIconGroup $variant={variant}>
<DeviceUpdateIcon
iconSize={iconSize}
updateStatus={updateStatusDevice}
variant={variant}
/>
{isDesktopSuite && (
<SuiteUpdateIcon
<ComponentWithSubIcon
variant={variant}
subIconProps={{
name: updateSubIcon,
color: theme.iconDefaultInverted,
size: iconSizes.extraSmall,
}}
>
<UpdateIconGroup $variant={variant}>
<DeviceUpdateIcon
iconSize={iconSize}
updateStatus={updateStatusDevice}
variant={variant}
/>
)}
</UpdateIconGroup>
</ComponentWithSubIcon>
</QuickActionButton>
{isDesktopSuite && (
<SuiteUpdateIcon
iconSize={iconSize}
updateStatus={updateStatusDevice}
variant={variant}
/>
)}
</UpdateIconGroup>
</ComponentWithSubIcon>
</QuickActionButton>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
mapUpdateStatusToIcon,
mapUpdateStatusToVariant,
UpdateStatus,
UpdateStatusSuite,
UpdateStatusDevice,
} from './updateQuickActionTypes';
import { Translation } from '../../../../../Translation';
import { spacings } from '@trezor/theme';
Expand All @@ -27,14 +29,15 @@ const SuiteIconRectangle = styled.div<{ $size: IconSize }>`
`;

const mapUpdateStatusToTranslation: Record<UpdateStatus, TranslationKey> = {
'update-downloaded-manual': 'TR_QUICK_ACTION_TOOLTIP_UPDATE_AVAILABLE',
'update-downloaded-auto-restart-to-update': 'TR_QUICK_ACTION_TOOLTIP_RESTART_TO_UPDATE',
'up-to-date': 'TR_QUICK_ACTION_TOOLTIP_UP_TO_DATE',
'update-available': 'TR_QUICK_ACTION_TOOLTIP_UPDATE_AVAILABLE',
'restart-to-update': 'TR_QUICK_ACTION_TOOLTIP_RESTART_TO_UPDATE',
'just-updated': 'TR_QUICK_ACTION_TOOLTIP_JUST_UPDATED',
};

type DeviceRowProps = {
updateStatus: UpdateStatus;
updateStatus: UpdateStatusDevice;
};

const DeviceRow = ({ updateStatus }: DeviceRowProps) => {
Expand Down Expand Up @@ -71,7 +74,7 @@ const DeviceRow = ({ updateStatus }: DeviceRowProps) => {
};

type SuiteRowProps = {
updateStatus: UpdateStatus;
updateStatus: UpdateStatusSuite;
};

const SuiteRow = ({ updateStatus }: SuiteRowProps) => {
Expand Down Expand Up @@ -102,8 +105,8 @@ const SuiteRow = ({ updateStatus }: SuiteRowProps) => {
};

type UpdateTooltipProps = {
updateStatusDevice: UpdateStatus;
updateStatusSuite: UpdateStatus;
updateStatusDevice: UpdateStatusDevice;
updateStatusSuite: UpdateStatusSuite;
};

export const UpdateTooltip = ({ updateStatusDevice, updateStatusSuite }: UpdateTooltipProps) => {
Expand Down
Loading

0 comments on commit 4019d26

Please sign in to comment.