From cfd0d60cfb0695002f8fece2262cc3c9e99b2d05 Mon Sep 17 00:00:00 2001 From: zenparsing Date: Fri, 14 May 2021 13:35:57 -0400 Subject: [PATCH] Update display of estimated pending rewards --- browser/extensions/api/brave_rewards_api.cc | 6 +-- browser/ui/webui/brave_rewards_page_ui.cc | 14 ++---- browser/ui/webui/brave_webui_source.cc | 2 + common/extensions/api/brave_rewards.json | 5 +- .../components/default/rewards/index.tsx | 48 ++++++++++++++++--- .../components/default/rewards/style.tsx | 10 ++++ .../storage/new_tab_storage.ts | 25 ++++++---- .../android_page/actions/rewards_actions.ts | 5 +- .../android_page/brave_rewards_page.tsx | 8 +++- .../android_page/components/adsBox.tsx | 40 +++++++++++++--- .../android_page/components/style.ts | 26 ++++++++++ .../android_page/reducers/rewards_reducer.ts | 3 +- .../resources/android_page/storage.ts | 16 +++++-- .../resources/page/actions/rewards_actions.ts | 5 +- .../resources/page/brave_rewards_page.tsx | 2 +- .../resources/page/components/adsBox.tsx | 40 +++++++++++++--- .../resources/page/components/style.tsx | 27 +++++++++++ .../page/reducers/rewards_reducer.ts | 3 +- .../brave_rewards/resources/page/storage.ts | 16 +++++-- .../shared/components/icons/money_bag.tsx | 13 +++++ .../resources/shared/lib/pending_rewards.ts | 31 ++++++++++++ components/definitions/newTab.d.ts | 3 +- components/definitions/rewards.d.ts | 5 +- .../resources/brave_components_strings.grd | 3 +- .../page/reducers/rewards_reducer_test.ts | 13 ++--- 25 files changed, 286 insertions(+), 83 deletions(-) create mode 100644 components/brave_rewards/resources/shared/components/icons/money_bag.tsx create mode 100644 components/brave_rewards/resources/shared/lib/pending_rewards.ts diff --git a/browser/extensions/api/brave_rewards_api.cc b/browser/extensions/api/brave_rewards_api.cc index aef76b30c63f..3baf147c87b4 100644 --- a/browser/extensions/api/brave_rewards_api.cc +++ b/browser/extensions/api/brave_rewards_api.cc @@ -1112,11 +1112,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 a3e3e6d3e954..4a374c32403a 100644 --- a/browser/ui/webui/brave_rewards_page_ui.cc +++ b/browser/ui/webui/brave_rewards_page_ui.cc @@ -1405,18 +1405,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 255060dfec8d..5ec1416d3d26 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -272,6 +272,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 }, // Together Widget { "togetherWidgetTitle", IDS_TOGETHER_WIDGET_TITLE }, { "togetherWidgetWelcomeTitle", IDS_TOGETHER_WIDGET_WELCOME_TITLE }, @@ -717,6 +718,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 d4dac8f85900..9b2628b52714 100644 --- a/common/extensions/api/brave_rewards.json +++ b/common/extensions/api/brave_rewards.json @@ -1132,11 +1132,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 9f5979d4a46b..6a8d8bbe6066 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 @@ -66,12 +70,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 = getLocale('rewardsWidgetBat') return ( + {this.renderPendingRewardsNotice()} { adsSupported ?
@@ -80,14 +85,10 @@ class Rewards extends React.PureComponent { {batFormatString}{converted} USD
- : null - } - { - !adsSupported - ? + : + {getLocale('rewardsWidgetAdsNotSupported')} - : null } {getLocale('rewardsWidgetEstimatedEarnings')} @@ -149,6 +150,39 @@ class Rewards extends React.PureComponent { ) } + renderPendingRewardsNotice = () => { + if (!this.props.adsAccountStatement) { + return null + } + + 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 e6712a9946f9..3b4c01c5b109 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 @@ -237,16 +236,22 @@ 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 (adsAccountStatement && + 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 d8f3d31e12fd..6628ce062e10 100644 --- a/components/brave_rewards/resources/android_page/actions/rewards_actions.ts +++ b/components/brave_rewards/resources/android_page/actions/rewards_actions.ts @@ -135,10 +135,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 34ccffc5cc07..7c76c0b6ad59 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 97cf4fc5c3b8..d1dc699b9c94 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, @@ -192,9 +204,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 @@ -222,6 +235,7 @@ class AdsBox extends React.Component { } const tokenString = getLocale('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 38a81e565bf9..9c616dbf8340 100644 --- a/components/brave_rewards/resources/android_page/reducers/rewards_reducer.ts +++ b/components/brave_rewards/resources/android_page/reducers/rewards_reducer.ts @@ -179,9 +179,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 1f924ecfd815..d20142ded02a 100644 --- a/components/brave_rewards/resources/android_page/storage.ts +++ b/components/brave_rewards/resources/android_page/storage.ts @@ -46,9 +46,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, @@ -89,6 +90,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 } @@ -97,13 +103,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 c3b52b7c3667..99e6d746f7c5 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 7731cca6c0d8..3795421e5327 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 470176f8de43..bd91c4c29402 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, @@ -390,9 +401,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 @@ -404,6 +416,8 @@ class AdsBox extends React.Component { const notEmpty = rows && rows.length !== 0 const tokenString = getLocale('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 dfcccc003d13..3580bc115d39 100644 --- a/components/brave_rewards/resources/page/reducers/rewards_reducer.ts +++ b/components/brave_rewards/resources/page/reducers/rewards_reducer.ts @@ -247,9 +247,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 af6cad9cb09a..8fa09df850b9 100644 --- a/components/brave_rewards/resources/page/storage.ts +++ b/components/brave_rewards/resources/page/storage.ts @@ -46,9 +46,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, @@ -99,6 +100,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 @@ -109,13 +115,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 d37c4589d891..f81454d98ba0 100644 --- a/components/definitions/newTab.d.ts +++ b/components/definitions/newTab.d.ts @@ -252,8 +252,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 97c282d69f8b..77543c791310 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 81464a96f10f..3b46cf6755a1 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. @@ -485,6 +485,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 }