diff --git a/browser/extensions/api/brave_rewards_api.cc b/browser/extensions/api/brave_rewards_api.cc index a221d64c9281..75a82d688e60 100644 --- a/browser/extensions/api/brave_rewards_api.cc +++ b/browser/extensions/api/brave_rewards_api.cc @@ -1129,11 +1129,7 @@ void BraveRewardsGetAdsAccountStatementFunction::OnGetAdsAccountStatement( Respond(OneArgument(base::Value(success))); } else { base::Value statement(base::Value::Type::DICTIONARY); - statement.SetDoubleKey("estimatedPendingRewards", - estimated_pending_rewards); - const std::string next_payment_date_as_string = - base::NumberToString(next_payment_date); - statement.SetStringKey("nextPaymentDate", next_payment_date_as_string); + statement.SetDoubleKey("nextPaymentDate", next_payment_date * 1000); statement.SetIntKey("adsReceivedThisMonth", ads_received_this_month); statement.SetDoubleKey("earningsThisMonth", earnings_this_month); statement.SetDoubleKey("earningsLastMonth", earnings_last_month); diff --git a/browser/ui/webui/brave_rewards_page_ui.cc b/browser/ui/webui/brave_rewards_page_ui.cc index 7968f7335aa2..3142d653da5f 100644 --- a/browser/ui/webui/brave_rewards_page_ui.cc +++ b/browser/ui/webui/brave_rewards_page_ui.cc @@ -1410,18 +1410,10 @@ void RewardsDOMHandler::OnGetStatement(const bool success, base::DictionaryValue history; - history.SetDouble("adsEstimatedPendingRewards", - estimated_pending_rewards); - - if (next_payment_date == 0) { - history.SetString("adsNextPaymentDate", ""); - } else { - base::Time time = base::Time::FromDoubleT(next_payment_date); - history.SetString("adsNextPaymentDate", - base::TimeFormatWithPattern(time, "MMMd")); - } - + history.SetDouble("adsNextPaymentDate", next_payment_date * 1000); history.SetInteger("adsReceivedThisMonth", ads_received_this_month); + history.SetDouble("adsEarningsThisMonth", earnings_this_month); + history.SetDouble("adsEarningsLastMonth", earnings_last_month); CallJavascriptFunction("brave_rewards.statement", history); } diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index a7b0403ecbe2..3e352dadf8d6 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -273,6 +273,7 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "editCardsTitle", IDS_EDIT_CARDS_TITLE }, { "tosAndPp", IDS_REWARDS_WIDGET_TOS_AND_PP}, // NOLINT { "rewardsWidgetStartUsing", IDS_REWARDS_WIDGET_START_USING}, // NOLINT + { "pendingRewardsMessage", IDS_BRAVE_REWARDS_PENDING_REWARDS_MESSAGE }, // Rewards BAP Deprecation Alert { "bapDeprecationAlertText", IDS_REWARDS_BAP_DEPRECATION_ALERT_TEXT }, { "bapDeprecationHeader", IDS_REWARDS_BAP_DEPRECATION_HEADER }, @@ -725,6 +726,7 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "optOutTooltip", IDS_BRAVE_UI_ADS_OPT_OUT_TOOLTIP }, { "payment", IDS_BRAVE_UI_PAYMENT }, { "paymentNotMade", IDS_BRAVE_UI_PAYMENT_NOT_MADE }, + { "pendingRewardsMessage", IDS_BRAVE_REWARDS_PENDING_REWARDS_MESSAGE }, { "pendingContributions", IDS_BRAVE_UI_PENDING_CONTRIBUTIONS }, { "pendingContributionEmpty", IDS_BRAVE_UI_PENDING_CONTRIBUTION_EMPTY }, { "pendingContributionRemoveAll", IDS_BRAVE_UI_PENDING_CONTRIBUTION_REMOVE_ALL }, // NOLINT diff --git a/common/extensions/api/brave_rewards.json b/common/extensions/api/brave_rewards.json index f40d5ed26235..4d658527a7ad 100644 --- a/common/extensions/api/brave_rewards.json +++ b/common/extensions/api/brave_rewards.json @@ -1149,11 +1149,8 @@ "type": "object", "optional": true, "properties": { - "estimatedPendingRewards": { - "type": "number" - }, "nextPaymentDate": { - "type": "string" + "type": "number" }, "adsReceivedThisMonth": { "type": "integer" 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 24c55ebf0531..7ecda3f80fd6 100644 --- a/components/brave_new_tab_ui/components/default/rewards/index.tsx +++ b/components/brave_new_tab_ui/components/default/rewards/index.tsx @@ -8,6 +8,7 @@ import { convertBalance } from '../../../../brave_rewards/resources/page/utils' import { getLocale, splitStringForTag } from '../../../../common/locale' import { + ArrivingSoon, WidgetWrapper, WidgetLayer, NotificationsList, @@ -34,6 +35,9 @@ import Notification from './notification' import BrandedWallpaperNotification from './brandedWallpaperNotification' import { BatColorIcon, CloseStrokeIcon } from 'brave-ui/components/icons' +import { formatMessage } from '../../../../../components/brave_rewards/resources/shared/lib/locale_context' +import { getDaysUntilRewardsPayment } from '../../../../../components/brave_rewards/resources/shared/lib/pending_rewards' + export interface RewardsProps { enabledAds: boolean balance: NewTab.RewardsBalance @@ -68,12 +72,13 @@ class Rewards extends React.PureComponent { const rate = parameters.rate || 0.0 const showEnableAds = !enabledAds && adsSupported - const amount = adsAccountStatement ? adsAccountStatement.estimatedPendingRewards : 0 + const amount = adsAccountStatement ? adsAccountStatement.earningsThisMonth : 0 const converted = convertBalance(amount, rate) const batFormatString = onlyAnonWallet ? getLocale('rewardsWidgetBap') : getLocale('rewardsWidgetBat') return ( + {this.renderPendingRewardsNotice()} { adsSupported ?
@@ -82,14 +87,10 @@ class Rewards extends React.PureComponent { {batFormatString}{converted} USD
- : null - } - { - !adsSupported - ? + : + {getLocale('rewardsWidgetAdsNotSupported')} - : null } {getLocale('rewardsWidgetEstimatedEarnings')} @@ -152,6 +153,35 @@ class Rewards extends React.PureComponent { ) } + renderPendingRewardsNotice = () => { + const { + nextPaymentDate, + earningsLastMonth + } = this.props.adsAccountStatement + + if (earningsLastMonth <= 0) { + return null + } + + const days = getDaysUntilRewardsPayment(nextPaymentDate) + if (!days) { + return null + } + + return ( + + { + formatMessage(getLocale('pendingRewardsMessage'), [ + + +{earningsLastMonth} BAT + , + days + ]) + } + + ) + } + renderRewardsInfo = () => { const { adsSupported, diff --git a/components/brave_new_tab_ui/components/default/rewards/style.tsx b/components/brave_new_tab_ui/components/default/rewards/style.tsx index 7d6f26ddb0fb..211d422ea74f 100644 --- a/components/brave_new_tab_ui/components/default/rewards/style.tsx +++ b/components/brave_new_tab_ui/components/default/rewards/style.tsx @@ -282,3 +282,13 @@ export const StyledTOS = styled(TOSAndPP as React.ComponentType)` export const StyleCenter = styled('div')<{}>` text-align: center; ` + +export const ArrivingSoon = styled.div` + background: ${p => p.theme.palette.white}; + border-radius: 6px; + padding: 0 10px; + color: ${p => p.theme.palette.neutral900}; + font-size: 14px; + line-height: 22px; + margin-bottom: 8px; +` 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 f28c36586a5b..109284589455 100644 --- a/components/brave_new_tab_ui/storage/new_tab_storage.ts +++ b/components/brave_new_tab_ui/storage/new_tab_storage.ts @@ -53,8 +53,7 @@ export const defaultState: NewTab.State = { togetherPromptDismissed: false, rewardsState: { adsAccountStatement: { - estimatedPendingRewards: 0, - nextPaymentDate: '', + nextPaymentDate: 0, adsReceivedThisMonth: 0, earningsThisMonth: 0, earningsLastMonth: 0 @@ -238,16 +237,21 @@ export const replaceStackWidgets = (state: NewTab.State) => { } const cleanData = (state: NewTab.State) => { - // We need to disable linter as we defined in d.ts that this values are number, - // but we need this check to covert from old version to a new one - /* tslint:disable */ - if (typeof state.rewardsState.totalContribution === 'string') { - state.rewardsState.totalContribution = 0.0 + const { rewardsState } = state + + if (typeof (rewardsState.totalContribution as any) === 'string') { + rewardsState.totalContribution = 0 + } + + // nextPaymentDate updated from seconds-since-epoch-string to ms-since-epoch + const { adsAccountStatement } = rewardsState + if (typeof (adsAccountStatement.nextPaymentDate as any) === 'string') { + adsAccountStatement.nextPaymentDate = + Number(adsAccountStatement.nextPaymentDate) * 1000 || 0 } - /* tslint:enable */ - if (!state.rewardsState.parameters) { - state.rewardsState.parameters = defaultState.rewardsState.parameters + if (!rewardsState.parameters) { + rewardsState.parameters = defaultState.rewardsState.parameters } return state diff --git a/components/brave_rewards/resources/android_page/actions/rewards_actions.ts b/components/brave_rewards/resources/android_page/actions/rewards_actions.ts index b356f6a3cc3d..de682205ca90 100644 --- a/components/brave_rewards/resources/android_page/actions/rewards_actions.ts +++ b/components/brave_rewards/resources/android_page/actions/rewards_actions.ts @@ -124,10 +124,7 @@ export const onPendingContributions = (list: Rewards.PendingContribution[]) => list }) -export const onStatement = (data: {adsEstimatedPendingRewards: number, adsNextPaymentDate: string, adsReceivedThisMonth: number}) => - action(types.ON_STATEMENT, { - data - }) +export const onStatement = (data: any) => action(types.ON_STATEMENT, { data }) export const getStatement = () => action(types.GET_STATEMENT) diff --git a/components/brave_rewards/resources/android_page/brave_rewards_page.tsx b/components/brave_rewards/resources/android_page/brave_rewards_page.tsx index 3c37539abeee..72928de01d64 100644 --- a/components/brave_rewards/resources/android_page/brave_rewards_page.tsx +++ b/components/brave_rewards/resources/android_page/brave_rewards_page.tsx @@ -14,6 +14,8 @@ import App from './components/app' require('../../../../ui/webui/resources/fonts/muli.css') require('../../../../ui/webui/resources/fonts/poppins.css') +import { WithThemeVariables } from '../shared/components/with_theme_variables' + // Utils import store from './store' import { ThemeProvider } from 'styled-components' @@ -33,7 +35,9 @@ window.cr.define('brave_rewards', function () { render( - + + + , document.getElementById('root')) @@ -103,7 +107,7 @@ window.cr.define('brave_rewards', function () { } } - function statement (data: {adsEstimatedPendingRewards: number, adsNextPaymentDate: string, adsReceivedThisMonth: number}) { + function statement (data: any) { getActions().onStatement(data) } diff --git a/components/brave_rewards/resources/android_page/components/adsBox.tsx b/components/brave_rewards/resources/android_page/components/adsBox.tsx index 0ca0fade7eb4..071305dcab97 100644 --- a/components/brave_rewards/resources/android_page/components/adsBox.tsx +++ b/components/brave_rewards/resources/android_page/components/adsBox.tsx @@ -12,15 +12,25 @@ import { List, NextContribution, Tokens } from '../../ui/components' import { Grid, Column, Select, ControlWrapper } from 'brave-ui/components' import AdsOnboarding from './adsOnboarding' import { + StyledArrivingSoon, StyledListContent, StyledTotalContent } from './style' +import { MoneyBagIcon } from '../../shared/components/icons/money_bag' +import { formatMessage } from '../../shared/lib/locale_context' +import { getDaysUntilRewardsPayment } from '../../shared/lib/pending_rewards' + // Utils import { getLocale } from '../../../../common/locale' import * as rewardsActions from '../actions/rewards_actions' import * as utils from '../utils' +const nextPaymentDateFormatter = new Intl.DateTimeFormat(undefined, { + month: 'short', + day: 'numeric' +}) + interface Props extends Rewards.ComponentProps { } @@ -179,9 +189,11 @@ class AdsBox extends React.Component { let adsEnabled = false let adsUIEnabled = false let adsIsSupported = false - let estimatedPendingRewards = 0 - let nextPaymentDate = '' + let nextPaymentDate = 0 let adsReceivedThisMonth = 0 + let earningsThisMonth = 0 + let earningsLastMonth = 0 + const { adsData, safetyNetFailed, @@ -193,9 +205,10 @@ class AdsBox extends React.Component { adsEnabled = adsData.adsEnabled adsUIEnabled = adsData.adsUIEnabled adsIsSupported = adsData.adsIsSupported - estimatedPendingRewards = adsData.adsEstimatedPendingRewards || 0 nextPaymentDate = adsData.adsNextPaymentDate adsReceivedThisMonth = adsData.adsReceivedThisMonth || 0 + earningsThisMonth = adsData.adsEarningsThisMonth || 0 + earningsLastMonth = adsData.adsEarningsLastMonth || 0 } // disabled / alert state @@ -223,6 +236,7 @@ class AdsBox extends React.Component { } const tokenString = getLocale(onlyAnonWallet ? 'points' : 'tokens') + const estimatedPendingDays = getDaysUntilRewardsPayment(nextPaymentDate) return ( { settingsChild={this.adsSettings(adsEnabled)} {...boxPropsExtra} > + { + earningsLastMonth > 0 && estimatedPendingDays && + + + { + formatMessage(getLocale('pendingRewardsMessage'), [ + + +{earningsLastMonth} BAT + , + estimatedPendingDays + ]) + } + + } {getLocale('adsCurrentEarnings')}}> {getLocale('adsPaymentDate')}}> - {nextPaymentDate} + {nextPaymentDateFormatter.format(new Date(nextPaymentDate))} diff --git a/components/brave_rewards/resources/android_page/components/style.ts b/components/brave_rewards/resources/android_page/components/style.ts index a6f8d70873a0..36280d7b248a 100644 --- a/components/brave_rewards/resources/android_page/components/style.ts +++ b/components/brave_rewards/resources/android_page/components/style.ts @@ -84,3 +84,29 @@ export const StyledWalletClose = styled('div')<{}>` color: ${p => p.theme.color.subtleExclude}; width: 25px; ` + +export const StyledArrivingSoon = styled.div` + background: rgba(93, 181, 252, 0.2); + margin-bottom: 8px; + padding: 4px; + color: var(--brave-palette-neutral700); + text-align: center; + font-size: 12px; + line-height: 22px; + + span.amount { + color: var(--brave-palette-neutral900); + font-weight: 600; + font-size: 14px; + line-height: 24px; + } + + .icon { + height: 16px; + width: auto; + fill: var(--brave-palette-blue500); + vertical-align: middle; + margin-bottom: 3px; + margin-right: 8px; + } +` diff --git a/components/brave_rewards/resources/android_page/reducers/rewards_reducer.ts b/components/brave_rewards/resources/android_page/reducers/rewards_reducer.ts index 8261606f50ee..bf935cb80d12 100644 --- a/components/brave_rewards/resources/android_page/reducers/rewards_reducer.ts +++ b/components/brave_rewards/resources/android_page/reducers/rewards_reducer.ts @@ -153,9 +153,10 @@ const rewardsReducer: Reducer = (state: Rewards.State } const data = action.payload.data - state.adsData.adsEstimatedPendingRewards = data.adsEstimatedPendingRewards state.adsData.adsNextPaymentDate = data.adsNextPaymentDate state.adsData.adsReceivedThisMonth = data.adsReceivedThisMonth + state.adsData.adsEarningsThisMonth = data.adsEarningsThisMonth + state.adsData.adsEarningsLastMonth = data.adsEarningsLastMonth break } case types.ON_INLINE_TIP_SETTINGS_CHANGE: { diff --git a/components/brave_rewards/resources/android_page/storage.ts b/components/brave_rewards/resources/android_page/storage.ts index b0691dfbf970..7dfe4cfae29c 100644 --- a/components/brave_rewards/resources/android_page/storage.ts +++ b/components/brave_rewards/resources/android_page/storage.ts @@ -45,9 +45,10 @@ export const defaultState: Rewards.State = { shouldAllowAdsSubdivisionTargeting: true, adsUIEnabled: false, adsIsSupported: false, - adsEstimatedPendingRewards: 0, - adsNextPaymentDate: '', - adsReceivedThisMonth: 0 + adsNextPaymentDate: 0, + adsReceivedThisMonth: 0, + adsEarningsThisMonth: 0, + adsEarningsLastMonth: 0 }, adsHistory: [], pendingContributionTotal: 0, @@ -88,6 +89,11 @@ const cleanData = (state: Rewards.State) => { state.parameters = defaultState.parameters } + // Data type change: adsNextPaymentDate (string -> number) + if (typeof (state.adsData.adsNextPaymentDate as any) !== 'number') { + throw new Error('Invalid adsNextPaymentDate') + } + return state } @@ -96,13 +102,13 @@ export const load = (): Rewards.State => { let state: Rewards.State = defaultState if (data) { try { - state = JSON.parse(data) + state = cleanData(JSON.parse(data)) state.initializing = true } catch (e) { console.error('Could not parse local storage data: ', e) } } - return cleanData(state) + return state } export const debouncedSave = debounce((data: Rewards.State) => { diff --git a/components/brave_rewards/resources/page/actions/rewards_actions.ts b/components/brave_rewards/resources/page/actions/rewards_actions.ts index 0e937e04b454..6ba425edd20f 100644 --- a/components/brave_rewards/resources/page/actions/rewards_actions.ts +++ b/components/brave_rewards/resources/page/actions/rewards_actions.ts @@ -211,10 +211,7 @@ export const onPendingContributions = (list: Rewards.PendingContribution[]) => list }) -export const onStatement = (data: {adsEstimatedPendingRewards: number, adsNextPaymentDate: string, adsReceivedThisMonth: number}) => - action(types.ON_STATEMENT, { - data - }) +export const onStatement = (data: any) => action(types.ON_STATEMENT, { data }) export const getStatement = () => action(types.GET_STATEMENT) diff --git a/components/brave_rewards/resources/page/brave_rewards_page.tsx b/components/brave_rewards/resources/page/brave_rewards_page.tsx index 1944a536a8c5..5e7eb5553420 100644 --- a/components/brave_rewards/resources/page/brave_rewards_page.tsx +++ b/components/brave_rewards/resources/page/brave_rewards_page.tsx @@ -154,7 +154,7 @@ window.cr.define('brave_rewards', function () { } } - function statement (data: {adsEstimatedPendingRewards: number, adsNextPaymentDate: string, adsReceivedThisMonth: number}) { + function statement (data: any) { getActions().onStatement(data) } diff --git a/components/brave_rewards/resources/page/components/adsBox.tsx b/components/brave_rewards/resources/page/components/adsBox.tsx index aa9af1c21786..9007bb759f56 100644 --- a/components/brave_rewards/resources/page/components/adsBox.tsx +++ b/components/brave_rewards/resources/page/components/adsBox.tsx @@ -19,11 +19,21 @@ import { } from '../../ui/components' import { Grid, Column, Select, ControlWrapper } from 'brave-ui/components' +import { ArrivingSoon } from './style' +import { MoneyBagIcon } from '../../shared/components/icons/money_bag' +import { formatMessage } from '../../shared/lib/locale_context' +import { getDaysUntilRewardsPayment } from '../../shared/lib/pending_rewards' + // Utils import * as utils from '../utils' import { getLocale } from '../../../../common/locale' import * as rewardsActions from '../actions/rewards_actions' +const nextPaymentDateFormatter = new Intl.DateTimeFormat(undefined, { + month: 'short', + day: 'numeric' +}) + interface Props extends Rewards.ComponentProps { } @@ -374,9 +384,10 @@ class AdsBox extends React.Component { let adsPerHour = 0 let adsUIEnabled = false let adsIsSupported = false - let estimatedPendingRewards = 0 - let nextPaymentDate = '' + let nextPaymentDate = 0 let adsReceivedThisMonth = 0 + let earningsThisMonth = 0 + let earningsLastMonth = 0 const { adsData, @@ -392,9 +403,10 @@ class AdsBox extends React.Component { adsPerHour = adsData.adsPerHour adsUIEnabled = adsData.adsUIEnabled adsIsSupported = adsData.adsIsSupported - estimatedPendingRewards = adsData.adsEstimatedPendingRewards || 0 nextPaymentDate = adsData.adsNextPaymentDate adsReceivedThisMonth = adsData.adsReceivedThisMonth || 0 + earningsThisMonth = adsData.adsEarningsThisMonth || 0 + earningsLastMonth = adsData.adsEarningsLastMonth || 0 } const enabled = adsEnabled && adsIsSupported @@ -406,6 +418,8 @@ class AdsBox extends React.Component { const notEmpty = rows && rows.length !== 0 const tokenString = getLocale(onlyAnonWallet ? 'points' : 'tokens') + const estimatedPendingDays = getDaysUntilRewardsPayment(nextPaymentDate) + return ( <> { onSettingsClick={this.onSettingsToggle} attachedAlert={this.adsNotSupportedAlert(adsIsSupported)} > + { + earningsLastMonth > 0 && estimatedPendingDays && + + + { + formatMessage(getLocale('pendingRewardsMessage'), [ + + +{earningsLastMonth} BAT + , + estimatedPendingDays + ]) + } + + } - {nextPaymentDate} + {nextPaymentDateFormatter.format(new Date(nextPaymentDate))} diff --git a/components/brave_rewards/resources/page/components/style.tsx b/components/brave_rewards/resources/page/components/style.tsx index b68a71f75e92..5e70fb4589b2 100644 --- a/components/brave_rewards/resources/page/components/style.tsx +++ b/components/brave_rewards/resources/page/components/style.tsx @@ -32,3 +32,30 @@ export const QRText = styled('div')<{}>` export const TourPromoWrapper = styled('div')<{}>` margin-top: 30px; ` + +export const ArrivingSoon = styled.div` + background: rgba(93, 181, 252, 0.2); + border-radius: 4px; + margin-bottom: 8px; + padding: 4px; + color: var(--brave-palette-neutral700); + text-align: center; + font-size: 12px; + line-height: 22px; + + span.amount { + color: var(--brave-palette-neutral900); + font-weight: 600; + font-size: 14px; + line-height: 24px; + } + + .icon { + height: 16px; + width: auto; + fill: var(--brave-palette-blue500); + vertical-align: middle; + margin-bottom: 3px; + margin-right: 8px; + } +` diff --git a/components/brave_rewards/resources/page/reducers/rewards_reducer.ts b/components/brave_rewards/resources/page/reducers/rewards_reducer.ts index a77972586767..9a5c1250a59e 100644 --- a/components/brave_rewards/resources/page/reducers/rewards_reducer.ts +++ b/components/brave_rewards/resources/page/reducers/rewards_reducer.ts @@ -241,9 +241,10 @@ const rewardsReducer: Reducer = (state: Rewards.State } const data = action.payload.data - state.adsData.adsEstimatedPendingRewards = data.adsEstimatedPendingRewards state.adsData.adsNextPaymentDate = data.adsNextPaymentDate state.adsData.adsReceivedThisMonth = data.adsReceivedThisMonth + state.adsData.adsEarningsThisMonth = data.adsEarningsThisMonth + state.adsData.adsEarningsLastMonth = data.adsEarningsLastMonth break } case types.ON_INLINE_TIP_SETTINGS_CHANGE: { diff --git a/components/brave_rewards/resources/page/storage.ts b/components/brave_rewards/resources/page/storage.ts index 0cc505f2584d..e47ca51f92e3 100644 --- a/components/brave_rewards/resources/page/storage.ts +++ b/components/brave_rewards/resources/page/storage.ts @@ -45,9 +45,10 @@ export const defaultState: Rewards.State = { shouldAllowAdsSubdivisionTargeting: true, adsUIEnabled: false, adsIsSupported: false, - adsEstimatedPendingRewards: 0, - adsNextPaymentDate: '', - adsReceivedThisMonth: 0 + adsNextPaymentDate: 0, + adsReceivedThisMonth: 0, + adsEarningsThisMonth: 0, + adsEarningsLastMonth: 0 }, adsHistory: [], pendingContributionTotal: 0, @@ -98,6 +99,11 @@ const cleanData = (state: Rewards.State) => { } } + // Data type change: adsNextPaymentDate (string -> number) + if (typeof (state.adsData.adsNextPaymentDate as any) !== 'number') { + throw new Error('Invalid adsNextPaymentDate') + } + state.ui.modalRedirect = 'hide' return state @@ -108,13 +114,13 @@ export const load = (): Rewards.State => { let state: Rewards.State = defaultState if (data) { try { - state = JSON.parse(data) + state = cleanData(JSON.parse(data)) state.initializing = true } catch (e) { console.error('Could not parse local storage data: ', e) } } - return cleanData(state) + return state } export const debouncedSave = debounce((data: Rewards.State) => { diff --git a/components/brave_rewards/resources/shared/components/icons/money_bag.tsx b/components/brave_rewards/resources/shared/components/icons/money_bag.tsx new file mode 100644 index 000000000000..7ce99eab7f82 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/icons/money_bag.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 MoneyBagIcon () { + return ( + + + + ) +} diff --git a/components/brave_rewards/resources/shared/lib/pending_rewards.ts b/components/brave_rewards/resources/shared/lib/pending_rewards.ts new file mode 100644 index 000000000000..5ba8bceb7bf0 --- /dev/null +++ b/components/brave_rewards/resources/shared/lib/pending_rewards.ts @@ -0,0 +1,31 @@ +/* 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/. */ + +const pendingDaysFormatter = new Intl.NumberFormat(undefined, { + style: 'unit', + // @ts-ignore: "unit" property not yet supported by TS + unit: 'day', + // @ts-ignore: "unitDisplay" property not yet support by TS + unitDisplay: 'long', + maximumFractionDigits: 0 +}) + +export function getDaysUntilRewardsPayment (nextPaymentDate: number | Date) { + if (typeof nextPaymentDate === 'number') { + nextPaymentDate = new Date(nextPaymentDate) + } + + // Only show pending days when payment date is within the current month + if (nextPaymentDate.getMonth() !== new Date().getMonth()) { + return '' + } + + const delta = nextPaymentDate.getTime() - Date.now() + const days = Math.ceil(delta / 24 / 60 / 60 / 1000) + if (days < 1) { + return '' + } + + return pendingDaysFormatter.format(days) +} diff --git a/components/definitions/newTab.d.ts b/components/definitions/newTab.d.ts index 39a1d2268dd8..ded7dc06f8f7 100644 --- a/components/definitions/newTab.d.ts +++ b/components/definitions/newTab.d.ts @@ -253,8 +253,7 @@ declare namespace NewTab { } export interface AdsAccountStatement { - estimatedPendingRewards: number - nextPaymentDate: string + nextPaymentDate: number adsReceivedThisMonth: number earningsThisMonth: number earningsLastMonth: number diff --git a/components/definitions/rewards.d.ts b/components/definitions/rewards.d.ts index d3b8ae6b771e..0dadb41f372f 100644 --- a/components/definitions/rewards.d.ts +++ b/components/definitions/rewards.d.ts @@ -224,9 +224,10 @@ declare namespace Rewards { shouldAllowAdsSubdivisionTargeting: boolean adsUIEnabled: boolean adsIsSupported: boolean - adsEstimatedPendingRewards: number - adsNextPaymentDate: string + adsNextPaymentDate: number adsReceivedThisMonth: number + adsEarningsThisMonth: number + adsEarningsLastMonth: number } export enum RewardsType { diff --git a/components/resources/brave_components_strings.grd b/components/resources/brave_components_strings.grd index df88685da48c..25d6292f3470 100644 --- a/components/resources/brave_components_strings.grd +++ b/components/resources/brave_components_strings.grd @@ -392,7 +392,7 @@ Earn [[currency]] by viewing Brave Ads. Ads presented are based on your interests, as inferred from your browsing behavior. No personal data or browsing history ever leaves your browser. Auto-Contribute An automatic way to support publishers and content creators. Set a monthly payment and browse normally. The Brave Verified sites you visit will receive your contributions automatically, based on your attention as measured by Brave. - Estimated pending rewards + Current earnings this month (estimated) Next payment date Ads received this month Sorry! Ads are not yet available in your region. @@ -487,6 +487,7 @@ Error creating Brave Browser BAT card You need a verified wallet to Login You need a minimum of $115 BAT to create an Uphold wallet. Please try again after you have verified your wallet with Uphold. + $1+1.5 BAT arriving in $24 days Amount: diff --git a/components/test/brave_rewards/page/reducers/rewards_reducer_test.ts b/components/test/brave_rewards/page/reducers/rewards_reducer_test.ts index 39901e7b67c6..2e280b2e33fb 100644 --- a/components/test/brave_rewards/page/reducers/rewards_reducer_test.ts +++ b/components/test/brave_rewards/page/reducers/rewards_reducer_test.ts @@ -103,7 +103,7 @@ describe('rewards reducer', () => { adsUIEnabled: false, adsIsSupported: false, adsEstimatedPendingRewards: 0, - adsNextPaymentDate: '', + adsNextPaymentDate: 0, adsReceivedThisMonth: 0 } @@ -118,7 +118,7 @@ describe('rewards reducer', () => { adsUIEnabled: true, adsIsSupported: true, adsEstimatedPendingRewards: 0, - adsNextPaymentDate: '', + adsNextPaymentDate: 0, adsReceivedThisMonth: 0 } @@ -154,9 +154,10 @@ describe('rewards reducer', () => { shouldAllowAdsSubdivisionTargeting: true, adsUIEnabled: true, adsIsSupported: true, - adsEstimatedPendingRewards: 0, - adsNextPaymentDate: '', - adsReceivedThisMonth: 0 + adsNextPaymentDate: 0, + adsReceivedThisMonth: 0, + adsEarningsThisMonth: 0, + adsEarningsLastMonth: 0 } const assertion = reducers({ @@ -191,7 +192,7 @@ describe('rewards reducer', () => { adsUIEnabled: false, adsIsSupported: false, adsEstimatedPendingRewards: 0, - adsNextPaymentDate: '', + adsNextPaymentDate: 0, adsReceivedThisMonth: 0 }