From 84d9eb6461bde7b53801e73df33a4a6369564569 Mon Sep 17 00:00:00 2001 From: zenparsing Date: Mon, 25 Jan 2021 16:02:21 -0500 Subject: [PATCH] Add BAP messaging UI --- browser/extensions/api/brave_rewards_api.cc | 19 ++++ browser/ui/webui/brave_webui_source.cc | 4 + .../browser/ads_service_impl_unittest.cc | 2 +- .../brave_new_tab_ui/api/initialData.ts | 10 +- .../default/rewards/bapDeprecationModal.tsx | 92 ++++++++++++++++++ .../containers/newTab/index.tsx | 2 + .../reducers/rewards_reducer.ts | 3 +- .../brave_rewards/browser/rewards_service.h | 2 +- .../browser/rewards_service_impl.cc | 15 ++- .../browser/rewards_service_impl.h | 2 +- .../browser/test/rewards_browsertest.cc | 77 +++++++++++++++ .../_locales/en_US/messages.json | 12 +++ .../brave_rewards/components/app.tsx | 26 +++++ .../assets/alert_background.svg | 15 +++ .../bap_deprecation_alert.style.ts | 75 ++++++++++++++ .../bap_deprecation/bap_deprecation_alert.tsx | 40 ++++++++ .../bap_deprecation_popup.style.ts | 56 +++++++++++ .../bap_deprecation/bap_deprecation_popup.tsx | 35 +++++++ .../bap_deprecation/icons/warning_icon.tsx | 9 ++ .../components/bap_deprecation/index.ts | 97 +++++++++++++++++++ .../bap_deprecation/stories/index.tsx | 56 +++++++++++ .../bap_deprecation/stories/locale_strings.ts | 11 +++ .../resources/brave_components_strings.grd | 12 +++ .../include/bat/ledger/option_keys.h | 2 + .../internal/contribution/contribution.cc | 13 +++ .../ledger/internal/promotion/promotion.cc | 10 +- .../ledger/internal/wallet/wallet_balance.cc | 10 +- 27 files changed, 698 insertions(+), 9 deletions(-) create mode 100644 components/brave_new_tab_ui/components/default/rewards/bapDeprecationModal.tsx create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/assets/alert_background.svg create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_alert.style.ts create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_alert.tsx create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_popup.style.ts create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_popup.tsx create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/icons/warning_icon.tsx create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/index.ts create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/stories/index.tsx create mode 100644 components/brave_rewards/resources/shared/components/bap_deprecation/stories/locale_strings.ts diff --git a/browser/extensions/api/brave_rewards_api.cc b/browser/extensions/api/brave_rewards_api.cc index d97f2d7160f9..08aa6d44b890 100644 --- a/browser/extensions/api/brave_rewards_api.cc +++ b/browser/extensions/api/brave_rewards_api.cc @@ -47,6 +47,25 @@ BraveRewardsOpenBrowserActionUIFunction::Run() { std::unique_ptr params( brave_rewards::OpenBrowserActionUI::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); + + auto* profile = Profile::FromBrowserContext(browser_context()); + + // Start the rewards ledger process if it is not already started + auto* rewards_service = RewardsServiceFactory::GetForProfile(profile); + if (!rewards_service) + return RespondNow(Error("Rewards service is not initialized")); + + rewards_service->StartProcess(base::DoNothing()); + + // Load the rewards extension if it is not already loaded + auto* extension_service = + extensions::ExtensionSystem::Get(profile)->extension_service(); + if (!extension_service) + return RespondNow(Error("Extension service is not initialized")); + + static_cast(extension_service->component_loader()) + ->AddRewardsExtension(); + std::string error; if (!BraveActionAPI::ShowActionUI(this, brave_rewards_extension_id, diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index a09baba88b4c..ed324b6bf777 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -252,6 +252,10 @@ 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 + // Rewards BAP Deprecation Alert + { "bapDeprecationAlertText", IDS_REWARDS_BAP_DEPRECATION_ALERT_TEXT }, + { "bapDeprecationHeader", IDS_REWARDS_BAP_DEPRECATION_HEADER }, + { "bapDeprecationOK", IDS_REWARDS_BAP_DEPRECATION_OK }, // Together Widget { "togetherWidgetTitle", IDS_TOGETHER_WIDGET_TITLE }, { "togetherWidgetWelcomeTitle", IDS_TOGETHER_WIDGET_WELCOME_TITLE }, diff --git a/components/brave_ads/browser/ads_service_impl_unittest.cc b/components/brave_ads/browser/ads_service_impl_unittest.cc index fce994f596df..6cc3c8e10d08 100644 --- a/components/brave_ads/browser/ads_service_impl_unittest.cc +++ b/components/brave_ads/browser/ads_service_impl_unittest.cc @@ -182,7 +182,7 @@ class MockRewardsService : public RewardsService { MOCK_METHOD1(DisconnectWallet, void(const std::string& wallet_type)); - MOCK_METHOD0(OnlyAnonWallet, bool()); + MOCK_CONST_METHOD0(OnlyAnonWallet, bool()); MOCK_METHOD1(AddPrivateObserver, void(RewardsServicePrivateObserver* observer)); diff --git a/components/brave_new_tab_ui/api/initialData.ts b/components/brave_new_tab_ui/api/initialData.ts index 0ef62740d66b..4cd157308c0a 100644 --- a/components/brave_new_tab_ui/api/initialData.ts +++ b/components/brave_new_tab_ui/api/initialData.ts @@ -24,6 +24,7 @@ export type InitialData = { export type PreInitialRewardsData = { enabledAds: boolean adsSupported: boolean + onlyAnonWallet: boolean } export type InitialRewardsData = { @@ -103,18 +104,23 @@ export async function getRewardsPreInitialData (): Promise chrome.braveRewards.getAdsEnabled((enabledAds: boolean) => { resolve(enabledAds) })), new Promise(resolve => chrome.braveRewards.getAdsSupported((adsSupported: boolean) => { resolve(adsSupported) + })), + new Promise(resolve => chrome.braveRewards.onlyAnonWallet((onlyAnonWallet: boolean) => { + resolve(onlyAnonWallet) })) ]) return { enabledAds, - adsSupported + adsSupported, + onlyAnonWallet } as PreInitialRewardsData } catch (err) { throw Error(err) diff --git a/components/brave_new_tab_ui/components/default/rewards/bapDeprecationModal.tsx b/components/brave_new_tab_ui/components/default/rewards/bapDeprecationModal.tsx new file mode 100644 index 000000000000..d3e12c8f3cd1 --- /dev/null +++ b/components/brave_new_tab_ui/components/default/rewards/bapDeprecationModal.tsx @@ -0,0 +1,92 @@ +/* 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 { getLocale } from '../../../../common/locale' + +import { WithThemeVariables } from '../../../../brave_rewards/resources/shared/components/with_theme_variables' +import { LocaleContext } from '../../../../brave_rewards/resources/shared/lib/locale_context' +import { + BAPDeprecationAlert, + shouldShowBAPAlert, + shouldShowBAPPopup, + saveBAPAlertDismissed, + saveBAPPopupShown +} from '../../../../brave_rewards/resources/shared/components/bap_deprecation' + +const locale = { + getString: getLocale +} + +function modalRequestInHash () { + return /^#?bap-deprecation$/i.test(window.location.hash) +} + +interface Props { + rewardsState: NewTab.RewardsWidgetState +} + +// A modal that will display a BAP deprecation notice for Rewards users +// in Japan. This component is temporary and should be removed in 1.23.x +export default function BAPDeprecationModal (props: Props) { + const { onlyAnonWallet, balance } = props.rewardsState + + const [popupShown, setPopupShown] = React.useState(false) + + const [showModal, setShowModal] = React.useState( + modalRequestInHash() || + shouldShowBAPAlert(onlyAnonWallet, balance.total)) + + React.useEffect(() => { + if (popupShown || showModal) { + return + } + if (shouldShowBAPPopup(onlyAnonWallet, balance.total)) { + setPopupShown(true) + saveBAPPopupShown() + const popupURL = 'brave_rewards_panel.html#bap-deprecation' + chrome.braveRewards.openBrowserActionUI(popupURL) + } + }, [popupShown, showModal, onlyAnonWallet, balance]) + + React.useEffect(() => { + if (modalRequestInHash()) { + window.location.hash = '' + } + + // Attach a hashchange listener while this component is rendered. If the + // BAP deprecation popup has been opened in the brave rewards extension + // panel and the user clicks "Learn more", it will update the hash on + // this page. + const onHashChange = () => { + if (modalRequestInHash()) { + window.location.hash = '' + setShowModal(true) + } + } + + window.addEventListener('hashchange', onHashChange) + return () => { window.removeEventListener('hashchange', onHashChange) } + }, []) + + if (!showModal) { + return null + } + + const onClose = () => { + setShowModal(false) + if (!popupShown) { + saveBAPAlertDismissed() + } + } + + return ( + + + + + + ) +} diff --git a/components/brave_new_tab_ui/containers/newTab/index.tsx b/components/brave_new_tab_ui/containers/newTab/index.tsx index 6bfebdb846b4..96afe43dd7ac 100644 --- a/components/brave_new_tab_ui/containers/newTab/index.tsx +++ b/components/brave_new_tab_ui/containers/newTab/index.tsx @@ -24,6 +24,7 @@ import BrandedWallpaperLogo from '../../components/default/brandedWallpaper/logo import { brandedWallpaperLogoClicked } from '../../api/brandedWallpaper' import BraveTodayHint from '../../components/default/braveToday/hint' import BraveToday from '../../components/default/braveToday' +import BAPDeprecationModal from '../../components/default/rewards/bapDeprecationModal' // Helpers import VisibilityTimer from '../../helpers/visibilityTimer' @@ -1162,6 +1163,7 @@ class NewTabPage extends React.Component { cardsHidden={this.allWidgetsHidden()} toggleCards={this.toggleAllCards} /> + ) } diff --git a/components/brave_new_tab_ui/reducers/rewards_reducer.ts b/components/brave_new_tab_ui/reducers/rewards_reducer.ts index 91e11335d997..fca2db5a3648 100644 --- a/components/brave_new_tab_ui/reducers/rewards_reducer.ts +++ b/components/brave_new_tab_ui/reducers/rewards_reducer.ts @@ -115,7 +115,8 @@ const rewardsReducer: Reducer = (state: NewTab.State, rewardsState: { ...state.rewardsState, enabledAds: preInitialRewardsDataPayload.enabledAds, - adsSupported: preInitialRewardsDataPayload.adsSupported + adsSupported: preInitialRewardsDataPayload.adsSupported, + onlyAnonWallet: preInitialRewardsDataPayload.onlyAnonWallet } } break diff --git a/components/brave_rewards/browser/rewards_service.h b/components/brave_rewards/browser/rewards_service.h index d346f9033c41..8657fa988311 100644 --- a/components/brave_rewards/browser/rewards_service.h +++ b/components/brave_rewards/browser/rewards_service.h @@ -322,7 +322,7 @@ class RewardsService : public KeyedService { virtual void DisconnectWallet(const std::string& wallet_type) = 0; - virtual bool OnlyAnonWallet() = 0; + virtual bool OnlyAnonWallet() const = 0; virtual void GetAnonWalletStatus(GetAnonWalletStatusCallback callback) = 0; diff --git a/components/brave_rewards/browser/rewards_service_impl.cc b/components/brave_rewards/browser/rewards_service_impl.cc index c6c6dd474940..73517d622e9d 100644 --- a/components/brave_rewards/browser/rewards_service_impl.cc +++ b/components/brave_rewards/browser/rewards_service_impl.cc @@ -1489,6 +1489,19 @@ void RewardsServiceImpl::ClearState(const std::string& name) { bool RewardsServiceImpl::GetBooleanOption(const std::string& name) const { DCHECK(!name.empty()); + if (name == ledger::option::kContributionsDisabledForBAPMigration) { + if (OnlyAnonWallet()) { + base::Time::Exploded cutoff_exploded{ + .year = 2021, .month = 3, .day_of_month = 13}; + base::Time cutoff; + DCHECK(base::Time::FromUTCExploded(cutoff_exploded, &cutoff)); + if (base::Time::Now() >= cutoff) { + return true; + } + } + return false; + } + const auto it = kBoolOptions.find(name); DCHECK(it != kBoolOptions.end()); @@ -3005,7 +3018,7 @@ void RewardsServiceImpl::ShowNotification( callback(ledger::type::Result::LEDGER_OK); } -bool RewardsServiceImpl::OnlyAnonWallet() { +bool RewardsServiceImpl::OnlyAnonWallet() const { const int32_t current_country = country_codes::GetCountryIDFromPrefs(profile_->GetPrefs()); diff --git a/components/brave_rewards/browser/rewards_service_impl.h b/components/brave_rewards/browser/rewards_service_impl.h index 8f15027c8916..1e45e50eaffc 100644 --- a/components/brave_rewards/browser/rewards_service_impl.h +++ b/components/brave_rewards/browser/rewards_service_impl.h @@ -303,7 +303,7 @@ class RewardsServiceImpl : public RewardsService, void DisconnectWallet(const std::string& wallet_type) override; - bool OnlyAnonWallet() override; + bool OnlyAnonWallet() const override; void GetAnonWalletStatus(GetAnonWalletStatusCallback callback) override; diff --git a/components/brave_rewards/browser/test/rewards_browsertest.cc b/components/brave_rewards/browser/test/rewards_browsertest.cc index 9d3a4decb782..196dd6733ce5 100644 --- a/components/brave_rewards/browser/test/rewards_browsertest.cc +++ b/components/brave_rewards/browser/test/rewards_browsertest.cc @@ -8,6 +8,8 @@ #include "base/containers/flat_map.h" #include "base/test/bind.h" +#include "base/time/time.h" +#include "base/time/time_override.h" #include "bat/ledger/internal/uphold/uphold_util.h" #include "brave/browser/brave_rewards/rewards_service_factory.h" #include "brave/common/brave_paths.h" @@ -23,12 +25,26 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/country_codes/country_codes.h" #include "components/network_session_configurator/common/network_switches.h" #include "content/public/test/browser_test.h" #include "net/dns/mock_host_resolver.h" // npm run test -- brave_browser_tests --filter=RewardsBrowserTest.* +namespace { + +base::Time GetDate(int year, int month, int day_of_month) { + base::Time time; + DCHECK(base::Time::FromUTCExploded( + base::Time::Exploded{ + .year = year, .month = month, .day_of_month = day_of_month}, + &time)); + return time; +} + +} // namespace + namespace rewards_browsertest { class RewardsBrowserTest : public InProcessBrowserTest { @@ -109,6 +125,18 @@ class RewardsBrowserTest : public InProcessBrowserTest { return url; } + double FetchBalance() { + double total = -1.0; + base::RunLoop run_loop; + rewards_service_->FetchBalance(base::BindLambdaForTesting( + [&](ledger::type::Result result, ledger::type::BalancePtr balance) { + total = balance ? balance->total : -1.0; + run_loop.Quit(); + })); + run_loop.Run(); + return total; + } + brave_rewards::RewardsServiceImpl* rewards_service_; std::unique_ptr https_server_; std::unique_ptr response_; @@ -428,4 +456,53 @@ IN_PROC_BROWSER_TEST_F(RewardsBrowserTest, DISABLED_UpholdLimitNoBAT) { } } +IN_PROC_BROWSER_TEST_F(RewardsBrowserTest, BAPCutoffNonJP) { + rewards_browsertest_util::StartProcess(rewards_service_); + rewards_browsertest_util::CreateWallet(rewards_service_); + rewards_service_->FetchPromotions(); + promotion_->WaitForPromotionInitialization(); + promotion_->ClaimPromotionViaCode(); + + { + base::subtle::ScopedTimeClockOverrides time_override( + []() { return GetDate(2021, 3, 13); }, nullptr, nullptr); + ASSERT_EQ(FetchBalance(), 30.0); + } +} + +IN_PROC_BROWSER_TEST_F(RewardsBrowserTest, BAPCutoffBefore) { + rewards_browsertest_util::StartProcess(rewards_service_); + rewards_browsertest_util::CreateWallet(rewards_service_); + rewards_service_->FetchPromotions(); + promotion_->WaitForPromotionInitialization(); + promotion_->ClaimPromotionViaCode(); + + browser()->profile()->GetPrefs()->SetInteger( + country_codes::kCountryIDAtInstall, 19024); + + { + base::subtle::ScopedTimeClockOverrides time_override( + []() { return GetDate(2021, 3, 13) - base::TimeDelta::FromSeconds(1); }, + nullptr, nullptr); + ASSERT_EQ(FetchBalance(), 30.0); + } +} + +IN_PROC_BROWSER_TEST_F(RewardsBrowserTest, BAPCutoffAfter) { + rewards_browsertest_util::StartProcess(rewards_service_); + rewards_browsertest_util::CreateWallet(rewards_service_); + rewards_service_->FetchPromotions(); + promotion_->WaitForPromotionInitialization(); + promotion_->ClaimPromotionViaCode(); + + browser()->profile()->GetPrefs()->SetInteger( + country_codes::kCountryIDAtInstall, 19024); + + { + base::subtle::ScopedTimeClockOverrides time_override( + []() { return GetDate(2021, 3, 13); }, nullptr, nullptr); + ASSERT_EQ(FetchBalance(), 0.0); + } +} + } // namespace rewards_browsertest 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 66f3be0f5cf5..f0566ee07c8f 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 @@ -764,5 +764,17 @@ "onboardingPanelCompleteText": { "message": "By using Brave Rewards you are helping make the web a better place for everyone. And that’s awesome!", "description": "" + }, + "bapDeprecationHeader": { + "message": "Brave Rewards in Japan is switching to a new system!", + "description": "Header text for BAP deprecation warning message" + }, + "bapDeprecationLearnMore": { + "message": "Learn more", + "description": "Link to learn more about BAP deprecation" + }, + "bapDeprecationPopupText": { + "message": "Please use your BAP by March 13th. After that, BAP will be discontinued.", + "description": "Text for BAP deprecation popup" } } diff --git a/components/brave_rewards/resources/extension/brave_rewards/components/app.tsx b/components/brave_rewards/resources/extension/brave_rewards/components/app.tsx index 2165e56d62c0..9c58f3c6924e 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/components/app.tsx +++ b/components/brave_rewards/resources/extension/brave_rewards/components/app.tsx @@ -7,6 +7,8 @@ import { bindActionCreators, Dispatch } from 'redux' import { connect } from 'react-redux' import { getTabData } from '../background/api/tabs_api' +import { BAPDeprecationPopup } from '../../../shared/components/bap_deprecation' + // Components import Panel from './panel' @@ -174,6 +176,30 @@ export class RewardsPanel extends React.Component { } render () { + if (/^#?bap-deprecation$/i.test(window.location.hash)) { + const onLearnMore = () => { + const targetURL = 'chrome://newtab#bap-deprecation' + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + // If the currently active tab is the newtab page, then update + // the hash value for that page's URL. Otherwise, open a new + // tab pointed to the newtab page. + const [tab] = tabs + const tabID = tab ? tab.id : undefined + const tabURL = tab ? (tab.url || '') : '' + if (tabID && /^chrome:\/\/newtab(\/|$)/i.test(tabURL)) { + chrome.tabs.update(tabID, { url: targetURL }) + } else { + chrome.tabs.create({ url: targetURL }) + } + window.close() + }) + } + + return ( + + ) + } + return ( + + + + + + + + + + + + + + diff --git a/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_alert.style.ts b/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_alert.style.ts new file mode 100644 index 000000000000..dd2cc976d1bf --- /dev/null +++ b/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_alert.style.ts @@ -0,0 +1,75 @@ +/* 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 styled from 'styled-components' + +import bannerBackground from './assets/alert_background.svg' + +export const root = styled.div` + background-image: url('${bannerBackground}'); + background-repeat: no-repeat; + background-size: contain; + background-color: var(--brave-palette-white); + box-shadow: 0px 0px 16px rgba(99, 105, 110, 0.2); + border-radius: 8px; + width: 440px; + padding: 12px; + font-family: var(--brave-font-heading); + + .icon { + color: #fff; + } +` + +export const banner = styled.div` + color: var(--brave-palette-white); + text-align: center; + margin-top: -12px; + + .icon { + height: 35px; + width: auto; + } +` + +export const content = styled.div` + padding: 30px 22px 18px; +` + +export const header = styled.div` + text-align: center; + font-weight: 600; + font-size: 22px; + line-height: 32px; +` + +export const text = styled.div` + margin-top: 9px; + font-size: 14px; + line-height: 22px; + color: var(--brave-palette-neutral700); +` + +export const action = styled.div` + margin-top: 25px; + text-align: center; + + button { + color: var(--brave-palette-white); + min-width: 160px; + padding: 10px 20px; + font-weight: 600; + font-size: 14px; + line-height: 22px; + border: none; + border-radius: 40px; + background: var(--brave-color-brandBat); + cursor: pointer; + text-transform: uppercase; + } + + button:active { + background: var(--brave-color-brandBatActive); + } +` diff --git a/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_alert.tsx b/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_alert.tsx new file mode 100644 index 000000000000..7e4b9a4f7026 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_alert.tsx @@ -0,0 +1,40 @@ +/* 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 { LocaleContext } from '../../lib/locale_context' +import { Modal, ModalCloseButton } from '../modal' +import { WarningIcon } from './icons/warning_icon' + +import * as style from './bap_deprecation_alert.style' + +interface Props { + onClose: () => void +} + +export function BAPDeprecationAlert (props: Props) { + const { getString } = React.useContext(LocaleContext) + return ( + + + + + + + {getString('bapDeprecationHeader')} + + + {getString('bapDeprecationAlertText')} + + + + + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_popup.style.ts b/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_popup.style.ts new file mode 100644 index 000000000000..eb2c1fce0274 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_popup.style.ts @@ -0,0 +1,56 @@ +/* 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 styled from 'styled-components' + +export const root = styled.div` + background: linear-gradient(328.73deg, #A1A8F2 8.09%, #4C54D2 97.6%); + color: var(--brave-palette-white); + font-family: var(--brave-font-heading); + font-size: 14px; + font-weight: 600; + line-height: 22px; + width: 248px; + padding: 28px 16px 16px; + + .icon { + color: var(--brave-palette-white); + } +` + +export const content = styled.div`` + +export const heading = styled.div` + .icon { + vertical-align: middle; + margin-bottom: 3px; + margin-right: 4px; + height: 20px; + width: 20px; + } +` + +export const text = styled.div` + color: rgba(255, 255, 255, 0.7); +` + +export const action = styled.div` + text-align: center; + margin-top: 16px; + + button { + cursor: pointer; + font-weight: 600; + font-size: 13px; + line-height: 19px; + padding: 10px 40px; + border: solid 1px var(--brave-palette-white); + border-radius: 26px; + background: none; + } + + button:active { + background: #A1A8F2; + } +` diff --git a/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_popup.tsx b/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_popup.tsx new file mode 100644 index 000000000000..90a4f8c58e24 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/bap_deprecation/bap_deprecation_popup.tsx @@ -0,0 +1,35 @@ +/* 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 { LocaleContext } from '../../lib/locale_context' +import { WarningIcon } from './icons/warning_icon' + +import * as style from './bap_deprecation_popup.style' + +interface Props { + onLearnMore: () => void +} + +export function BAPDeprecationPopup (props: Props) { + const { getString } = React.useContext(LocaleContext) + return ( + + + + {getString('bapDeprecationHeader')} + + + {getString('bapDeprecationPopupText')} + + + + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/bap_deprecation/icons/warning_icon.tsx b/components/brave_rewards/resources/shared/components/bap_deprecation/icons/warning_icon.tsx new file mode 100644 index 000000000000..eb2bc568af67 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/bap_deprecation/icons/warning_icon.tsx @@ -0,0 +1,9 @@ +import * as React from 'react' + +export function WarningIcon () { + return ( + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/bap_deprecation/index.ts b/components/brave_rewards/resources/shared/components/bap_deprecation/index.ts new file mode 100644 index 000000000000..d036bfb61744 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/bap_deprecation/index.ts @@ -0,0 +1,97 @@ +/* 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/. */ + +export { BAPDeprecationAlert } from './bap_deprecation_alert' +export { BAPDeprecationPopup } from './bap_deprecation_popup' + +const bapAlertBegins = Date.parse('2021-03-06T00:00:00Z') +const bapCutoffBegins = Date.parse('2021-03-13T00:00:00Z') + +interface InteractionState { + popupShown: number + alertDismissed: number +} + +const interactionStateKey = 'bapDeprecationState' + +function loadInteractionState (): InteractionState { + let data: any + try { + data = JSON.parse(localStorage.getItem(interactionStateKey) || '{}') + } catch { + // Ignore + } + data = data || {} + return { + popupShown: Number(data.popupShown || 0), + alertDismissed: Number(data.alertDismissed || 0) + } +} + +function saveInteractionState (state: Partial) { + localStorage.setItem(interactionStateKey, JSON.stringify({ + ...loadInteractionState(), + ...state + })) +} + +export function saveBAPPopupShown () { + saveInteractionState({ popupShown: Date.now() }) +} + +export function saveBAPAlertDismissed () { + saveInteractionState({ alertDismissed: Date.now() }) +} + +function daysInPast (time: number) { + const span = Date.now() - time + return span / 1000 / 60 / 60 / 24 +} + +export function shouldShowBAPAlert (onlyAnonWallet: boolean, balance: number) { + // Do not show the modal if external wallets are supported or if + // the user has zero balance + if (!onlyAnonWallet || balance <= 0) { + return false + } + + // Do not show the modal before the alert start date + if (Date.now() < bapAlertBegins) { + return false + } + + const { alertDismissed } = loadInteractionState() + + if (alertDismissed) { + // Do not show the modal if the user has dismissed it within the last 3 days + // or if they have dismissed it and we are now past the BAP cutoff date + if (daysInPast(alertDismissed) < 3 || Date.now() >= bapCutoffBegins) { + return false + } + } + + return true +} + +export function shouldShowBAPPopup (onlyAnonWallet: boolean, balance: number) { + // Do not show the popup if external wallets are supported or if + // the user has zero balance + if (!onlyAnonWallet || balance <= 0) { + return false + } + + // Do not show the popup after the alert start date + if (Date.now() >= bapAlertBegins) { + return false + } + + const { popupShown } = loadInteractionState() + + // Do not show the popup if it has been shown within the last 3 days + if (popupShown && daysInPast(popupShown) < 3) { + return false + } + + return true +} diff --git a/components/brave_rewards/resources/shared/components/bap_deprecation/stories/index.tsx b/components/brave_rewards/resources/shared/components/bap_deprecation/stories/index.tsx new file mode 100644 index 000000000000..e67022c3f6ab --- /dev/null +++ b/components/brave_rewards/resources/shared/components/bap_deprecation/stories/index.tsx @@ -0,0 +1,56 @@ +/* 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 { storiesOf } from '@storybook/react' + +import { LocaleContext } from '../../../lib/locale_context' +import { WithThemeVariables } from '../../with_theme_variables' + +import { BAPDeprecationPopup } from '../bap_deprecation_popup' +import { BAPDeprecationAlert } from '../bap_deprecation_alert' + +import { localeStrings } from './locale_strings' + +const localeContext = { + getString (key: string) { + return localeStrings[key] || 'MISSING' + } +} + +function actionLogger (name: string) { + return (...args: any[]) => { + console.log(name, ...args) + } +} + +interface StoryWrapperProps { + children: React.ReactNode +} + +function StoryWrapper (props: StoryWrapperProps) { + return ( + + + {props.children} + + + ) +} + +storiesOf('Rewards/BAP Deprecation', module) + .add('Popup', () => { + return ( + + + + ) + }) + .add('Alert', () => { + return ( + + + + ) + }) diff --git a/components/brave_rewards/resources/shared/components/bap_deprecation/stories/locale_strings.ts b/components/brave_rewards/resources/shared/components/bap_deprecation/stories/locale_strings.ts new file mode 100644 index 000000000000..e35f62974bdc --- /dev/null +++ b/components/brave_rewards/resources/shared/components/bap_deprecation/stories/locale_strings.ts @@ -0,0 +1,11 @@ +/* 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/. */ + +export const localeStrings = { + bapDeprecationAlertText: 'The tipping feature will be unavailable from March 13th until April 13th. All BAPs will be discontinued by April 13th. Please use your BAP by March 13th. Any BAP remaining in your Brave browser after March 13th cannot be held or transferred by you or Brave, and will be lost. A new and improved system awaits!', + bapDeprecationHeader: 'Brave Rewards in Japan is switching to a new system!', + bapDeprecationLearnMore: 'Learn more', + bapDeprecationOK: 'OK', + bapDeprecationPopupText: 'Please use your BAP by March 13th. After that, BAP will be discontinued.' +} diff --git a/components/resources/brave_components_strings.grd b/components/resources/brave_components_strings.grd index d67f09fc5c3f..a835599776ce 100644 --- a/components/resources/brave_components_strings.grd +++ b/components/resources/brave_components_strings.grd @@ -955,6 +955,18 @@ > Hide sponsored images + + + The tipping feature will be unavailable from March 13th until April + 13th. All BAPs will be discontinued by April 13th. Please use your BAP + by March 13th. Any BAP remaining in your Brave browser after March 13th + cannot be held or transferred by you or Brave, and will be lost. A new + and improved system awaits! + + + Brave Rewards in Japan is switching to a new system! + + OK Edit Cards By clicking {{title}}, you agree to the $1Terms of Service$2 and $3Privacy Policy$4. Start using Brave Rewards diff --git a/vendor/bat-native-ledger/include/bat/ledger/option_keys.h b/vendor/bat-native-ledger/include/bat/ledger/option_keys.h index 7702a6eac85f..1c7fb87bb095 100644 --- a/vendor/bat-native-ledger/include/bat/ledger/option_keys.h +++ b/vendor/bat-native-ledger/include/bat/ledger/option_keys.h @@ -13,6 +13,8 @@ namespace option { const char kPublisherListRefreshInterval[] = "publisher_list_refresh_interval"; const char kClaimUGP[] = "claim_ugp"; +const char kContributionsDisabledForBAPMigration[] = + "contributions_disabled_for_bap_migration"; } // namespace option } // namespace ledger diff --git a/vendor/bat-native-ledger/src/bat/ledger/internal/contribution/contribution.cc b/vendor/bat-native-ledger/src/bat/ledger/internal/contribution/contribution.cc index bbf6de672fd6..93e417f7e4f9 100644 --- a/vendor/bat-native-ledger/src/bat/ledger/internal/contribution/contribution.cc +++ b/vendor/bat-native-ledger/src/bat/ledger/internal/contribution/contribution.cc @@ -19,6 +19,7 @@ #include "bat/ledger/internal/ledger_impl.h" #include "bat/ledger/internal/publisher/publisher_status_helper.h" #include "bat/ledger/internal/wallet/wallet_balance.h" +#include "bat/ledger/option_keys.h" using std::placeholders::_1; using std::placeholders::_2; @@ -148,6 +149,12 @@ void Contribution::ResetReconcileStamp() { } void Contribution::StartMonthlyContribution() { + if (ledger_->ledger_client()->GetBooleanOption( + option::kContributionsDisabledForBAPMigration)) { + BLOG(1, "Monthly contributions disabled for BAP migration"); + return; + } + const auto reconcile_stamp = ledger_->state()->GetReconcileStamp(); ResetReconcileStamp(); @@ -272,6 +279,12 @@ void Contribution::OneTimeTip( const std::string& publisher_key, const double amount, ledger::ResultCallback callback) { + if (ledger_->ledger_client()->GetBooleanOption( + option::kContributionsDisabledForBAPMigration)) { + BLOG(1, "One-time tips disabled for BAP migration"); + callback(type::Result::LEDGER_ERROR); + return; + } tip_->Process(publisher_key, amount, callback); } diff --git a/vendor/bat-native-ledger/src/bat/ledger/internal/promotion/promotion.cc b/vendor/bat-native-ledger/src/bat/ledger/internal/promotion/promotion.cc index c9156c19d000..9e39af56c20f 100644 --- a/vendor/bat-native-ledger/src/bat/ledger/internal/promotion/promotion.cc +++ b/vendor/bat-native-ledger/src/bat/ledger/internal/promotion/promotion.cc @@ -14,12 +14,13 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "bat/ledger/internal/common/time_util.h" +#include "bat/ledger/internal/constants.h" #include "bat/ledger/internal/credentials/credentials_util.h" #include "bat/ledger/internal/ledger_impl.h" #include "bat/ledger/internal/legacy/wallet_info_properties.h" #include "bat/ledger/internal/promotion/promotion_transfer.h" #include "bat/ledger/internal/promotion/promotion_util.h" -#include "bat/ledger/internal/constants.h" +#include "bat/ledger/option_keys.h" #include "wrapper.hpp" // NOLINT @@ -107,6 +108,13 @@ void Promotion::Initialize() { } void Promotion::Fetch(ledger::FetchPromotionCallback callback) { + if (ledger_->ledger_client()->GetBooleanOption( + option::kContributionsDisabledForBAPMigration)) { + BLOG(1, "Fetch promotions disabled for BAP migration"); + callback(type::Result::LEDGER_OK, {}); + return; + } + // If we fetched promotions recently, fulfill this request from the // database instead of querying the server again if (!ledger::is_testing) { diff --git a/vendor/bat-native-ledger/src/bat/ledger/internal/wallet/wallet_balance.cc b/vendor/bat-native-ledger/src/bat/ledger/internal/wallet/wallet_balance.cc index 1fd32be55214..11e7ccb8f148 100644 --- a/vendor/bat-native-ledger/src/bat/ledger/internal/wallet/wallet_balance.cc +++ b/vendor/bat-native-ledger/src/bat/ledger/internal/wallet/wallet_balance.cc @@ -11,8 +11,9 @@ #include #include "bat/ledger/global_constants.h" -#include "bat/ledger/internal/ledger_impl.h" #include "bat/ledger/internal/constants.h" +#include "bat/ledger/internal/ledger_impl.h" +#include "bat/ledger/option_keys.h" using std::placeholders::_1; using std::placeholders::_2; @@ -28,6 +29,13 @@ WalletBalance::WalletBalance(LedgerImpl* ledger) : WalletBalance::~WalletBalance() = default; void WalletBalance::Fetch(ledger::FetchBalanceCallback callback) { + if (ledger_->ledger_client()->GetBooleanOption( + option::kContributionsDisabledForBAPMigration)) { + BLOG(1, "Fetch balance disabled for BAP migration"); + callback(type::Result::LEDGER_OK, type::Balance::New()); + return; + } + // if we don't have user funds in anon card anymore // we can skip balance server ping if (!ledger_->state()->GetFetchOldBalanceEnabled()) {