Skip to content

Commit 65ca68d

Browse files
committed
Merge branch 'frontend-offline-error-component'
2 parents 4977ac9 + 62cff22 commit 65ca68d

File tree

4 files changed

+66
-22
lines changed

4 files changed

+66
-22
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Copyright 2025 Shift Crypto AG
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { useEffect, useState } from 'react';
18+
import { useTranslation } from 'react-i18next';
19+
import { Message } from '../message/message';
20+
import { getConfig } from '@/utils/config';
21+
import style from './offline-errors.module.css';
22+
23+
type Props = {
24+
error?: string | null;
25+
}
26+
27+
export const OfflineError = ({
28+
error,
29+
}: Props) => {
30+
31+
const { t } = useTranslation();
32+
const [usesProxy, setUsesProxy] = useState<boolean>();
33+
34+
useEffect(() => {
35+
getConfig().then(({ backend }) => setUsesProxy(backend.proxy.useProxy));
36+
}, []);
37+
38+
// Status: offline error
39+
const offlineErrorTextLines: string[] = [];
40+
if (error) {
41+
offlineErrorTextLines.push(t('account.reconnecting')); // Lost connection, trying to reconnect…
42+
offlineErrorTextLines.push(error);
43+
if (usesProxy) {
44+
offlineErrorTextLines.push(t('account.maybeProxyError'));
45+
}
46+
}
47+
48+
if (!error) {
49+
return null;
50+
}
51+
52+
return (
53+
<Message type="error" className={style.status}>
54+
{offlineErrorTextLines.join('\n')}
55+
</Message>
56+
);
57+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.status {
2+
white-space: pre-line;
3+
}

frontends/web/src/routes/account/account.tsx

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import { isBitcoinBased } from './utils';
4040
import { MultilineMarkup } from '@/utils/markup';
4141
import { Dialog } from '@/components/dialog/dialog';
4242
import { A } from '@/components/anchor/anchor';
43-
import { getConfig } from '@/utils/config';
4443
import { i18n } from '@/i18n/i18n';
4544
import { ContentWrapper } from '@/components/contentwrapper/contentwrapper';
4645
import { GlobalBanners } from '@/components/banners';
@@ -51,6 +50,7 @@ import { Button, Input } from '@/components/forms';
5150
import { SubTitle } from '@/components/title';
5251
import { TransactionHistorySkeleton } from '@/routes/account/transaction-history-skeleton';
5352
import { RatesContext } from '@/contexts/RatesContext';
53+
import { OfflineError } from '@/components/banners/offline-error';
5454
import style from './account.module.css';
5555

5656
type Props = {
@@ -95,7 +95,6 @@ const RemountAccount = ({
9595
);
9696
const syncedAddressesCount = useSubscribe(syncAddressesCount(code));
9797
const [transactions, setTransactions] = useState<accountApi.TTransactions>();
98-
const [usesProxy, setUsesProxy] = useState<boolean>();
9998
const [detailID, setDetailID] = useState<accountApi.ITransaction['internalID'] | null>(null);
10099
const [showSearchBar, setShowSearchBar] = useState<boolean>(false);
101100
const [searchTerm, setSearchTerm] = useState<string>('');
@@ -133,10 +132,6 @@ const RemountAccount = ({
133132
});
134133
}, [transactions, debouncedSearchTerm]);
135134

136-
useEffect(() => {
137-
getConfig().then(({ backend }) => setUsesProxy(backend.proxy.useProxy));
138-
}, []);
139-
140135
const onAccountChanged = useCallback((status: accountApi.IStatus | undefined) => {
141136
if (status === undefined || status.fatalError) {
142137
return;
@@ -190,16 +185,6 @@ const RemountAccount = ({
190185
);
191186
}
192187

193-
// Status: offline error
194-
const offlineErrorTextLines: string[] = [];
195-
if (status !== undefined && status.offlineError !== null) {
196-
offlineErrorTextLines.push(t('account.reconnecting'));
197-
offlineErrorTextLines.push(status.offlineError);
198-
if (usesProxy) {
199-
offlineErrorTextLines.push(t('account.maybeProxyError'));
200-
}
201-
}
202-
203188
// Status: not synced
204189
const notSyncedText = (status !== undefined && !status.synced && syncedAddressesCount !== undefined && syncedAddressesCount > 1) ? (
205190
'\n' + t('account.syncedAddressesCount', {
@@ -232,13 +217,8 @@ const RemountAccount = ({
232217
<GuidedContent>
233218
<Main>
234219
<ContentWrapper>
220+
<OfflineError error={status?.offlineError} />
235221
<GlobalBanners code={code} devices={devices} />
236-
<Message
237-
className={style.status}
238-
hidden={status === undefined || !status.offlineError}
239-
type="error">
240-
{offlineErrorTextLines.join('\n')}
241-
</Message>
242222
<Message
243223
className={style.status}
244224
hidden={status === undefined || status.synced || !!status.offlineError}

frontends/web/src/routes/account/summary/accountssummary.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { RatesContext } from '@/contexts/RatesContext';
3838
import { ContentWrapper } from '@/components/contentwrapper/contentwrapper';
3939
import { GlobalBanners } from '@/components/banners';
4040
import { BackupReminder } from '@/components/banners/backup';
41+
import { OfflineError } from '@/components/banners/offline-error';
4142

4243
type TProps = {
4344
accounts: accountApi.IAccount[];
@@ -63,6 +64,7 @@ export const AccountsSummary = ({
6364
const [chartData, setChartData] = useState<accountApi.TChartData>();
6465
const [accountsBalanceSummary, setAccountsBalanceSummary] = useState<accountApi.TAccountsBalanceSummary>();
6566
const [balances, setBalances] = useState<Balances>();
67+
const [offlineError, setOfflineError] = useState<string | null>(null);
6668

6769
const getChartData = useCallback(async () => {
6870
// replace previous timer if present
@@ -98,6 +100,7 @@ export const AccountsSummary = ({
98100
if (status.disabled || !mounted.current) {
99101
return;
100102
}
103+
setOfflineError(status.offlineError);
101104
if (!status.synced) {
102105
return accountApi.init(code);
103106
}
@@ -176,6 +179,7 @@ export const AccountsSummary = ({
176179
<GuidedContent>
177180
<Main>
178181
<ContentWrapper>
182+
<OfflineError error={offlineError} />
179183
<GlobalBanners devices={devices} />
180184
{accountsByKeystore.map(({ keystore }) => (
181185
<BackupReminder

0 commit comments

Comments
 (0)