diff --git a/suite-common/wallet-core/src/device/deviceReducer.ts b/suite-common/wallet-core/src/device/deviceReducer.ts
index 8a45197661ff..0f74b5fa89cf 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 825192413a85..691f9adeee03 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 000000000000..0a568d5fc28b
--- /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 000000000000..0faa93be0f6e
--- /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 118d254e8b75..fc6d24e6e425 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 = () => {
-
+
+
);
};