-
Notifications
You must be signed in to change notification settings - Fork 107
frontend: add backup reminder banner. #3446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
/** | ||
* Copyright 2025 Shift Crypto AG | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import { apiGet } from '@/utils/request'; | ||
|
||
export type TShowBackupBannerResponse = { | ||
success: boolean; | ||
show?: boolean; | ||
fiat?: string; | ||
} | ||
|
||
export const getShowBackupBanner = (rootFingerprint: string): Promise<TShowBackupBannerResponse> => { | ||
return apiGet(`keystore/show-backup-banner/${rootFingerprint}`); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/** | ||
* Copyright 2025 Shift Crypto AG | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
import { MouseEvent } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { getDeviceList } from '@/api/devices'; | ||
import { Link, useNavigate } from 'react-router-dom'; | ||
import { connectKeystore } from '@/api/account'; | ||
import { Status } from '@/components/status/status'; | ||
import { MultilineMarkup } from '@/utils/markup'; | ||
import { TKeystore } from '@/api/account'; | ||
import { AppContext } from '@/contexts/AppContext'; | ||
import { getShowBackupBanner, TShowBackupBannerResponse } from '@/api/backupBanner'; | ||
import { useContext, useEffect, useState } from 'react'; | ||
import { TAccountsBalanceSummary } from '@/api/account'; | ||
|
||
type BackupReminderProps = { | ||
keystore: TKeystore; | ||
accountsBalanceSummary?: TAccountsBalanceSummary; | ||
accountCode: string; | ||
} | ||
|
||
export const BackupReminder = ({ keystore, accountsBalanceSummary, accountCode }: BackupReminderProps) => { | ||
const { t } = useTranslation(); | ||
const [bannerResponse, setBannerResponse] = useState<TShowBackupBannerResponse | null>(null); | ||
const { hideAmounts } = useContext(AppContext); | ||
const navigate = useNavigate(); | ||
|
||
useEffect(() => { | ||
getShowBackupBanner(keystore.rootFingerprint).then(setBannerResponse); | ||
}, [keystore.rootFingerprint, accountsBalanceSummary]); | ||
|
||
|
||
if (hideAmounts) { | ||
// If amounts are hidden, we don't show the backup reminder. | ||
return; | ||
} | ||
|
||
if (!bannerResponse || !bannerResponse.success) { | ||
return null; | ||
} | ||
|
||
const maybeNavigateToSettings = async (e: MouseEvent<HTMLAnchorElement>) => { | ||
e.preventDefault(); | ||
const connectResult = await connectKeystore(accountCode); | ||
if (connectResult.success) { | ||
const devices = await getDeviceList(); | ||
const deviceSettingsURL = `/settings/device-settings/${Object.keys(devices)[0]}`; | ||
// Proceed to the setting screen if the keystore was connected. | ||
navigate(deviceSettingsURL); | ||
} | ||
}; | ||
|
||
return ( | ||
<Status | ||
type="info" | ||
hidden={!bannerResponse.show} | ||
dismissible={`banner-backup-${keystore.rootFingerprint}`}> | ||
<MultilineMarkup | ||
tagName="span" | ||
withBreaks | ||
markup={t('account.backupReminder', | ||
{ | ||
name: keystore.name, | ||
fiat: bannerResponse.fiat, | ||
})} | ||
/> | ||
<Link to="#" onClick={maybeNavigateToSettings} >{t('account.backupReminderLink')} </Link> | ||
</Status> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ import { AppContext } from '@/contexts/AppContext'; | |
import { getAccountsByKeystore, isAmbiguousName } from '@/routes/account/utils'; | ||
import { RatesContext } from '@/contexts/RatesContext'; | ||
import { ContentWrapper } from '@/components/contentwrapper/contentwrapper'; | ||
import { BackupReminder } from '@/components/banners/backup'; | ||
|
||
type TProps = { | ||
accounts: accountApi.IAccount[]; | ||
|
@@ -169,6 +170,10 @@ export const AccountsSummary = ({ | |
getAccountsBalanceSummary(); | ||
}, [onStatusChanged, getAccountsBalanceSummary, accounts]); | ||
|
||
useEffect(() => { | ||
getAccountsBalanceSummary(); | ||
}, [defaultCurrency, getAccountsBalanceSummary]); | ||
|
||
return ( | ||
<GuideWrapper> | ||
<GuidedContent> | ||
|
@@ -177,6 +182,14 @@ export const AccountsSummary = ({ | |
<Status hidden={!hasCard} type="warning"> | ||
{t('warning.sdcard')} | ||
</Status> | ||
{accountsByKeystore.map(({ keystore, accounts }) => ( | ||
<BackupReminder | ||
key={keystore.rootFingerprint} | ||
keystore={keystore} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't actually need the total in the banner, because the total is only used by the backend to determine whether the banner needs to be displayed. but yes, I need to revert to USD as well. (also, AFAIK |
||
accountsBalanceSummary={accountsBalanceSummary} | ||
accountCode={accounts[0].code} | ||
/> | ||
))} | ||
</ContentWrapper> | ||
<Header title={<h2>{t('accountSummary.title')}</h2>}> | ||
<HideAmountsButton /> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change this "1000" to a lower value for testing