diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index 69ed85e47df9..9dc3458f82aa 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -261,6 +261,7 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "rewardsOptInHeader", IDS_REWARDS_WIDGET_OPT_IN_HEADER }, { "rewardsOptInTerms", IDS_BRAVE_REWARDS_ONBOARDING_TERMS }, { "rewardsOptInText", IDS_REWARDS_WIDGET_OPT_IN_TEXT }, + { "rewardsLogInToSeeBalance", IDS_REWARDS_LOG_IN_TO_SEE_BALANCE }, { "rewardsPaymentCheckStatus", IDS_REWARDS_PAYMENT_CHECK_STATUS }, { "rewardsPaymentCompleted", IDS_REWARDS_PAYMENT_COMPLETED }, { "rewardsPaymentPending", IDS_REWARDS_PAYMENT_PENDING }, @@ -697,6 +698,7 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "rewardsContribute", IDS_BRAVE_UI_REWARDS_CONTRIBUTE }, { "rewardsContributeAttention", IDS_BRAVE_UI_REWARDS_CONTRIBUTE_ATTENTION }, // NOLINT { "rewardsGrantDaysRemaining", IDS_REWARDS_WIDGET_GRANT_DAYS_REMAINING }, // NOLINT + { "rewardsLogInToSeeBalance", IDS_REWARDS_LOG_IN_TO_SEE_BALANCE }, { "rewardsPaymentCheckStatus", IDS_REWARDS_PAYMENT_CHECK_STATUS }, { "rewardsPaymentCompleted", IDS_REWARDS_PAYMENT_COMPLETED }, { "rewardsPaymentPending", IDS_REWARDS_PAYMENT_PENDING }, diff --git a/components/brave_new_tab_ui/api/initialData.ts b/components/brave_new_tab_ui/api/initialData.ts index cad4525a8633..b61b9e5d3809 100644 --- a/components/brave_new_tab_ui/api/initialData.ts +++ b/components/brave_new_tab_ui/api/initialData.ts @@ -31,6 +31,7 @@ export type PreInitialRewardsData = { export type InitialRewardsData = { report: NewTab.RewardsBalanceReport balance: NewTab.RewardsBalance + externalWallet?: RewardsExtension.ExternalWallet adsAccountStatement: NewTab.AdsAccountStatement parameters: NewTab.RewardsParameters } @@ -131,7 +132,8 @@ export async function getRewardsInitialData (): Promise { adsAccountStatement, report, balance, - parameters + parameters, + externalWallet ] = await Promise.all([ new Promise(resolve => chrome.braveRewards.getAdsAccountStatement((success: boolean, adsAccountStatement: NewTab.AdsAccountStatement) => { resolve(success ? adsAccountStatement : undefined) @@ -145,6 +147,9 @@ export async function getRewardsInitialData (): Promise { new Promise(resolve => chrome.braveRewards.getRewardsParameters((parameters: NewTab.RewardsParameters) => { resolve(parameters) })), + new Promise(resolve => { + chrome.braveRewards.getExternalWallet((_, wallet) => resolve(wallet)) + }), new Promise(resolve => { chrome.braveRewards.fetchPromotions() resolve(true) @@ -154,7 +159,8 @@ export async function getRewardsInitialData (): Promise { adsAccountStatement, report, balance, - parameters + parameters, + externalWallet } as InitialRewardsData } catch (err) { throw Error(err) diff --git a/components/brave_new_tab_ui/components/default/rewards/index.tsx b/components/brave_new_tab_ui/components/default/rewards/index.tsx index 0581a2ec3e99..9a8ef1bfb7ef 100644 --- a/components/brave_new_tab_ui/components/default/rewards/index.tsx +++ b/components/brave_new_tab_ui/components/default/rewards/index.tsx @@ -12,6 +12,7 @@ import { LocaleContext } from '../../../../brave_rewards/resources/shared/lib/lo import { WithThemeVariables } from '../../../../brave_rewards/resources/shared/components/with_theme_variables' import { GrantInfo } from '../../../../brave_rewards/resources/shared/lib/grant_info' import { OnboardingCompletedStore } from '../../../../brave_rewards/resources/shared/lib/onboarding_completed_store' +import { externalWalletFromExtensionData } from '../../../../brave_rewards/resources/shared/lib/external_wallet' import { RewardsCard, @@ -45,6 +46,7 @@ export interface RewardsProps { rewardsEnabled: boolean enabledAds: boolean balance: NewTab.RewardsBalance + externalWallet?: RewardsExtension.ExternalWallet report?: NewTab.RewardsBalanceReport adsAccountStatement: NewTab.AdsAccountStatement parameters: NewTab.RewardsParameters @@ -105,6 +107,7 @@ export const RewardsWidget = createWidget((props: RewardsProps) => { exchangeCurrency='USD' exchangeRate={props.parameters.rate} grantInfo={grantInfo} + externalWallet={externalWalletFromExtensionData(props.externalWallet)} nextPaymentDate={adsInfo ? adsInfo.nextPaymentDate : 0} earningsThisMonth={adsInfo ? adsInfo.earningsThisMonth : 0} earningsLastMonth={adsInfo ? adsInfo.earningsLastMonth : 0} diff --git a/components/brave_new_tab_ui/reducers/rewards_reducer.ts b/components/brave_new_tab_ui/reducers/rewards_reducer.ts index 4b485b10cae8..cceed50e0612 100644 --- a/components/brave_new_tab_ui/reducers/rewards_reducer.ts +++ b/components/brave_new_tab_ui/reducers/rewards_reducer.ts @@ -96,6 +96,7 @@ const rewardsReducer: Reducer = (state: NewTab.State, const initialRewardsDataPayload = payload as InitialRewardsData const newRewardsState = { balance: initialRewardsDataPayload.balance, + externalWallet: initialRewardsDataPayload.externalWallet, report: initialRewardsDataPayload.report, totalContribution: getTotalContributions(initialRewardsDataPayload.report), parameters: initialRewardsDataPayload.parameters diff --git a/components/brave_new_tab_ui/storage/new_tab_storage.ts b/components/brave_new_tab_ui/storage/new_tab_storage.ts index ab27e5e8dc52..ff9102c132e3 100644 --- a/components/brave_new_tab_ui/storage/new_tab_storage.ts +++ b/components/brave_new_tab_ui/storage/new_tab_storage.ts @@ -70,6 +70,7 @@ export const defaultState: NewTab.State = { total: 0, wallets: {} }, + externalWallet: undefined, dismissedNotifications: [], rewardsEnabled: false, enabledAds: false, diff --git a/components/brave_rewards/resources/extension/brave_rewards/_locales/en_US/messages.json b/components/brave_rewards/resources/extension/brave_rewards/_locales/en_US/messages.json index 3799810dfae2..0338aefb5982 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/_locales/en_US/messages.json +++ b/components/brave_rewards/resources/extension/brave_rewards/_locales/en_US/messages.json @@ -135,6 +135,21 @@ "message": "Brave Rewards is having an issue. Please try again later.", "description": "" }, + "rewardsLogInToSeeBalance": { + "message": "$begin_bold$Log in to $provider$$end_bold$ to see your balance", + "description": "", + "placeholders": { + "begin_bold": { + "content": "$1" + }, + "provider": { + "content": "$2" + }, + "end_bold": { + "content": "$3" + } + } + }, "rewardsPaymentCheckStatus": { "message": "Check status", "description": "" diff --git a/components/brave_rewards/resources/rewards_panel/lib/extension_api_adapter.ts b/components/brave_rewards/resources/rewards_panel/lib/extension_api_adapter.ts index 8f9a6131726e..57f5c9b3678f 100644 --- a/components/brave_rewards/resources/rewards_panel/lib/extension_api_adapter.ts +++ b/components/brave_rewards/resources/rewards_panel/lib/extension_api_adapter.ts @@ -83,25 +83,13 @@ export function getRewardsParameters () { }) } -const externalWalletLoginURLs = new Map() - -export function getExternalWalletLoginURL (provider: ExternalWalletProvider) { - return new Promise((resolve) => { - resolve(externalWalletLoginURLs.get(provider) || '') - }) -} - export function getExternalWalletProviders () { return new Promise((resolve) => { // The extension API currently does not support retrieving a list of // external wallet providers. Instead, use the `getExternalWallet` function - // to retrieve the "currently selected" provider and save the OAuth URL for - // that provider. + // to retrieve the "currently selected" provider. chrome.braveRewards.getExternalWallet((_, wallet) => { const provider = wallet && externalWalletProviderFromString(wallet.type) - if (provider && wallet && wallet.loginUrl) { - externalWalletLoginURLs.set(provider, wallet.loginUrl) - } resolve(provider ? [provider] : []) }) }) diff --git a/components/brave_rewards/resources/rewards_panel/lib/extension_host.ts b/components/brave_rewards/resources/rewards_panel/lib/extension_host.ts index 3d0eaea84f96..2ebfecf8dc75 100644 --- a/components/brave_rewards/resources/rewards_panel/lib/extension_host.ts +++ b/components/brave_rewards/resources/rewards_panel/lib/extension_host.ts @@ -132,6 +132,31 @@ export function createHost (): Host { }) } + function getExternalWalletActionURL (action: ExternalWalletAction) { + const verifyURL = 'chrome://rewards#verify' + + const { externalWallet } = stateManager.getState() + if (!externalWallet) { + return verifyURL + } + + const { links } = externalWallet + switch (action) { + case 'add-funds': + return links.addFunds || links.account || '' + case 'complete-verification': + return links.completeVerification || links.account || '' + case 'reconnect': + return links.reconnect || '' + case 'verify': + return verifyURL + case 'view-account': + return links.account || '' + case 'disconnect': + return '' + } + } + function handleExternalWalletAction (action: ExternalWalletAction) { const { externalWallet } = stateManager.getState() @@ -143,26 +168,13 @@ export function createHost (): Host { return } - Promise.resolve().then(() => { - const verifyURL = 'chrome://rewards#verify' - if (!externalWallet) { - return verifyURL - } + const url = getExternalWalletActionURL(action) + if (!url) { + console.error(`Action URL does not exist for '${action}`) + return + } - const { links } = externalWallet - switch (action) { - case 'add-funds': - return links.addFunds || links.account - case 'complete-verification': - return links.completeVerification || links.account - case 'reconnect': - return apiAdapter.getExternalWalletLoginURL(externalWallet.provider) - case 'verify': - return verifyURL - case 'view-account': - return links.account - } - }).then(openTab, console.error) + openTab(url) } function handleStartupParameters () { diff --git a/components/brave_rewards/resources/shared/components/icons/arrow_circle_icon.tsx b/components/brave_rewards/resources/shared/components/icons/arrow_circle_icon.tsx new file mode 100644 index 000000000000..ba3ae7f4895c --- /dev/null +++ b/components/brave_rewards/resources/shared/components/icons/arrow_circle_icon.tsx @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react' + +export function ArrowCircleIcon () { + return ( + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/newtab/rewards_card.style.ts b/components/brave_rewards/resources/shared/components/newtab/rewards_card.style.ts index a74fbf9d0ba9..fbb40aaf0543 100644 --- a/components/brave_rewards/resources/shared/components/newtab/rewards_card.style.ts +++ b/components/brave_rewards/resources/shared/components/newtab/rewards_card.style.ts @@ -75,6 +75,31 @@ export const terms = styled.div` } ` +export const disconnected = styled.div` + margin-top: 20px; + padding: 16px; + font-size: 14px; + line-height: 20px; + background: linear-gradient(137.04deg, #346FE1 33.4%, #5844C3 82.8%); + border-radius: 8px; + cursor: pointer; + + strong { + font-weight: 600; + } +` + +export const disconnectedArrow = styled.div` + text-align: right; + line-height: 15px; + + .icon { + vertical-align: middle; + width: 21px; + height: auto; + } +` + export const balance = styled.div` margin-top: 16px; font-size: 12px; diff --git a/components/brave_rewards/resources/shared/components/newtab/rewards_card.tsx b/components/brave_rewards/resources/shared/components/newtab/rewards_card.tsx index 723b84c67a68..91d35da7da62 100644 --- a/components/brave_rewards/resources/shared/components/newtab/rewards_card.tsx +++ b/components/brave_rewards/resources/shared/components/newtab/rewards_card.tsx @@ -6,6 +6,8 @@ import * as React from 'react' import { LocaleContext, formatMessage } from '../../lib/locale_context' import { GrantInfo } from '../../lib/grant_info' +import { ExternalWallet, getExternalWalletProviderName } from '../../lib/external_wallet' +import { ArrowCircleIcon } from '../icons/arrow_circle_icon' import { BatIcon } from '../icons/bat_icon' import { SettingsIcon } from '../icons/settings_icon' import { InfoIcon } from './icons/info_icon' @@ -71,6 +73,7 @@ interface Props { earningsReceived: boolean contributionsThisMonth: number grantInfo: GrantInfo | null + externalWallet: ExternalWallet | null onEnableRewards: () => void onEnableAds: () => void onClaimGrant: () => void @@ -89,6 +92,32 @@ export function RewardsCard (props: Props) { ) } + const { externalWallet } = props + if (externalWallet && externalWallet.status === 'disconnected') { + const onClick = () => { + if (externalWallet.links.reconnect) { + window.open(externalWallet.links.reconnect, '_blank') + } + } + return ( + + { + formatMessage(getString('rewardsLogInToSeeBalance'), { + placeholders: { + $2: getExternalWalletProviderName(externalWallet.provider) + }, + tags: { + $1: (content) => {content} + } + }) + } + + + + + ) + } + const showPending = shouldRenderPendingRewards( props.earningsLastMonth, props.nextPaymentDate) diff --git a/components/brave_rewards/resources/shared/components/newtab/stories/index.tsx b/components/brave_rewards/resources/shared/components/newtab/stories/index.tsx index b45245eb118a..c6e600936b54 100644 --- a/components/brave_rewards/resources/shared/components/newtab/stories/index.tsx +++ b/components/brave_rewards/resources/shared/components/newtab/stories/index.tsx @@ -31,6 +31,7 @@ export function Card () { const nextPaymentDate = Date.now() + 1000 * 60 * 60 * 24 * daysUntilPayment const showGrant = knobs.boolean('Grant Available', false) const earningsReceived = knobs.boolean('Earnings received', false) + const disconnectedWallet = knobs.boolean('Disconnected', false) return ( @@ -51,6 +52,14 @@ export function Card () { claimableUntil: Date.now() + 1000 * 10 * 24 * 60 * 60, expiresAt: Date.now() + 1000 * 10 * 24 * 60 * 60 } : null} + externalWallet={disconnectedWallet ? { + provider: 'uphold', + status: 'disconnected', + username: '', + links: { + reconnect: 'https://brave.com' + } + } : null} nextPaymentDate={nextPaymentDate} earningsThisMonth={0.142} earningsLastMonth={1.25} diff --git a/components/brave_rewards/resources/shared/components/newtab/stories/locale_strings.ts b/components/brave_rewards/resources/shared/components/newtab/stories/locale_strings.ts index 5d5bf6c77633..0f97aee98f65 100644 --- a/components/brave_rewards/resources/shared/components/newtab/stories/locale_strings.ts +++ b/components/brave_rewards/resources/shared/components/newtab/stories/locale_strings.ts @@ -14,6 +14,7 @@ export const localeStrings = { rewardsGiving: 'Giving', rewardsGrantDaysRemaining: 'You have $1 left to claim', rewardsLearnMore: 'Learn more', + rewardsLogInToSeeBalance: '$1Log in to $2$3 to see your balance', rewardsOptInHeader: 'Earn tokens and give back', rewardsOptInTerms: 'By proceeding, you agree to the $1Terms of Service$2 and $3Privacy Policy$4.', rewardsOptInText: 'Earn tokens by viewing Brave Private Ads and support content creators automatically.', diff --git a/components/brave_rewards/resources/shared/components/wallet_card/stories/index.tsx b/components/brave_rewards/resources/shared/components/wallet_card/stories/index.tsx index ece4e4793253..2bd6ba578d6b 100644 --- a/components/brave_rewards/resources/shared/components/wallet_card/stories/index.tsx +++ b/components/brave_rewards/resources/shared/components/wallet_card/stories/index.tsx @@ -44,7 +44,9 @@ export function Wallet () { const externalWallet: ExternalWallet = { provider: 'uphold', - status: 'verified', + status: knobs.boolean('Wallet disconnected', false) + ? 'disconnected' + : 'verified', username: 'brave123', links: {} } diff --git a/components/brave_rewards/resources/shared/components/wallet_card/stories/locale_strings.ts b/components/brave_rewards/resources/shared/components/wallet_card/stories/locale_strings.ts index a73343b13a1b..8b7d0f0a51a0 100644 --- a/components/brave_rewards/resources/shared/components/wallet_card/stories/locale_strings.ts +++ b/components/brave_rewards/resources/shared/components/wallet_card/stories/locale_strings.ts @@ -1,4 +1,5 @@ export const localeStrings = { + rewardsLogInToSeeBalance: '$1Log in to $2$3 to see your balance', rewardsPaymentCheckStatus: 'Check status', rewardsPaymentCompleted: 'Congrats! Your $1 rewards have arrived!', rewardsPaymentPending: 'Your $1 $2 rewards will arrive in $3', diff --git a/components/brave_rewards/resources/shared/components/wallet_card/wallet_card.style.ts b/components/brave_rewards/resources/shared/components/wallet_card/wallet_card.style.ts index 19cfc134d425..eb6258da7f35 100644 --- a/components/brave_rewards/resources/shared/components/wallet_card/wallet_card.style.ts +++ b/components/brave_rewards/resources/shared/components/wallet_card/wallet_card.style.ts @@ -11,21 +11,68 @@ export const root = styled.div` color: var(--brave-palette-white); ` -export const overview = styled.div` - display: flex; - align-items: flex-end; - margin-bottom: 7px; +export const grid = styled.div` padding: 13px 13px 0; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto; + grid-template-areas: + "status-indicator earnings" + "balance earnings" + "add-funds view-statement"; ` -export const balancePanel = styled.div` - flex: 1 1 50%; - padding-right: 5px; +export const statusIndicator = styled.div` + grid-area: status-indicator; + justify-self: start; + align-self: start; ` export const rewardsBalance = styled.div` - margin-left: 20px; - margin-top: 6px; + grid-area: balance; + justify-self: start; + align-self: end; + margin: 6px 5px 0 20px; +` + +export const disconnectedBalance = styled.div` + grid-area: balance; + justify-self: start; + align-self: start; + margin: 6px 15px 0 4px; + padding: 12px; + border-radius: 8px; + background: linear-gradient(137.04deg, #346FE1 33.4%, #5844C3 82.8%); + font-size: 12px; + line-height: 18px; + cursor: pointer; + position: relative; + + strong { + font-weight: 600; + color: #fff; + } + + .icon { + display: none; + width: 20px; + height: 20px; + position: absolute; + bottom: 1em; + right: 1em; + } + + &.cover-actions { + grid-area: balance-start / balance-start / add-funds-end / balance-end; + align-self: stretch; + font-size: 14px; + line-height: 20px; + padding-bottom: 32px; + + .icon { + display: block; + } + } ` export const balanceHeader = styled.div` @@ -54,8 +101,10 @@ export const exchangeAmount = styled.div` ` export const earningsPanel = styled.div` - flex: 1 1 50%; - padding-left: 5px; + grid-area: earnings; + justify-content: start; + align-self: end; + margin-top: 20px; ` export const dateRange = styled.div` @@ -71,30 +120,24 @@ export const earningsHeader = styled.div` opacity: 0.65; ` -export const summaryBox = styled.div` - margin-top: 17px; - padding: 0 17px 16px; -` - -export const summaryActions = styled.div` - margin: 0 9px 24px 13px; - display: flex; - justify-content: space-between; - - button { - font-weight: 600; - font-size: 13px; - line-height: 20px; - padding: 6px 18px; - border-radius: 48px; - border: none; - background: transparent; - cursor: pointer; - } +const summaryActionButton = ` + font-weight: 600; + font-size: 13px; + line-height: 20px; + padding: 6px 18px; + border-radius: 48px; + border: none; + background: transparent; + cursor: pointer; ` export const addFunds = styled.div` + grid-area: add-funds; + margin-top: 9px; + margin-left: 9px; + button { + ${summaryActionButton} background: rgba(255, 255, 255, 0.24); &:hover { @@ -112,7 +155,14 @@ export const addFunds = styled.div` ` export const viewStatement = styled.div` + grid-area: view-statement; + align-self: center; + justify-self: end; + margin-top: 9px; + margin-right: 9px; + button { + ${summaryActionButton} padding: 6px 13px; } @@ -125,6 +175,10 @@ export const viewStatement = styled.div` } ` +export const summaryBox = styled.div` + padding: 17px 17px 16px; +` + export const pendingBox = styled.div` margin-top: 17px; border-radius: 0 0 14px 14px; diff --git a/components/brave_rewards/resources/shared/components/wallet_card/wallet_card.tsx b/components/brave_rewards/resources/shared/components/wallet_card/wallet_card.tsx index d63b33dcf9ef..81f27b85f095 100644 --- a/components/brave_rewards/resources/shared/components/wallet_card/wallet_card.tsx +++ b/components/brave_rewards/resources/shared/components/wallet_card/wallet_card.tsx @@ -4,8 +4,8 @@ import * as React from 'react' -import { LocaleContext } from '../../lib/locale_context' -import { ExternalWallet } from '../../lib/external_wallet' +import { LocaleContext, formatMessage } from '../../lib/locale_context' +import { ExternalWallet, getExternalWalletProviderName } from '../../lib/external_wallet' import { TokenAmount } from '../token_amount' import { ExchangeAmount } from '../exchange_amount' @@ -15,6 +15,7 @@ import { RewardsSummary, RewardsSummaryData } from './rewards_summary' import { PendingRewardsView } from './pending_rewards_view' import { PlusIcon } from './icons/plus_icon' import { WalletInfoIcon } from './icons/wallet_info_icon' +import { ArrowCircleIcon } from '../icons/arrow_circle_icon' import * as style from './wallet_card.style' @@ -48,34 +49,68 @@ export function getCurrentMonthRange () { export function WalletCard (props: Props) { const { getString } = React.useContext(LocaleContext) + const { externalWallet } = props + + const walletDisconnected = + externalWallet && externalWallet.status === 'disconnected' function onAddFundsClick () { props.onExternalWalletAction('add-funds') } + function renderBalance () { + if (externalWallet && walletDisconnected) { + const onReconnectClick = () => { + props.onExternalWalletAction('reconnect') + } + + const coverActions = props.showSummary && props.onViewStatement + + return ( + + { + formatMessage(getString('rewardsLogInToSeeBalance'), { + placeholders: { + $2: getExternalWalletProviderName(externalWallet.provider) + }, + tags: { + $1: (content) => {content} + } + }) + } + + + ) + } + + return ( + + + {getString('walletYourBalance')} + + + + + + + + + ) + } + return ( - - - + + + - - - {getString('walletYourBalance')} - - - - - - - - - + + {renderBalance()} {getCurrentMonthRange()} @@ -84,39 +119,40 @@ export function WalletCard (props: Props) { {getString('walletEstimatedEarnings')} - - - - ≈   - - + + + + ≈   + + - + { + props.showSummary && !walletDisconnected && + + + + } + { + props.showSummary && props.onViewStatement && + + + + } + { props.showSummary ? - - - - - { - props.onViewStatement && - - - - } - { links: { account: '', addFunds: '', - completeVerification: '' + completeVerification: '', + reconnect: '' } }) }) @@ -95,6 +96,12 @@ describe('external_wallet', () => { completeVerification: 'url' } }) + + expect(convert({ ...basicObject, loginUrl: 'url' })).toMatchObject({ + links: { + reconnect: 'url' + } + }) }) }) }) diff --git a/components/brave_rewards/resources/shared/lib/external_wallet.ts b/components/brave_rewards/resources/shared/lib/external_wallet.ts index b61ddd5074a3..53f26bf53199 100644 --- a/components/brave_rewards/resources/shared/lib/external_wallet.ts +++ b/components/brave_rewards/resources/shared/lib/external_wallet.ts @@ -20,6 +20,7 @@ export interface ExternalWallet { account?: string addFunds?: string completeVerification?: string + reconnect?: string } } @@ -98,7 +99,8 @@ export function externalWalletFromExtensionData ( links: { account: String(data.accountUrl || ''), addFunds: String(data.addUrl || ''), - completeVerification: String(data.verifyUrl || '') + completeVerification: String(data.verifyUrl || ''), + reconnect: String(data.loginUrl || '') } } } diff --git a/components/brave_rewards/resources/shared/lib/locale_context.test.ts b/components/brave_rewards/resources/shared/lib/locale_context.test.ts index 8a08dc5bd249..11763c5e69ba 100644 --- a/components/brave_rewards/resources/shared/lib/locale_context.test.ts +++ b/components/brave_rewards/resources/shared/lib/locale_context.test.ts @@ -31,10 +31,10 @@ describe('locale_context', () => { $1: (content) => `-${content}-`, $3: (content) => `=${content}=` } - })).toStrictEqual(['a ', '', '=x=', '', ' b ', '', '-y-', '', ' c']) + })).toStrictEqual(['a ', '=x=', ' b ', '-y-', ' c']) }) - it('fills placeholders and replacing tags', () => { + it('fills placeholders and replaces tags', () => { expect(formatMessage('a $1x$2 $3', { placeholders: { $3: 3 @@ -42,7 +42,18 @@ describe('locale_context', () => { tags: { $1: (content) => `-${content}-` } - })).toStrictEqual(['a ', '', '-x-', '', ' ', 3]) + })).toStrictEqual(['a ', '-x-', ' ', 3]) + }) + + it('fills placeholders nested within tags', () => { + expect(formatMessage('a $1 $2 $3 b', { + placeholders: { + $2: 2 + }, + tags: { + $1: (content) => ['-', ...content, '-'] + } + })).toStrictEqual(['a ', ['-', ' ', 2, ' ', '-'], ' b']) }) }) }) diff --git a/components/brave_rewards/resources/shared/lib/locale_context.ts b/components/brave_rewards/resources/shared/lib/locale_context.ts index ffa52a62f0cc..4b98a3c271e2 100644 --- a/components/brave_rewards/resources/shared/lib/locale_context.ts +++ b/components/brave_rewards/resources/shared/lib/locale_context.ts @@ -17,8 +17,8 @@ function splitMessage (message: string) { const slots: Array<[string, number]> = [] for (const match of message.matchAll(/([\s\S]*?)(\$\d|$)/g)) { - if (match[0]) { - parts.push(match[1] || '') + if (match[1]) { + parts.push(match[1]) } if (match[2]) { slots.push([match[2], parts.length]) @@ -72,21 +72,26 @@ export function formatMessage ( options = { placeholders } } + let tagKey = '' + let tagStart = -1 + for (const [key, index] of slots) { if (options.placeholders && key in options.placeholders) { parts[index] = options.placeholders[key] - } - - if (options.tags && key in options.tags) { - if (index < parts.length - 1) { - parts[index] = '' - parts[index + 1] = options.tags[key](parts[index + 1]) - if (index < parts.length - 2) { - parts[index + 2] = '' + } else if (options.tags) { + if (key in options.tags) { + tagKey = key + tagStart = index + } else if (tagStart >= 0) { + const content = parts.slice(tagStart + 1, index) + parts[tagStart] = options.tags[tagKey](content) + while (++tagStart <= index) { + parts[tagStart] = null } + tagStart = -1 } } } - return parts + return parts.filter(x => x != null) } diff --git a/components/definitions/newTab.d.ts b/components/definitions/newTab.d.ts index df167a9ac9da..56120df17013 100644 --- a/components/definitions/newTab.d.ts +++ b/components/definitions/newTab.d.ts @@ -151,6 +151,7 @@ declare namespace NewTab { rewardsEnabled: boolean adsSupported?: boolean balance: RewardsBalance + externalWallet?: RewardsExtension.ExternalWallet report?: RewardsBalanceReport adsAccountStatement: AdsAccountStatement dismissedNotifications: string[] diff --git a/components/resources/rewards_strings.grdp b/components/resources/rewards_strings.grdp index 495f9e283d7b..e3797c2c51fc 100644 --- a/components/resources/rewards_strings.grdp +++ b/components/resources/rewards_strings.grdp @@ -13,6 +13,10 @@ Your $1+1.5 BAT $2October rewards are on the way. Keep an eye out! + + $1Log in to $2Uphold$3 to see your balance + + Go to my $1Uphold account