diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index b27584f64edb..51c027cdb61b 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -258,6 +258,7 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "donationTitle", IDS_BRAVE_REWARDS_LOCAL_DONAT_TITLE }, { "donationDesc", IDS_BRAVE_REWARDS_LOCAL_DONAT_DESC }, { "donationTotalDonations", IDS_BRAVE_REWARDS_LOCAL_DONAT_TOTAL_DONATIONS }, // NOLINT + { "donationTotalMonthlyContribution", IDS_BRAVE_REWARDS_LOCAL_DONAT_TOTAL_MONTHLY_CONTRIBUTION }, // NOLINT { "donationVisitSome", IDS_BRAVE_REWARDS_LOCAL_DONAT_VISIT_SOME }, { "donationAbility", IDS_BRAVE_REWARDS_LOCAL_DONAT_ABILITY }, { "donationAbilityYT", IDS_BRAVE_REWARDS_LOCAL_DONAT_ABILITY_YT }, @@ -267,6 +268,10 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "donationDisabledText1", IDS_BRAVE_REWARDS_LOCAL_DONAT_DISABLED_TEXT1 }, // NOLINT { "donationDisabledText2", IDS_BRAVE_REWARDS_LOCAL_DONAT_DISABLED_TEXT2 }, // NOLINT { "donationNextDate", IDS_BRAVE_REWARDS_LOCAL_DONAT_NEXT_DATE }, + { "monthlyContributionTitle", IDS_BRAVE_REWARDS_LOCAL_MONTHLY_CONTRIBUTION_TITLE }, // NOLINT + { "monthlyContributionDesc", IDS_BRAVE_REWARDS_LOCAL_MONTHLY_CONTRIBUTION_DESC }, // NOLINT + { "monthlyContributionEmpty", IDS_BRAVE_REWARDS_LOCAL_MONTHLY_CONTRIBUTION_EMPTY }, // NOLINT + { "monthlyContributionDisabledText", IDS_BRAVE_REWARDS_LOCAL_MONTHLY_CONTRIBUTION_DISABLED_TEXT }, // NOLINT { "panelAddFunds", IDS_BRAVE_REWARDS_LOCAL_PANEL_ADD_FUNDS }, { "panelWithdrawFunds", IDS_BRAVE_REWARDS_LOCAL_PANEL_WITHDRAW_FUNDS }, diff --git a/components/brave_rewards/browser/rewards_service_browsertest.cc b/components/brave_rewards/browser/rewards_service_browsertest.cc index 70cb0fb691e2..948a657c1761 100644 --- a/components/brave_rewards/browser/rewards_service_browsertest.cc +++ b/components/brave_rewards/browser/rewards_service_browsertest.cc @@ -999,7 +999,7 @@ class BraveRewardsBrowserTest : contents(), "const delay = t => new Promise(resolve => setTimeout(resolve, t));" "delay(0).then(() => " - " document.querySelector(\"[type='donation']\")" + " document.querySelectorAll(\"[type='donation']\")[1]" " .parentElement.parentElement.innerText);", content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, content::ISOLATED_WORLD_ID_CONTENT_END); diff --git a/components/brave_rewards/resources/page/components/monthlyContributionBox.tsx b/components/brave_rewards/resources/page/components/monthlyContributionBox.tsx new file mode 100644 index 000000000000..d1e0f9786f0e --- /dev/null +++ b/components/brave_rewards/resources/page/components/monthlyContributionBox.tsx @@ -0,0 +1,160 @@ +/* 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' +import { bindActionCreators, Dispatch } from 'redux' +import { connect } from 'react-redux' + +import { + DisabledContent, + Box, + TableDonation, + List, + Tokens, + ModalDonation, + NextContribution +} from '../../ui/components' +import { Provider } from '../../ui/components/profile' + +import { getLocale } from '../../../../common/locale' +import * as rewardsActions from '../actions/rewards_actions' +import * as utils from '../utils' +import { DetailRow } from '../../ui/components/tableDonation' + +interface Props extends Rewards.ComponentProps { +} + +interface State { + modalShowAll: boolean +} + +class MonthlyContributionBox extends React.Component { + constructor (props: Props) { + super(props) + this.state = { + modalShowAll: false + } + } + + get actions () { + return this.props.actions + } + + disabledContent = () => { + return ( + + {getLocale('monthlyContributionDisabledText')} + + ) + } + + getRows = () => { + const { balance, recurringList } = this.props.rewardsData + let recurring: DetailRow[] = [] + + if (!recurringList) { + return recurring + } + + return recurringList.map((item: Rewards.Publisher) => { + let faviconUrl = `chrome://favicon/size/48@1x/${item.url}` + const verified = utils.isPublisherConnectedOrVerified(item.status) + + if (item.favIcon && verified) { + faviconUrl = `chrome://favicon/size/48@1x/${item.favIcon}` + } + + return { + profile: { + name: item.name, + verified, + provider: (item.provider ? item.provider : undefined) as Provider, + src: faviconUrl + }, + contribute: { + tokens: item.percentage.toFixed(1), + converted: utils.convertBalance(item.percentage.toString(), balance.rates) + }, + url: item.url, + type: 'recurring' as any, + onRemove: () => { this.actions.removeRecurringTip(item.id) } + } + }) + } + + onModalToggle = () => { + this.setState({ + modalShowAll: !this.state.modalShowAll + }) + } + + render () { + const { + balance, + firstLoad, + enabledMain, + recurringList, + reconcileStamp + } = this.props.rewardsData + const showDisabled = firstLoad !== false || !enabledMain + const tipRows = this.getRows() + const topRows = tipRows.slice(0, 5) + const numRows = tipRows && tipRows.length + const allSites = !(numRows > 5) + const total = utils.tipsListTotal(recurringList) + const converted = utils.convertBalance(total, balance.rates) + + return ( + + { + this.state.modalShowAll + ? + : null + } + + + + + + {new Intl.DateTimeFormat('default', { month: 'short', day: 'numeric' }).format(reconcileStamp * 1000)} + + + + + {getLocale('monthlyContributionEmpty')} + + + ) + } +} + +const mapStateToProps = (state: Rewards.ApplicationState) => ({ + rewardsData: state.rewardsData +}) + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + actions: bindActionCreators(rewardsActions, dispatch) +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(MonthlyContributionBox) diff --git a/components/brave_rewards/resources/page/components/settingsPage.tsx b/components/brave_rewards/resources/page/components/settingsPage.tsx index e1ea8149438f..952e776b5f88 100644 --- a/components/brave_rewards/resources/page/components/settingsPage.tsx +++ b/components/brave_rewards/resources/page/components/settingsPage.tsx @@ -13,6 +13,7 @@ import PageWallet from './pageWallet' import AdsBox from './adsBox' import ContributeBox from './contributeBox' import TipBox from './tipsBox' +import MonthlyContributionBox from './monthlyContributionBox' // Utils import * as rewardsActions from '../actions/rewards_actions' @@ -244,6 +245,7 @@ class SettingsPage extends React.Component { } + diff --git a/components/brave_rewards/resources/page/components/tipsBox.tsx b/components/brave_rewards/resources/page/components/tipsBox.tsx index 826310c9848f..9713bc7ddd1f 100644 --- a/components/brave_rewards/resources/page/components/tipsBox.tsx +++ b/components/brave_rewards/resources/page/components/tipsBox.tsx @@ -15,8 +15,7 @@ import { TableDonation, List, Tokens, - ModalDonation, - NextContribution + ModalDonation } from '../../ui/components' import { Provider } from '../../ui/components/profile' @@ -58,83 +57,40 @@ class TipBox extends React.Component { ) } - getTotal = () => { - const { reports } = this.props.rewardsData - - const currentTime = new Date() - const reportKey = `${currentTime.getFullYear()}_${currentTime.getMonth() + 1}` - const report: Rewards.Report = reports[reportKey] - - if (report) { - return utils.tipsTotal(report) - } - - return '0.0' - } - getTipsRows = () => { - const { balance, recurringList, tipsList } = this.props.rewardsData - - // Recurring - let recurring: DetailRow[] = [] - if (recurringList) { - recurring = recurringList.map((item: Rewards.Publisher) => { - let faviconUrl = `chrome://favicon/size/48@1x/${item.url}` - const verified = utils.isPublisherConnectedOrVerified(item.status) - if (item.favIcon && verified) { - faviconUrl = `chrome://favicon/size/48@1x/${item.favIcon}` - } - - return { - profile: { - name: item.name, - verified, - provider: (item.provider ? item.provider : undefined) as Provider, - src: faviconUrl - }, - contribute: { - tokens: item.percentage.toFixed(1), - converted: utils.convertBalance(item.percentage.toString(), balance.rates) - }, - url: item.url, - type: 'recurring' as any, - onRemove: () => { this.actions.removeRecurringTip(item.id) } - } - }) - } - - // Tips + const { balance, tipsList } = this.props.rewardsData let tips: DetailRow[] = [] - if (tipsList) { - tips = tipsList.map((item: Rewards.Publisher) => { - let faviconUrl = `chrome://favicon/size/48@1x/${item.url}` - const verified = utils.isPublisherConnectedOrVerified(item.status) - if (item.favIcon && verified) { - faviconUrl = `chrome://favicon/size/48@1x/${item.favIcon}` - } - - const token = utils.convertProbiToFixed(item.percentage.toString()) - return { - profile: { - name: item.name, - verified, - provider: (item.provider ? item.provider : undefined) as Provider, - src: faviconUrl - }, - contribute: { - tokens: token, - converted: utils.convertBalance(token, balance.rates) - }, - url: item.url, - text: item.tipDate ? new Date(item.tipDate * 1000).toLocaleDateString() : undefined, - type: 'donation' as any, - onRemove: () => { this.actions.removeRecurringTip(item.id) } - } - }) + if (!tipsList) { + return tips } - return recurring.concat(tips) + return tipsList.map((item: Rewards.Publisher) => { + let faviconUrl = `chrome://favicon/size/48@1x/${item.url}` + const verified = utils.isPublisherConnectedOrVerified(item.status) + if (item.favIcon && verified) { + faviconUrl = `chrome://favicon/size/48@1x/${item.favIcon}` + } + + const token = utils.convertProbiToFixed(item.percentage.toString()) + + return { + profile: { + name: item.name, + verified, + provider: (item.provider ? item.provider : undefined) as Provider, + src: faviconUrl + }, + contribute: { + tokens: token, + converted: utils.convertBalance(token, balance.rates) + }, + url: item.url, + text: item.tipDate ? new Date(item.tipDate * 1000).toLocaleDateString() : undefined, + type: 'donation' as any, + onRemove: () => { this.actions.removeRecurringTip(item.id) } + } + }) } onModalToggle = () => { @@ -219,8 +175,7 @@ class TipBox extends React.Component { firstLoad, enabledMain, ui, - recurringList, - reconcileStamp + tipsList } = this.props.rewardsData const { walletImported } = ui const showDisabled = firstLoad !== false || !enabledMain @@ -228,7 +183,7 @@ class TipBox extends React.Component { const topRows = tipRows.slice(0, 5) const numRows = tipRows && tipRows.length const allSites = !(numRows > 5) - const total = this.getTotal() + const total = utils.tipsListTotal(tipsList, true) const converted = utils.convertBalance(total, balance.rates) return ( @@ -247,22 +202,13 @@ class TipBox extends React.Component { ? : null } - { - recurringList && recurringList.length > 0 - ? - - {new Intl.DateTimeFormat('default', { month: 'short', day: 'numeric' }).format(reconcileStamp * 1000)} - - - : null - } - { return new BigNumber(report.donation).plus(tips).dividedBy('1e18').toFixed(1, BigNumber.ROUND_DOWN) } +export const tipsListTotal = (list: Rewards.Publisher[], convertProbi = false) => { + if (list.length === 0) { + return '0.0' + } + + let tipsTotal: number = 0 + + list.map((item: Rewards.Publisher) => { + if (convertProbi) { + tipsTotal += parseFloat(convertProbiToFixed(item.percentage.toString())) + } else { + tipsTotal += item.percentage + } + }) + + return tipsTotal.toFixed(1) +} + export const constructBackupString = (backupKey: string) => { return `Brave Wallet Recovery Key\nDate created: ${new Date(Date.now()).toLocaleDateString()} \n\nRecovery Key: ${backupKey}` + '\n\nNote: This key is not stored on Brave servers. ' + diff --git a/components/brave_rewards/resources/ui/components/modalDonation/__snapshots__/spec.tsx.snap b/components/brave_rewards/resources/ui/components/modalDonation/__snapshots__/spec.tsx.snap index 9667f6961958..2089f1d4809e 100644 --- a/components/brave_rewards/resources/ui/components/modalDonation/__snapshots__/spec.tsx.snap +++ b/components/brave_rewards/resources/ui/components/modalDonation/__snapshots__/spec.tsx.snap @@ -35,9 +35,7 @@ exports[`ModalDonation tests basic tests matches the snapshot 1`] = ` >
- MISSING: donationTips -
+ />
- MISSING: type + MISSING: date
void id?: string + title: string } export default class ModalDonation extends React.PureComponent { render () { - const { id, onClose, rows } = this.props + const { id, onClose, rows, title } = this.props const numRows = rows && rows.length || 0 return ( - {getLocale('donationTips')} + {title} - MISSING: type + MISSING: date { customStyle }, { - content: getLocale('type'), + content: getLocale('date'), customStyle }, { diff --git a/components/brave_rewards/resources/ui/stories/modal.tsx b/components/brave_rewards/resources/ui/stories/modal.tsx index fb60111ccb08..54a788668081 100644 --- a/components/brave_rewards/resources/ui/stories/modal.tsx +++ b/components/brave_rewards/resources/ui/stories/modal.tsx @@ -472,6 +472,7 @@ storiesOf('Rewards/Modal', module) return ( ) diff --git a/components/resources/brave_components_strings.grd b/components/resources/brave_components_strings.grd index cbaa84b88d63..d0b3ec7d0122 100644 --- a/components/resources/brave_components_strings.grd +++ b/components/resources/brave_components_strings.grd @@ -227,12 +227,17 @@ Supported sites Tips Tip content creators directly as you browse. You can also set up recurring monthly tips so you can support sites continuously. + Monthly Contributions + Set up recurring monthly contributions so you can support sites continuously. + No monthly contributions set up yet. + Support your favorite sites with recurring monthly contributions. You can set this up from the tipping banner. Earnings are paid every month. Set your desired frequency to increase or decrease earnings. Reward creators for the content you love. Your monthly payment gets distributed across the sites you visit. Sites will appear as you browse Total tips this month + Total contributions this month Have you tipped your favorite content creator today? Add Funds Withdraw Funds