From 313c85a1d7e32083936bcfe2ace2ca564595e3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Such=C3=BD?= Date: Thu, 30 Jan 2025 13:40:30 +0100 Subject: [PATCH] feat(mobile): FW update changelog (#16205) --- .../wallet-core/src/device/deviceReducer.ts | 11 +++ suite-native/intl/src/en.ts | 6 ++ .../FirmwareChangelog.tsx | 86 +++++++++++++++++++ .../FirmwareChangelogButton.tsx | 46 ++++++++++ .../FirmwareUpdateScreen.tsx | 4 +- 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareChangelog.tsx create mode 100644 suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareChangelogButton.tsx diff --git a/suite-common/wallet-core/src/device/deviceReducer.ts b/suite-common/wallet-core/src/device/deviceReducer.ts index 8a45197661f..0f74b5fa89c 100644 --- a/suite-common/wallet-core/src/device/deviceReducer.ts +++ b/suite-common/wallet-core/src/device/deviceReducer.ts @@ -1036,3 +1036,14 @@ export const selectDeviceUpdateFirmwareVersion = (state: DeviceRootState) => { return device ? getFwUpdateVersion(device) : null; }; + +export const selectFirmwareChangelog = (state: DeviceRootState) => { + const device = selectSelectedDevice(state); + const isBitcoinOnlyFirmware = selectHasBitcoinOnlyFirmware(state); + + if (isBitcoinOnlyFirmware) { + return device?.firmwareRelease?.changelog?.[0]?.changelog_bitcoinonly; + } + + return device?.firmwareRelease?.changelog?.[0]?.changelog; +}; diff --git a/suite-native/intl/src/en.ts b/suite-native/intl/src/en.ts index f51bd6c61ad..ad3b806f22b 100644 --- a/suite-native/intl/src/en.ts +++ b/suite-native/intl/src/en.ts @@ -402,6 +402,12 @@ export const en = { updateButton: 'Update firmware', title: 'Firmware update', subtitle: 'New firmware is now available. Update your device now.', + changelog: { + button: 'What’s new?', + title: 'What’s new?', + closeButton: 'Close', + changelogUnavailable: 'No changelog available', + }, }, firmwareUpdateProgress: { initializing: { title: 'Preparing your Trezor' }, diff --git a/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareChangelog.tsx b/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareChangelog.tsx new file mode 100644 index 00000000000..0a568d5fc28 --- /dev/null +++ b/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareChangelog.tsx @@ -0,0 +1,86 @@ +import { useMemo } from 'react'; +import { useSelector } from 'react-redux'; + +import { BottomSheet, Button, Text } from '@suite-native/atoms'; +import { selectFirmwareChangelog } from '@suite-common/wallet-core'; +import { Translation } from '@suite-native/intl'; +import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; + +type FirmwareChangelogProps = { + isVisible: boolean; + onClose: () => void; +}; + +const changelogSectionTitleTextStyle = prepareNativeStyle(utils => ({ + ...utils.typography.highlight, + paddingTop: utils.spacings.sp24, +})); + +const buttonContainerStyle = prepareNativeStyle(utils => ({ + marginTop: utils.spacings.sp32, +})); + +const ChangelogSectionTitle = ({ children }: { children: React.ReactNode }) => { + const { applyStyle } = useNativeStyles(); + + return {children}; +}; + +export const FirmwareChangelog = ({ isVisible, onClose }: FirmwareChangelogProps) => { + const firmwareChangelog = useSelector(selectFirmwareChangelog); + const { applyStyle } = useNativeStyles(); + + const formattedChangelog = useMemo(() => { + if (!firmwareChangelog) { + return ( + + + + ); + } + + let firmwareChangelogLines: string[] = []; + if (typeof firmwareChangelog === 'string') { + firmwareChangelogLines = firmwareChangelog.split('\n'); + } else { + firmwareChangelogLines = firmwareChangelog; + } + + return firmwareChangelogLines.map((text, index) => { + const key = text + index; + + // Match any number of '#' at the start of the line followed by text + if (/^#+\s*(.+)/.test(text)) { + const strippedText = text.replace(/^#+\s*/, '').trim(); + + return {strippedText}; + } + + // Match common list item markers with optional spaces + const listItemRegex = /^\s*[-+*]\s+(.+)/; + if (listItemRegex.test(text)) { + const formattedText = text.replace(listItemRegex, ' • $1'); + + return {formattedText}; + } + + return {text}; + }); + }, [firmwareChangelog]); + + return ( + + + + + {formattedChangelog} + + + ); +}; diff --git a/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareChangelogButton.tsx b/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareChangelogButton.tsx new file mode 100644 index 00000000000..986a75c0a8a --- /dev/null +++ b/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareChangelogButton.tsx @@ -0,0 +1,46 @@ +import { TouchableOpacity } from 'react-native'; +import { useState } from 'react'; + +import { Text } from '@suite-native/atoms'; +import { Translation } from '@suite-native/intl'; +import { Icon } from '@suite-native/icons'; +import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; + +import { FirmwareChangelog } from './FirmwareChangelog'; + +const linkTextStyle = prepareNativeStyle(utils => ({ + color: utils.colors.textSubdued, + textDecorationLine: 'underline', +})); + +const linkContainerStyle = prepareNativeStyle(_utils => ({ + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'flex-end', + gap: 2, +})); + +export const FirmwareChangelogButton = () => { + const { applyStyle } = useNativeStyles(); + const [isVisible, setIsVisible] = useState(false); + + const handlePress = () => { + setIsVisible(true); + }; + + const handleClose = () => { + setIsVisible(false); + }; + + return ( + <> + + + + + + + + + ); +}; diff --git a/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareUpdateScreen.tsx b/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareUpdateScreen.tsx index 118d254e8b7..fc6d24e6e42 100644 --- a/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareUpdateScreen.tsx +++ b/suite-native/module-device-settings/src/screens/FirmwareUpdateScreen/FirmwareUpdateScreen.tsx @@ -23,6 +23,7 @@ import { useAlert } from '@suite-native/alerts'; import { useIsFirmwareUpdateFeatureEnabled } from '@suite-native/firmware'; import { FirmwareUpdateVersionCard } from './FirmwareVersionCard'; +import { FirmwareChangelogButton } from './FirmwareChangelogButton'; const firmwareUpdateButtonStyle = prepareNativeStyle(utils => ({ marginHorizontal: utils.spacings.sp16, @@ -86,7 +87,8 @@ export const FirmwareUpdateScreen = () => { - + + ); };