Skip to content

Commit e0c952b

Browse files
committed
frontend: add red dot in sidebar and settings when fw can update
Users miss that the firmware can update or are just not aware. Added red dot in the sidebar (or on device tab) indicates that there is an important action.
1 parent 5252eaa commit e0c952b

File tree

6 files changed

+87
-8
lines changed

6 files changed

+87
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- Add Satoshi as an option in active currencies
1212
- Show address re-use warning and group UTXOs with the same address together in coin control.
1313
- Fix encoding of transaction notes on Windows
14+
- Add red dot in sidebar and on device settings tab to indicate that there is a firmware upgrade
1415

1516
## 4.42.1
1617
- BitBox02: fix missing button to re-install firmware, fixing interrupted installs ("invalid firmware").

frontends/web/src/app.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export const App = () => {
152152
<Sidebar
153153
accounts={activeAccounts}
154154
deviceIDs={deviceIDs}
155+
devices={devices}
155156
/>
156157
<div className="appContent flex flex-column flex-1" style={{ minWidth: 0 }}>
157158
<Update />

frontends/web/src/components/sidebar/sidebar.module.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,13 @@ a.sidebarActive .single img,
223223
text-align: left;
224224
width: 100%;
225225
}
226+
227+
.canUpgradeDot {
228+
height: 8px;
229+
left: 2px;
230+
max-width: 8px;
231+
position: relative;
232+
top: -2px;
233+
vertical-align: top;
234+
width: 8px;
235+
}

frontends/web/src/components/sidebar/sidebar.tsx

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
* limitations under the License.
1616
*/
1717

18-
import React, { useContext, useEffect } from 'react';
18+
import React, { useContext, useEffect, useState } from 'react';
1919
import { useLocation } from 'react-router';
2020
import { Link, NavLink } from 'react-router-dom';
2121
import { useTranslation } from 'react-i18next';
2222
import { useKeystores } from '../../hooks/backend';
23-
import { IAccount } from '../../api/account';
23+
import type { TDevices } from '../../api/devices';
24+
import type { IAccount } from '../../api/account';
2425
import { deregisterTest } from '../../api/keystores';
26+
import { getVersion } from '../../api/bitbox02';
2527
import coins from '../../assets/icons/coins.svg';
2628
import ejectIcon from '../../assets/icons/eject.svg';
2729
import shieldIcon from '../../assets/icons/shield_grey.svg';
@@ -31,7 +33,7 @@ import settingsGrey from '../../assets/icons/settings-alt_disabled.svg';
3133
import deviceSettings from '../../assets/icons/wallet-light.svg';
3234
import { debug } from '../../utils/env';
3335
import { AppLogoInverted, Logo } from '../icon/logo';
34-
import { CloseXWhite, USBSuccess } from '../icon';
36+
import { CloseXWhite, RedDot, USBSuccess } from '../icon';
3537
import { getAccountsByKeystore, isAmbiguiousName, isBitcoinOnly } from '../../routes/account/utils';
3638
import { SkipForTesting } from '../../routes/device/components/skipfortesting';
3739
import { Badge } from '../badge/badge';
@@ -41,6 +43,7 @@ import style from './sidebar.module.css';
4143

4244
type SidebarProps = {
4345
deviceIDs: string[];
46+
devices: TDevices;
4447
accounts: IAccount[];
4548
};
4649

@@ -75,12 +78,32 @@ const eject = (e: React.SyntheticEvent): void => {
7578

7679
const Sidebar = ({
7780
deviceIDs,
81+
devices,
7882
accounts,
7983
}: SidebarProps) => {
8084
const { t } = useTranslation();
8185
const { pathname } = useLocation();
86+
const [ canUpgrade, setCanUpgrade ] = useState(false);
8287
const { activeSidebar, sidebarStatus, toggleSidebar } = useContext(AppContext);
8388

89+
useEffect(() => {
90+
const checkUpgradableDevices = async () => {
91+
setCanUpgrade(false);
92+
const bitbox02Devices = Object.keys(devices).filter(deviceID => devices[deviceID] === 'bitbox02');
93+
94+
for (const deviceID of bitbox02Devices) {
95+
const { canUpgrade } = await getVersion(deviceID);
96+
if (canUpgrade) {
97+
setCanUpgrade(true);
98+
// exit early as we found an upgradable device
99+
return;
100+
}
101+
}
102+
};
103+
104+
checkUpgradableDevices();
105+
}, [devices]);
106+
84107
useEffect(() => {
85108
const swipe = {
86109
active: false,
@@ -243,7 +266,12 @@ const Sidebar = ({
243266
<img draggable={false} src={settingsGrey} alt={t('sidebar.settings')} />
244267
<img draggable={false} src={settings} alt={t('sidebar.settings')} />
245268
</div>
246-
<span className={style.sidebarLabel}>{t('sidebar.settings')}</span>
269+
<span className={style.sidebarLabel}>
270+
{t('sidebar.settings')}
271+
{canUpgrade && (
272+
<RedDot className={style.canUpgradeDot} width={8} height={8} />
273+
)}
274+
</span>
247275
</NavLink>
248276
</div>
249277

frontends/web/src/routes/settings/components/tabs.module.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@
2323
}
2424
}
2525

26+
.canUpgradeDot {
27+
vertical-align: top;
28+
}

frontends/web/src/routes/settings/components/tabs.tsx

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
import { ReactNode } from 'react';
1818
import { NavLink } from 'react-router-dom';
19+
import { useTranslation } from 'react-i18next';
20+
import { useLoad } from '../../../hooks/api';
21+
import { getVersion } from '../../../api/bitbox02';
1922
import { route } from '../../../utils/route';
2023
import { SettingsItem } from './settingsItem/settingsItem';
21-
import { ChevronRightDark } from '../../../components/icon';
22-
import { useTranslation } from 'react-i18next';
24+
import { ChevronRightDark, RedDot } from '../../../components/icon';
2325
import styles from './tabs.module.css';
2426

2527
type TWithSettingsTabsProps = {
@@ -33,6 +35,7 @@ type TTab = {
3335
name: string;
3436
url: string;
3537
hideMobileMenu?: boolean;
38+
canUpgrade?: boolean;
3639
}
3740

3841
type TTabs = {
@@ -57,7 +60,16 @@ export const WithSettingsTabs = ({
5760
);
5861
};
5962

60-
export const Tab = ({ name, url, hideMobileMenu }: TTab) => {
63+
export const Tab = ({
64+
name,
65+
url,
66+
hideMobileMenu,
67+
canUpgrade,
68+
}: TTab) => {
69+
70+
const upgradeDot = canUpgrade ? (
71+
<RedDot className={styles.canUpgradeDot} width={8} height={8} />
72+
) : null;
6173

6274
if (!hideMobileMenu) {
6375
// Will only be shown on mobile (index/general settings page)
@@ -67,6 +79,7 @@ export const Tab = ({ name, url, hideMobileMenu }: TTab) => {
6779
settingName={name}
6880
onClick={() => route(url)}
6981
extraComponent={<ChevronRightDark/>} />
82+
{upgradeDot}
7083
</div>
7184
);
7285
}
@@ -77,18 +90,41 @@ export const Tab = ({ name, url, hideMobileMenu }: TTab) => {
7790
to={url}
7891
key={url}>
7992
{name}
93+
{upgradeDot}
8094
</NavLink>
8195
);
8296
};
8397

98+
type TTabWithVersionCheck = TTab & {
99+
deviceID: string;
100+
}
101+
102+
const TabWithVersionCheck = ({ deviceID, ...props }: TTabWithVersionCheck) => {
103+
104+
const versionInfo = useLoad(() => getVersion(deviceID), [deviceID]);
105+
106+
return (
107+
<Tab
108+
canUpgrade={versionInfo ? versionInfo.canUpgrade : false}
109+
{...props}
110+
/>
111+
);
112+
};
113+
84114
export const Tabs = ({ deviceIDs, hideMobileMenu, hasAccounts }: TTabs) => {
85115
const { t } = useTranslation();
86116
return (
87117
<div className={styles.container}>
88118
<Tab key="appearance" hideMobileMenu={hideMobileMenu} name={t('settings.appearance')} url="/settings/appearance" />
89119
{hasAccounts ? <Tab key="manage-accounts" hideMobileMenu={hideMobileMenu} name={t('manageAccounts.title')} url="/settings/manage-accounts" /> : null}
90120
{deviceIDs.map(id => (
91-
<Tab hideMobileMenu={hideMobileMenu} name={t('sidebar.device')} key={`device-${id}`} url={`/settings/device-settings/${id}`} />
121+
<TabWithVersionCheck
122+
key={`device-${id}`}
123+
deviceID={id}
124+
hideMobileMenu={hideMobileMenu}
125+
name={t('sidebar.device')}
126+
url={`/settings/device-settings/${id}`}
127+
/>
92128
)) }
93129
<Tab key="advanced-settings" hideMobileMenu={hideMobileMenu} name={t('settings.advancedSettings')} url="/settings/advanced-settings" />
94130
<Tab key="about" hideMobileMenu={hideMobileMenu} name={t('settings.about')} url="/settings/about" />

0 commit comments

Comments
 (0)