From 785384c10f9416db918c2c267babe6227b79ad94 Mon Sep 17 00:00:00 2001 From: zenparsing Date: Wed, 28 Oct 2020 16:45:18 -0400 Subject: [PATCH] Add rewards everywhere onboarding experience --- browser/extensions/api/brave_rewards_api.cc | 44 ++++++ browser/extensions/api/brave_rewards_api.h | 20 +++ browser/ui/webui/brave_rewards_page_ui.cc | 38 ++++++ browser/ui/webui/brave_tip_ui.cc | 42 ++++++ browser/ui/webui/brave_webui_source.cc | 12 ++ common/extensions/api/brave_rewards.json | 28 ++++ .../browser/ads_service_impl_unittest.cc | 2 + .../brave_rewards/browser/rewards_service.cc | 1 + .../brave_rewards/browser/rewards_service.h | 7 + .../browser/rewards_service_impl.cc | 27 ++++ .../browser/rewards_service_impl.h | 4 + .../browser/test/rewards_browsertest.cc | 4 + .../test/rewards_contribution_browsertest.cc | 4 + .../browser/test/rewards_flag_browsertest.cc | 4 + .../test/rewards_notification_browsertest.cc | 4 + .../test/rewards_promotion_browsertest.cc | 4 + .../test/rewards_publisher_browsertest.cc | 4 + .../browser/test/rewards_state_browsertest.cc | 4 + components/brave_rewards/common/pref_names.cc | 1 + components/brave_rewards/common/pref_names.h | 1 + .../resources/android_page/storage.ts | 2 +- .../_locales/en_US/messages.json | 16 +++ .../actions/rewards_panel_actions.ts | 8 ++ .../reducers/rewards_panel_reducer.ts | 9 ++ .../brave_rewards/background/storage.ts | 3 +- .../brave_rewards/brave_rewards_panel.tsx | 23 +++- .../brave_rewards/components/panel.tsx | 23 ++++ .../constants/rewards_panel_types.ts | 2 + .../resources/page/actions/rewards_actions.ts | 12 +- .../resources/page/brave_rewards_page.tsx | 20 ++- .../resources/page/components/pageWallet.tsx | 22 ++- .../page/components/settingsPage.tsx | 1 + .../resources/page/constants/rewards_types.ts | 7 +- .../page/reducers/rewards_reducer.ts | 24 +++- .../brave_rewards/resources/page/storage.ts | 14 +- .../components/icons/close_icon.tsx | 0 .../resources/shared/components/modal.tsx | 50 +++++++ .../components/new_tab_link.tsx | 0 .../onboarding/assets/opt_in_modal_bg.svg | 22 +++ .../onboarding/icons/camera_icon.tsx | 13 ++ .../components/onboarding/icons/id_icon.tsx | 13 ++ .../shared/components/onboarding/index.tsx | 6 + .../onboarding/main_button.style.ts | 22 +++ .../components/onboarding/main_button.tsx | 20 +++ .../onboarding/rewards_opt_in_modal.style.ts | 128 ++++++++++++++++++ .../onboarding/rewards_opt_in_modal.tsx | 99 ++++++++++++++ .../components/onboarding/stories/index.tsx | 67 +++++++++ .../onboarding/stories/locale_strings.ts | 15 ++ .../onboarding/terms_of_service.tsx | 34 +++++ .../onboarding/tip_opt_in_form.style.ts | 72 ++++++++++ .../components/onboarding/tip_opt_in_form.tsx | 46 +++++++ .../components/with_theme_variables.tsx | 29 ++++ .../{tip => shared}/lib/locale_context.ts | 0 .../resources/tip/components/app.tsx | 15 +- .../resources/tip/components/app_error.tsx | 2 +- .../resources/tip/components/bat_string.tsx | 2 +- .../tip/components/current_monthly_form.tsx | 2 +- .../tip/components/monthly_tip_form.tsx | 2 +- .../tip/components/one_time_tip_form.tsx | 4 +- .../tip/components/opt_in_form.style.ts | 39 ++++++ .../resources/tip/components/opt_in_form.tsx | 34 +++++ .../tip/components/publisher_banner.tsx | 4 +- .../tip/components/terms_of_service.style.ts | 10 +- .../tip/components/terms_of_service.tsx | 4 +- .../resources/tip/components/tip_complete.tsx | 2 +- .../resources/tip/components/tip_form.tsx | 10 +- .../brave_rewards/resources/tip/lib/host.ts | 15 +- .../resources/tip/lib/interfaces.ts | 4 + .../resources/tip/lib/theme_loader.ts | 17 --- .../brave_rewards/resources/tip/main.tsx | 2 +- .../resources/tip/stories/index.tsx | 6 +- .../resources/tip/stories/locale_strings.ts | 8 +- components/definitions/chromel.d.ts | 2 + components/definitions/rewards.d.ts | 3 +- components/definitions/rewardsExtensions.d.ts | 1 + .../resources/brave_components_strings.grd | 10 ++ 76 files changed, 1206 insertions(+), 69 deletions(-) rename components/brave_rewards/resources/{tip => shared}/components/icons/close_icon.tsx (100%) create mode 100644 components/brave_rewards/resources/shared/components/modal.tsx rename components/brave_rewards/resources/{tip => shared}/components/new_tab_link.tsx (100%) create mode 100644 components/brave_rewards/resources/shared/components/onboarding/assets/opt_in_modal_bg.svg create mode 100644 components/brave_rewards/resources/shared/components/onboarding/icons/camera_icon.tsx create mode 100644 components/brave_rewards/resources/shared/components/onboarding/icons/id_icon.tsx create mode 100644 components/brave_rewards/resources/shared/components/onboarding/index.tsx create mode 100644 components/brave_rewards/resources/shared/components/onboarding/main_button.style.ts create mode 100644 components/brave_rewards/resources/shared/components/onboarding/main_button.tsx create mode 100644 components/brave_rewards/resources/shared/components/onboarding/rewards_opt_in_modal.style.ts create mode 100644 components/brave_rewards/resources/shared/components/onboarding/rewards_opt_in_modal.tsx create mode 100644 components/brave_rewards/resources/shared/components/onboarding/stories/index.tsx create mode 100644 components/brave_rewards/resources/shared/components/onboarding/stories/locale_strings.ts create mode 100644 components/brave_rewards/resources/shared/components/onboarding/terms_of_service.tsx create mode 100644 components/brave_rewards/resources/shared/components/onboarding/tip_opt_in_form.style.ts create mode 100644 components/brave_rewards/resources/shared/components/onboarding/tip_opt_in_form.tsx create mode 100644 components/brave_rewards/resources/shared/components/with_theme_variables.tsx rename components/brave_rewards/resources/{tip => shared}/lib/locale_context.ts (100%) create mode 100644 components/brave_rewards/resources/tip/components/opt_in_form.style.ts create mode 100644 components/brave_rewards/resources/tip/components/opt_in_form.tsx delete mode 100644 components/brave_rewards/resources/tip/lib/theme_loader.ts diff --git a/browser/extensions/api/brave_rewards_api.cc b/browser/extensions/api/brave_rewards_api.cc index 1514c72808b8..0c0db74d8a10 100644 --- a/browser/extensions/api/brave_rewards_api.cc +++ b/browser/extensions/api/brave_rewards_api.cc @@ -1166,5 +1166,49 @@ BraveRewardsIsInitializedFunction::Run() { OneArgument(std::make_unique(initialized))); } +BraveRewardsShouldShowOnboardingFunction:: +~BraveRewardsShouldShowOnboardingFunction() = default; + +ExtensionFunction::ResponseAction +BraveRewardsShouldShowOnboardingFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + auto* rewards_service = RewardsServiceFactory::GetForProfile(profile); + if (!rewards_service) { + return RespondNow(Error("Rewards service is not initialized")); + } + + const bool should_show = rewards_service->ShouldShowOnboarding(); + return RespondNow( + OneArgument(std::make_unique(should_show))); +} + +BraveRewardsSaveOnboardingResultFunction:: +~BraveRewardsSaveOnboardingResultFunction() = default; + +ExtensionFunction::ResponseAction +BraveRewardsSaveOnboardingResultFunction::Run() { + using ::brave_rewards::OnboardingResult; + + std::unique_ptr params( + brave_rewards::SaveOnboardingResult::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + Profile* profile = Profile::FromBrowserContext(browser_context()); + auto* rewards_service = RewardsServiceFactory::GetForProfile(profile); + if (!rewards_service) { + return RespondNow(Error("Rewards service is not initialized")); + } + + if (params->result == "opted-in") { + rewards_service->SaveOnboardingResult(OnboardingResult::kOptedIn); + } else if (params->result == "dismissed") { + rewards_service->SaveOnboardingResult(OnboardingResult::kDismissed); + } else { + NOTREACHED(); + } + + return RespondNow(NoArguments()); +} + } // namespace api } // namespace extensions diff --git a/browser/extensions/api/brave_rewards_api.h b/browser/extensions/api/brave_rewards_api.h index a38e2a9f302b..f208087611a3 100644 --- a/browser/extensions/api/brave_rewards_api.h +++ b/browser/extensions/api/brave_rewards_api.h @@ -455,6 +455,26 @@ class BraveRewardsIsInitializedFunction : public ExtensionFunction { ResponseAction Run() override; }; +class BraveRewardsShouldShowOnboardingFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.shouldShowOnboarding", UNKNOWN) + + protected: + ~BraveRewardsShouldShowOnboardingFunction() override; + + ResponseAction Run() override; +}; + +class BraveRewardsSaveOnboardingResultFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.saveOnboardingResult", UNKNOWN) + + protected: + ~BraveRewardsSaveOnboardingResultFunction() override; + + ResponseAction Run() override; +}; + } // namespace api } // namespace extensions diff --git a/browser/ui/webui/brave_rewards_page_ui.cc b/browser/ui/webui/brave_rewards_page_ui.cc index 9d3619d1c2b6..2e4825162b8c 100644 --- a/browser/ui/webui/brave_rewards_page_ui.cc +++ b/browser/ui/webui/brave_rewards_page_ui.cc @@ -199,6 +199,9 @@ class RewardsDOMHandler : public WebUIMessageHandler, void OnGetWalletPassphrase(const std::string& pass); + void GetOnboardingStatus(const base::ListValue* args); + void SaveOnboardingResult(const base::ListValue* args); + // RewardsServiceObserver implementation void OnFetchPromotions( brave_rewards::RewardsService* rewards_service, @@ -480,6 +483,12 @@ void RewardsDOMHandler::RegisterMessages() { web_ui()->RegisterMessageCallback("brave_rewards.getWalletPassphrase", base::BindRepeating(&RewardsDOMHandler::GetWalletPassphrase, base::Unretained(this))); + web_ui()->RegisterMessageCallback("brave_rewards.getOnboardingStatus", + base::BindRepeating(&RewardsDOMHandler::GetOnboardingStatus, + base::Unretained(this))); + web_ui()->RegisterMessageCallback("brave_rewards.saveOnboardingResult", + base::BindRepeating(&RewardsDOMHandler::SaveOnboardingResult, + base::Unretained(this))); } void RewardsDOMHandler::Init() { @@ -1922,6 +1931,35 @@ void RewardsDOMHandler::OnGetWalletPassphrase(const std::string& passphrase) { base::Value(passphrase)); } +void RewardsDOMHandler::GetOnboardingStatus(const base::ListValue* args) { + if (!rewards_service_ || !web_ui()->CanCallJavascript()) { + return; + } + base::Value data(base::Value::Type::DICTIONARY); + data.SetBoolKey("showOnboarding", rewards_service_->ShouldShowOnboarding()); + web_ui()->CallJavascriptFunctionUnsafe( + "brave_rewards.onboardingStatus", + data); +} + +void RewardsDOMHandler::SaveOnboardingResult(const base::ListValue* args) { + using brave_rewards::OnboardingResult; + + CHECK_EQ(1U, args->GetSize()); + if (!rewards_service_) { + return; + } + + const std::string result_type = args->GetList()[0].GetString(); + if (result_type == "opted-in") { + rewards_service_->SaveOnboardingResult(OnboardingResult::kOptedIn); + } else if (result_type == "dismissed") { + rewards_service_->SaveOnboardingResult(OnboardingResult::kDismissed); + } else { + NOTREACHED(); + } +} + } // namespace BraveRewardsPageUI::BraveRewardsPageUI(content::WebUI* web_ui, diff --git a/browser/ui/webui/brave_tip_ui.cc b/browser/ui/webui/brave_tip_ui.cc index 9d161eaf6103..bc8a7ec071bb 100644 --- a/browser/ui/webui/brave_tip_ui.cc +++ b/browser/ui/webui/brave_tip_ui.cc @@ -74,6 +74,8 @@ class TipMessageHandler : public WebUIMessageHandler, void DialogReady(const base::ListValue* args); void GetPublisherBanner(const base::ListValue* args); void GetRewardsParameters(const base::ListValue* args); + void GetOnboardingStatus(const base::ListValue* args); + void SaveOnboardingResult(const base::ListValue* args); void OnTip(const base::ListValue* args); void GetRecurringTips(const base::ListValue* args); void GetReconcileStamp(const base::ListValue* args); @@ -133,6 +135,18 @@ void TipMessageHandler::RegisterMessages() { &TipMessageHandler::GetRewardsParameters, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "getOnboardingStatus", + base::BindRepeating( + &TipMessageHandler::GetOnboardingStatus, + base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "saveOnboardingResult", + base::BindRepeating( + &TipMessageHandler::SaveOnboardingResult, + base::Unretained(this))); + web_ui()->RegisterMessageCallback( "onTip", base::BindRepeating( @@ -269,6 +283,34 @@ void TipMessageHandler::GetRewardsParameters(const base::ListValue* args) { weak_factory_.GetWeakPtr())); } +void TipMessageHandler::GetOnboardingStatus(const base::ListValue* args) { + if (!rewards_service_) { + return; + } + AllowJavascript(); + base::Value data(base::Value::Type::DICTIONARY); + data.SetBoolKey("showOnboarding", rewards_service_->ShouldShowOnboarding()); + FireWebUIListener("onboardingStatusUpdated", data); +} + +void TipMessageHandler::SaveOnboardingResult(const base::ListValue* args) { + using brave_rewards::OnboardingResult; + + CHECK_EQ(1U, args->GetSize()); + if (!rewards_service_) { + return; + } + + const std::string result_type = args->GetList()[0].GetString(); + if (result_type == "opted-in") { + rewards_service_->SaveOnboardingResult(OnboardingResult::kOptedIn); + } else if (result_type == "dismissed") { + rewards_service_->SaveOnboardingResult(OnboardingResult::kDismissed); + } else { + NOTREACHED(); + } +} + void TipMessageHandler::OnTip(const base::ListValue* args) { CHECK_EQ(3U, args->GetSize()); const std::string publisher_key = args->GetList()[0].GetString(); diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index 078d1ac66d98..d2de5b8df0df 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -621,6 +621,12 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "off", IDS_BRAVE_UI_OFF }, { "ok", IDS_BRAVE_UI_OK }, { "on", IDS_BRAVE_UI_ON }, + { "onboardingEarnHeader", IDS_BRAVE_REWARDS_ONBOARDING_EARN_HEADER }, + { "onboardingEarnText", IDS_BRAVE_REWARDS_ONBOARDING_EARN_TEXT }, + { "onboardingStartUsingRewards", IDS_BRAVE_REWARDS_ONBOARDING_START_USING_REWARDS }, // NOLINT + { "onboardingTerms", IDS_BRAVE_REWARDS_ONBOARDING_TERMS }, + { "onboardingTipHeader", IDS_BRAVE_REWARDS_ONBOARDING_TIP_HEADER }, + { "onboardingTipText", IDS_BRAVE_REWARDS_ONBOARDING_TIP_TEXT }, { "oneTime", IDS_BRAVE_UI_ONE_TIME }, { "oneTimeDonation", IDS_BRAVE_UI_ONE_TIME_DONATION }, { "openBalance", IDS_BRAVE_UI_OPEN_BALANCE }, @@ -850,8 +856,14 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "notEnoughTokens", IDS_BRAVE_UI_NOT_ENOUGH_TOKENS }, { "notEnoughTokensLink", IDS_BRAVE_UI_NOT_ENOUGH_TOKENS_LINK }, { "on", IDS_BRAVE_UI_ON }, + { "onboardingMaybeLater", IDS_BRAVE_REWARDS_ONBOARDING_MAYBE_LATER }, + { "onboardingStartUsingRewards", IDS_BRAVE_REWARDS_ONBOARDING_START_USING_REWARDS }, // NOLINT + { "onboardingTerms", IDS_BRAVE_REWARDS_ONBOARDING_TERMS }, + { "onboardingTipHeader", IDS_BRAVE_REWARDS_ONBOARDING_TIP_HEADER }, + { "onboardingTipText", IDS_BRAVE_REWARDS_ONBOARDING_TIP_TEXT }, { "oneTimeTip", IDS_BRAVE_REWARDS_TIP_ONE_TIME_TIP }, { "oneTimeTipAmount", IDS_BRAVE_REWARDS_TIP_ONE_TIME_TIP_AMOUNT }, + { "optInRequired", IDS_BRAVE_REWARDS_TIP_OPT_IN_REQUIRED }, { "points", IDS_BRAVE_UI_POINTS }, { "postHeader", IDS_BRAVE_REWARDS_TIP_POST_HEADER }, { "postHeaderTwitter", IDS_BRAVE_REWARDS_TIP_POST_HEADER_TWITTER }, diff --git a/common/extensions/api/brave_rewards.json b/common/extensions/api/brave_rewards.json index a167c4d10e47..7005f4f99328 100644 --- a/common/extensions/api/brave_rewards.json +++ b/common/extensions/api/brave_rewards.json @@ -1182,6 +1182,34 @@ ] } ] + }, + { + "name": "shouldShowOnboarding", + "type": "function", + "description": "Gets a value indicating whether rewards onboarding should be shown to the user.", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "showOnboarding", + "type": "boolean" + } + ] + } + ] + }, + { + "name": "saveOnboardingResult", + "type": "function", + "description": "Saves the rewards user onboarding result.", + "parameters": [ + { + "type": "string", + "name": "result" + } + ] } ] } diff --git a/components/brave_ads/browser/ads_service_impl_unittest.cc b/components/brave_ads/browser/ads_service_impl_unittest.cc index 5d638ed8dbad..e3decddff9e7 100644 --- a/components/brave_ads/browser/ads_service_impl_unittest.cc +++ b/components/brave_ads/browser/ads_service_impl_unittest.cc @@ -88,6 +88,8 @@ class MockRewardsService : public RewardsService { MOCK_METHOD1(GetAutoContributeEnabled, void(brave_rewards::GetAutoContributeEnabledCallback)); MOCK_METHOD1(SetAutoContributeEnabled, void(bool)); + MOCK_CONST_METHOD0(ShouldShowOnboarding, bool()); + MOCK_METHOD1(SaveOnboardingResult, void(brave_rewards::OnboardingResult)); MOCK_METHOD2(SetTimer, void(uint64_t, uint32_t*)); MOCK_METHOD4(GetPublisherActivityFromUrl, void(uint64_t, const std::string&, diff --git a/components/brave_rewards/browser/rewards_service.cc b/components/brave_rewards/browser/rewards_service.cc index c4a5582f95f6..6bf7c29e1996 100644 --- a/components/brave_rewards/browser/rewards_service.cc +++ b/components/brave_rewards/browser/rewards_service.cc @@ -61,6 +61,7 @@ void RewardsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { #if defined(OS_ANDROID) registry->RegisterBooleanPref(prefs::kUseRewardsStagingServer, false); #endif + registry->RegisterTimePref(prefs::kOnboarded, base::Time()); registry->RegisterUint64Pref(prefs::kPromotionLastFetchStamp, 0ull); registry->RegisterBooleanPref(prefs::kPromotionCorruptedMigrated, false); registry->RegisterBooleanPref(prefs::kAnonTransferChecked, false); diff --git a/components/brave_rewards/browser/rewards_service.h b/components/brave_rewards/browser/rewards_service.h index 19cd4fb813bf..37a09e04e7e7 100644 --- a/components/brave_rewards/browser/rewards_service.h +++ b/components/brave_rewards/browser/rewards_service.h @@ -135,6 +135,11 @@ using StartProcessCallback = using GetWalletPassphraseCallback = base::Callback; +enum class OnboardingResult { + kOptedIn, + kDismissed +}; + class RewardsService : public KeyedService { public: RewardsService(); @@ -200,6 +205,8 @@ class RewardsService : public KeyedService { virtual void GetAutoContributeEnabled( GetAutoContributeEnabledCallback callback) = 0; virtual void SetAutoContributeEnabled(bool enabled) = 0; + virtual bool ShouldShowOnboarding() const = 0; + virtual void SaveOnboardingResult(OnboardingResult result) = 0; virtual void GetBalanceReport( const uint32_t month, const uint32_t year, diff --git a/components/brave_rewards/browser/rewards_service_impl.cc b/components/brave_rewards/browser/rewards_service_impl.cc index c946a65841aa..5b4b47f6e9b8 100644 --- a/components/brave_rewards/browser/rewards_service_impl.cc +++ b/components/brave_rewards/browser/rewards_service_impl.cc @@ -1641,6 +1641,33 @@ void RewardsServiceImpl::SetAutoContributeEnabled(bool enabled) { } } +bool RewardsServiceImpl::ShouldShowOnboarding() const { + PrefService* prefs = profile_->GetPrefs(); + const base::Time onboard_time = prefs->GetTime(prefs::kOnboarded); + + bool ads_enabled = false; + bool ads_supported = true; + auto* ads_service = brave_ads::AdsServiceFactory::GetForProfile(profile_); + if (ads_service) { + ads_enabled = ads_service->IsEnabled(); + ads_supported = ads_service->IsSupportedLocale(); + } + + return onboard_time.is_null() && !ads_enabled && ads_supported; +} + +void RewardsServiceImpl::SaveOnboardingResult(OnboardingResult result) { + PrefService* prefs = profile_->GetPrefs(); + prefs->SetTime(prefs::kOnboarded, base::Time::Now()); + if (result == OnboardingResult::kOptedIn) { + SetAutoContributeEnabled(true); + auto* ads_service = brave_ads::AdsServiceFactory::GetForProfile(profile_); + if (ads_service) { + ads_service->SetEnabled(true); + } + } +} + void RewardsServiceImpl::OnAdsEnabled(bool ads_enabled) { if (ads_enabled) { StartLedger(base::DoNothing()); diff --git a/components/brave_rewards/browser/rewards_service_impl.h b/components/brave_rewards/browser/rewards_service_impl.h index 9e6ba6aa1952..28bb85e60587 100644 --- a/components/brave_rewards/browser/rewards_service_impl.h +++ b/components/brave_rewards/browser/rewards_service_impl.h @@ -308,6 +308,10 @@ class RewardsServiceImpl : public RewardsService, void SetAutoContributeEnabled(bool enabled) override; + bool ShouldShowOnboarding() const override; + + void SaveOnboardingResult(OnboardingResult result) override; + void GetMonthlyReport( const uint32_t month, const uint32_t year, diff --git a/components/brave_rewards/browser/test/rewards_browsertest.cc b/components/brave_rewards/browser/test/rewards_browsertest.cc index ebf727119bf3..3358796f734c 100644 --- a/components/brave_rewards/browser/test/rewards_browsertest.cc +++ b/components/brave_rewards/browser/test/rewards_browsertest.cc @@ -70,6 +70,10 @@ class RewardsBrowserTest : public InProcessBrowserTest { // Other contribution_->Initialize(browser(), rewards_service_); promotion_->Initialize(browser(), rewards_service_); + + // Bypass onboarding UX by default + rewards_service_->SaveOnboardingResult( + brave_rewards::OnboardingResult::kDismissed); } void TearDown() override { diff --git a/components/brave_rewards/browser/test/rewards_contribution_browsertest.cc b/components/brave_rewards/browser/test/rewards_contribution_browsertest.cc index ea71229f13de..0c679b99eaf0 100644 --- a/components/brave_rewards/browser/test/rewards_contribution_browsertest.cc +++ b/components/brave_rewards/browser/test/rewards_contribution_browsertest.cc @@ -71,6 +71,10 @@ class RewardsContributionBrowserTest : public InProcessBrowserTest { // Other promotion_->Initialize(browser(), rewards_service_); contribution_->Initialize(browser(), rewards_service_); + + // Bypass onboarding UX by default + rewards_service_->SaveOnboardingResult( + brave_rewards::OnboardingResult::kDismissed); } void TearDown() override { diff --git a/components/brave_rewards/browser/test/rewards_flag_browsertest.cc b/components/brave_rewards/browser/test/rewards_flag_browsertest.cc index 66b1bdb3f513..1cf17bf085fe 100644 --- a/components/brave_rewards/browser/test/rewards_flag_browsertest.cc +++ b/components/brave_rewards/browser/test/rewards_flag_browsertest.cc @@ -54,6 +54,10 @@ class RewardsFlagBrowserTest : public InProcessBrowserTest { &RewardsFlagBrowserTest::GetTestResponse, base::Unretained(this))); rewards_service_->SetLedgerEnvForTesting(); + + // Bypass onboarding UX by default + rewards_service_->SaveOnboardingResult( + brave_rewards::OnboardingResult::kDismissed); } void GetTestResponse( diff --git a/components/brave_rewards/browser/test/rewards_notification_browsertest.cc b/components/brave_rewards/browser/test/rewards_notification_browsertest.cc index e680f1018b4f..e036d57a460c 100644 --- a/components/brave_rewards/browser/test/rewards_notification_browsertest.cc +++ b/components/brave_rewards/browser/test/rewards_notification_browsertest.cc @@ -74,6 +74,10 @@ class RewardsNotificationBrowserTest contribution_->Initialize(browser(), rewards_service_); rewards_notification_service_ = rewards_service_->GetNotificationService(); rewards_notification_service_->AddObserver(this); + + // Bypass onboarding UX by default + rewards_service_->SaveOnboardingResult( + brave_rewards::OnboardingResult::kDismissed); } void TearDown() override { diff --git a/components/brave_rewards/browser/test/rewards_promotion_browsertest.cc b/components/brave_rewards/browser/test/rewards_promotion_browsertest.cc index ec1c71d8bd4c..fc06e83a3250 100644 --- a/components/brave_rewards/browser/test/rewards_promotion_browsertest.cc +++ b/components/brave_rewards/browser/test/rewards_promotion_browsertest.cc @@ -60,6 +60,10 @@ class RewardsPromotionBrowserTest : public InProcessBrowserTest { // Other promotion_->Initialize(browser(), rewards_service_); + + // Bypass onboarding UX by default + rewards_service_->SaveOnboardingResult( + brave_rewards::OnboardingResult::kDismissed); } void TearDown() override { diff --git a/components/brave_rewards/browser/test/rewards_publisher_browsertest.cc b/components/brave_rewards/browser/test/rewards_publisher_browsertest.cc index 18a4c1472fbf..a56ecf839ec3 100644 --- a/components/brave_rewards/browser/test/rewards_publisher_browsertest.cc +++ b/components/brave_rewards/browser/test/rewards_publisher_browsertest.cc @@ -58,6 +58,10 @@ class RewardsPublisherBrowserTest : public InProcessBrowserTest { &RewardsPublisherBrowserTest::GetTestResponse, base::Unretained(this))); rewards_service_->SetLedgerEnvForTesting(); + + // Bypass onboarding UX by default + rewards_service_->SaveOnboardingResult( + brave_rewards::OnboardingResult::kDismissed); } void TearDown() override { diff --git a/components/brave_rewards/browser/test/rewards_state_browsertest.cc b/components/brave_rewards/browser/test/rewards_state_browsertest.cc index d2df1f56f46e..1a3ca6d17b8a 100644 --- a/components/brave_rewards/browser/test/rewards_state_browsertest.cc +++ b/components/brave_rewards/browser/test/rewards_state_browsertest.cc @@ -64,6 +64,10 @@ class RewardsStateBrowserTest : public InProcessBrowserTest { &RewardsStateBrowserTest::GetTestResponse, base::Unretained(this))); rewards_service_->SetLedgerEnvForTesting(); + + // Bypass onboarding UX by default + rewards_service_->SaveOnboardingResult( + brave_rewards::OnboardingResult::kDismissed); } void GetTestResponse( diff --git a/components/brave_rewards/common/pref_names.cc b/components/brave_rewards/common/pref_names.cc index 571d2b0890d9..99fb6c9510e2 100644 --- a/components/brave_rewards/common/pref_names.cc +++ b/components/brave_rewards/common/pref_names.cc @@ -28,6 +28,7 @@ const char kUpholdAnonAddress[] = "brave.rewards.uphold_anon_address"; const char kBadgeText[] = "brave.rewards.badge_text"; const char kUseRewardsStagingServer[] = "brave.rewards.use_staging_server"; +const char kOnboarded[] = "brave.rewards.onboarded"; const char kPromotionLastFetchStamp[] = "brave.rewards.promotion_last_fetch_stamp"; const char kPromotionCorruptedMigrated[] = diff --git a/components/brave_rewards/common/pref_names.h b/components/brave_rewards/common/pref_names.h index 07abdb403c57..a4884f1eae2f 100644 --- a/components/brave_rewards/common/pref_names.h +++ b/components/brave_rewards/common/pref_names.h @@ -21,6 +21,7 @@ extern const char kNotificationStartupDelay[]; extern const char kExternalWallets[]; // DEPRECATED extern const char kBadgeText[]; extern const char kUseRewardsStagingServer[]; +extern const char kOnboarded[]; // Defined in native-ledger extern const char kServerPublisherListStamp[]; diff --git a/components/brave_rewards/resources/android_page/storage.ts b/components/brave_rewards/resources/android_page/storage.ts index eb4a2dfe7a4d..ae28d2f48317 100644 --- a/components/brave_rewards/resources/android_page/storage.ts +++ b/components/brave_rewards/resources/android_page/storage.ts @@ -28,7 +28,7 @@ export const defaultState: Rewards.State = { paymentIdCheck: true, walletRecoveryStatus: null, walletServerProblem: false, - onBoardingDisplayed: false + verifyOnboardingDisplayed: false }, autoContributeList: [], safetyNetFailed: false, diff --git a/components/brave_rewards/resources/extension/brave_rewards/_locales/en_US/messages.json b/components/brave_rewards/resources/extension/brave_rewards/_locales/en_US/messages.json index c1b037c083a6..c438f8d03e83 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 @@ -626,5 +626,21 @@ "walletVerificationNote3": { "message": "Note: Your transactions will be visible to Uphold once you authorize; for instance, they will be able to see the recipient and amount of your tips.", "description": "Displayed in login modal for uphold" + }, + "onboardingEarnHeader": { + "message": "Earn Tokens & Give Back", + "description": "" + }, + "onboardingEarnText": { + "message": "Earn tokens by viewing privacy-respecting ads and support your favorite sites and content creators automatically.", + "description": "" + }, + "onboardingStartUsingRewards": { + "message": "Start using Brave Rewards", + "description": "" + }, + "onboardingTerms": { + "message": "By proceeding, you agree to the $$1Terms of Service$$2 and $$3Privacy Policy$$4.", + "description": "" } } diff --git a/components/brave_rewards/resources/extension/brave_rewards/actions/rewards_panel_actions.ts b/components/brave_rewards/resources/extension/brave_rewards/actions/rewards_panel_actions.ts index aa1eeea8ae3a..da501406a921 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/actions/rewards_panel_actions.ts +++ b/components/brave_rewards/resources/extension/brave_rewards/actions/rewards_panel_actions.ts @@ -80,6 +80,14 @@ export const onEnabledAC = (enabled: boolean) => action(types.ON_ENABLED_AC, { enabled }) +export const onShouldShowOnboarding = (showOnboarding: boolean) => action(types.ON_SHOULD_SHOW_ONBOARDING, { + showOnboarding +}) + +export const saveOnboardingResult = (result: 'opted-in' | 'dismissed') => action(types.SAVE_ONBOARDING_RESULT, { + result +}) + export const onPublisherListNormalized = (properties: RewardsExtension.PublisherNormalized[]) => action(types.ON_PUBLISHER_LIST_NORMALIZED, { properties diff --git a/components/brave_rewards/resources/extension/brave_rewards/background/reducers/rewards_panel_reducer.ts b/components/brave_rewards/resources/extension/brave_rewards/background/reducers/rewards_panel_reducer.ts index 7d985948e148..5ba22556afab 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/background/reducers/rewards_panel_reducer.ts +++ b/components/brave_rewards/resources/extension/brave_rewards/background/reducers/rewards_panel_reducer.ts @@ -241,6 +241,15 @@ export const rewardsPanelReducer: Reducer = state.enabledAC = payload.enabled break } + case types.ON_SHOULD_SHOW_ONBOARDING: { + state = { ...state, showOnboarding: payload.showOnboarding } + break + } + case types.SAVE_ONBOARDING_RESULT: { + state = { ...state, showOnboarding: false } + chrome.braveRewards.saveOnboardingResult(payload.result) + break + } case types.ON_PUBLISHER_LIST_NORMALIZED: { const list = payload.properties let publishers: Record = state.publishers diff --git a/components/brave_rewards/resources/extension/brave_rewards/background/storage.ts b/components/brave_rewards/resources/extension/brave_rewards/background/storage.ts index 3f7b3b41ff0c..ac64c5a02f82 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/background/storage.ts +++ b/components/brave_rewards/resources/extension/brave_rewards/background/storage.ts @@ -26,7 +26,8 @@ export const defaultState: RewardsExtension.State = { total: 0, wallets: {} }, - initializing: true + initializing: true, + showOnboarding: false } export const load = (): RewardsExtension.State => { diff --git a/components/brave_rewards/resources/extension/brave_rewards/brave_rewards_panel.tsx b/components/brave_rewards/resources/extension/brave_rewards/brave_rewards_panel.tsx index 2352ec0f1cf4..b18e9cdff7dc 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/brave_rewards_panel.tsx +++ b/components/brave_rewards/resources/extension/brave_rewards/brave_rewards_panel.tsx @@ -14,26 +14,37 @@ require('emptykit.css') require('../../../../../ui/webui/resources/fonts/muli.css') require('../../../../../ui/webui/resources/fonts/poppins.css') +import { LocaleContext } from '../../shared/lib/locale_context' +import { WithThemeVariables } from '../../shared/components/with_theme_variables' + // Components import App from './components/app' // Utils -import { getUIMessages } from './background/api/locale_api' +import { getMessage, getUIMessages } from './background/api/locale_api' const store: Store = new Store({ portName: 'REWARDSPANEL' }) +const localeContext = { + getString: (key: string) => getMessage(key) +} + initLocale(getUIMessages()) store.ready().then( () => { render( - - - - - , + + + + + + + + + , document.getElementById('root')) }) .catch(() => { diff --git a/components/brave_rewards/resources/extension/brave_rewards/components/panel.tsx b/components/brave_rewards/resources/extension/brave_rewards/components/panel.tsx index cf90b40867ad..6444b6ce0c34 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/components/panel.tsx +++ b/components/brave_rewards/resources/extension/brave_rewards/components/panel.tsx @@ -11,6 +11,7 @@ import { Provider } from '../../../ui/components/profile' import { NotificationType, WalletState } from '../../../ui/components/walletWrapper' import { RewardsNotificationType } from '../constants/rewards_panel_types' import { Type as AlertType } from '../../../ui/components/alert' +import { RewardsOptInModal } from '../../../shared/components/onboarding' // Utils import * as rewardsPanelActions from '../actions/rewards_panel_actions' @@ -94,6 +95,10 @@ export class Panel extends React.Component { this.props.actions.onEnabledAC(enabled) }) + chrome.braveRewards.shouldShowOnboarding((showOnboarding: boolean) => { + this.props.actions.onShouldShowOnboarding(showOnboarding) + }) + this.actions.fetchPromotions() chrome.braveRewards.getBalanceReport(new Date().getMonth() + 1, new Date().getFullYear(), @@ -647,6 +652,23 @@ export class Panel extends React.Component { return (!walletStatus || walletStatus === 'unverified') && balance && balance.total < 25 } + showOnboarding () { + if (!this.props.rewardsPanelData.showOnboarding) { + return null + } + + const onEnable = () => this.actions.saveOnboardingResult('opted-in') + const onClose = () => this.actions.saveOnboardingResult('dismissed') + + return ( + + ) + } + render () { const { pendingContributionTotal, enabledAC, externalWallet, balance, parameters } = this.props.rewardsPanelData const publisher: RewardsExtension.Publisher | undefined = this.getPublisher() @@ -744,6 +766,7 @@ export class Panel extends React.Component { {...this.getWalletSummary()} /> + {this.showOnboarding()} ) } diff --git a/components/brave_rewards/resources/extension/brave_rewards/constants/rewards_panel_types.ts b/components/brave_rewards/resources/extension/brave_rewards/constants/rewards_panel_types.ts index 71af93f0b46f..929d5133639c 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/constants/rewards_panel_types.ts +++ b/components/brave_rewards/resources/extension/brave_rewards/constants/rewards_panel_types.ts @@ -19,6 +19,8 @@ export const enum types { ON_PROMOTION_FINISH = '@@rewards_panel/ON_PROMOTION_FINISH', ON_PENDING_CONTRIBUTIONS_TOTAL = '@@rewards_panel/ON_PENDING_CONTRIBUTIONS_TOTAL', ON_ENABLED_AC = '@@rewards_panel/ON_ENABLED_AC', + ON_SHOULD_SHOW_ONBOARDING = '@@rewards_panel/ON_SHOULD_SHOW_ONBOARDING', + SAVE_ONBOARDING_RESULT = '@@rewards_panel/SAVE_ONBOARDING_RESULT', ON_PUBLISHER_LIST_NORMALIZED = '@@rewards_panel/ON_PUBLISHER_LIST_NORMALIZED', ON_EXCLUDED_SITES_CHANGED = '@@rewards_panel/ON_EXCLUDED_SITES_CHANGED', ON_SETTING_SAVE = '@@rewards_panel/ON_SETTING_SAVE', diff --git a/components/brave_rewards/resources/page/actions/rewards_actions.ts b/components/brave_rewards/resources/page/actions/rewards_actions.ts index e818605d65fe..3a3d56e61537 100644 --- a/components/brave_rewards/resources/page/actions/rewards_actions.ts +++ b/components/brave_rewards/resources/page/actions/rewards_actions.ts @@ -262,7 +262,7 @@ export const onExternalWallet = (result: number, wallet: Rewards.ExternalWallet) wallet }) -export const onOnBoardingDisplayed = () => action(types.ON_ON_BOARDING_DISPLAYED) +export const onVerifyOnboardingDisplaed = () => action(types.ON_VERIFY_ONBOARDING_DISPLAYED) export const processRewardsPageUrl = (path: string, query: string) => action(types.PROCESS_REWARDS_PAGE_URL, { path, @@ -338,3 +338,13 @@ export const getWalletPassphrase = () => action(types.GET_WALLET_PASSPHRASE) export const onWalletPassphrase = (passphrase: string) => action(types.ON_WALLET_PASSPHRASE, { passphrase }) + +export const getOnboardingStatus = () => action(types.GET_ONBOARDING_STATUS) + +export const onOnboardingStatus = (showOnboarding: boolean) => action(types.ON_ONBOARDING_STATUS, { + showOnboarding +}) + +export const saveOnboardingResult = (result: 'opted-in' | 'dismissed') => action(types.SAVE_ONBOARDING_RESULT, { + result +}) diff --git a/components/brave_rewards/resources/page/brave_rewards_page.tsx b/components/brave_rewards/resources/page/brave_rewards_page.tsx index 2b2ef4700a64..3cb0b2b78b2d 100644 --- a/components/brave_rewards/resources/page/brave_rewards_page.tsx +++ b/components/brave_rewards/resources/page/brave_rewards_page.tsx @@ -9,6 +9,9 @@ import { initLocale } from 'brave-ui' import { bindActionCreators } from 'redux' require('emptykit.css') +import { LocaleContext } from '../shared/lib/locale_context' +import { WithThemeVariables } from '../shared/components/with_theme_variables' + // Components import App from './components/app' require('../../../../ui/webui/resources/fonts/muli.css') @@ -30,10 +33,18 @@ window.cr.define('brave_rewards', function () { initLocale(window.loadTimeData.data_) } + const localeContext = { + getString: (key: string) => self.loadTimeData.getString(key) + } + render( - + + + + + , document.getElementById('root')) @@ -250,6 +261,10 @@ window.cr.define('brave_rewards', function () { getActions().onWalletPassphrase(passphrase) } + function onboardingStatus (result: { showOnboarding: boolean }) { + getActions().onOnboardingStatus(result.showOnboarding) + } + return { initialize, rewardsParameters, @@ -295,7 +310,8 @@ window.cr.define('brave_rewards', function () { initialized, completeReset, paymentId, - walletPassphrase + walletPassphrase, + onboardingStatus } }) diff --git a/components/brave_rewards/resources/page/components/pageWallet.tsx b/components/brave_rewards/resources/page/components/pageWallet.tsx index b45cb73a4c7c..b5432b8832e7 100644 --- a/components/brave_rewards/resources/page/components/pageWallet.tsx +++ b/components/brave_rewards/resources/page/components/pageWallet.tsx @@ -5,6 +5,7 @@ import * as React from 'react' import { bindActionCreators, Dispatch } from 'redux' import { connect } from 'react-redux' +import { RewardsOptInModal } from '../../shared/components/onboarding' // Components import { ModalActivity, @@ -332,7 +333,7 @@ class PageWallet extends React.Component { return } - if (!ui.onBoardingDisplayed && externalWallet.status === 0) { + if (!ui.verifyOnboardingDisplayed && externalWallet.status === 0) { this.toggleVerifyModal() return } @@ -349,7 +350,7 @@ class PageWallet extends React.Component { } if (hideVerify) { - this.actions.onOnBoardingDisplayed() + this.actions.onVerifyOnboardingDisplayed() } this.handleUpholdLink() @@ -766,6 +767,22 @@ class PageWallet extends React.Component { return (!walletStatus || walletStatus === 'unverified') && balance && balance.total < 25 } + getOnboardingModal () { + if (!this.props.rewardsData.showOnboarding) { + return null + } + const onAddFunds = () => this.onFundsAction('add') + const onEnable = () => this.actions.saveOnboardingResult('opted-in') + const onClose = () => this.actions.saveOnboardingResult('dismissed') + return ( + + ) + } + render () { const { balance, @@ -857,6 +874,7 @@ class PageWallet extends React.Component { ? this.generateMonthlyReport() : null } + {this.getOnboardingModal()} ) } diff --git a/components/brave_rewards/resources/page/components/settingsPage.tsx b/components/brave_rewards/resources/page/components/settingsPage.tsx index 69d6e4e3d287..8816260186d0 100644 --- a/components/brave_rewards/resources/page/components/settingsPage.tsx +++ b/components/brave_rewards/resources/page/components/settingsPage.tsx @@ -141,6 +141,7 @@ class SettingsPage extends React.Component { this.actions.fetchPromotions() this.actions.getExternalWallet('uphold') + this.actions.getOnboardingStatus() if (window.location.pathname.length > 1) { const pathElements = window.location.pathname.split('/') diff --git a/components/brave_rewards/resources/page/constants/rewards_types.ts b/components/brave_rewards/resources/page/constants/rewards_types.ts index 1f6094a94200..f2c03088f484 100644 --- a/components/brave_rewards/resources/page/constants/rewards_types.ts +++ b/components/brave_rewards/resources/page/constants/rewards_types.ts @@ -72,7 +72,7 @@ export const enum types { ON_BALANCE = '@@rewards/ON_BALANCE', GET_EXTERNAL_WALLET = '@@rewards/GET_EXTERNAL_WALLET', ON_EXTERNAL_WALLET = '@@rewards/ON_EXTERNAL_WALLET', - ON_ON_BOARDING_DISPLAYED = '@@rewards/ON_ON_BOARDING_DISPLAYED', + ON_VERIFY_ONBOARDING_DISPLAYED = '@@rewards/ON_VERIFY_ONBOARDING_DISPLAYED', PROCESS_REWARDS_PAGE_URL = '@@rewards/PROCESS_REWARDS_PAGE_URL', ON_PROCESS_REWARDS_PAGE_URL = '@@rewards/ON_PROCESS_REWARDS_PAGE_URL', HIDE_REDIRECT_MODAL = '@@rewards/HIDE_REDIRECT_MODAL', @@ -94,5 +94,8 @@ export const enum types { ON_PAYMENT_ID = '@@rewards/ON_PAYMENT_ID', SET_FIRST_LOAD = '@@rewards/SET_FIRST_LOAD', GET_WALLET_PASSPHRASE = '@@rewards/GET_WALLLET_PASSPHRASE', - ON_WALLET_PASSPHRASE = '@@rewards/ON_WALLLET_PASSPHRASE' + ON_WALLET_PASSPHRASE = '@@rewards/ON_WALLLET_PASSPHRASE', + GET_ONBOARDING_STATUS = '@@rewards/GET_ONBOARDING_STATUS', + ON_ONBOARDING_STATUS = '@@rewards/ON_ONBOARDING_STATUS', + SAVE_ONBOARDING_RESULT = '@@rewards/SAVE_ONBOARDING_RESULT' } diff --git a/components/brave_rewards/resources/page/reducers/rewards_reducer.ts b/components/brave_rewards/resources/page/reducers/rewards_reducer.ts index 441d85c3d976..7dd54ab685a1 100644 --- a/components/brave_rewards/resources/page/reducers/rewards_reducer.ts +++ b/components/brave_rewards/resources/page/reducers/rewards_reducer.ts @@ -269,10 +269,10 @@ const rewardsReducer: Reducer = (state: Rewards.State break } - case types.ON_ON_BOARDING_DISPLAYED: { + case types.ON_VERIFY_ONBOARDING_DISPLAYED: { let ui = state.ui - ui.onBoardingDisplayed = true + ui.verifyOnboardingDisplayed = true state = { ...state, ui @@ -434,6 +434,26 @@ const rewardsReducer: Reducer = (state: Rewards.State state.firstLoad = action.payload.firstLoad break } + case types.GET_ONBOARDING_STATUS: { + chrome.send('brave_rewards.getOnboardingStatus') + break + } + case types.ON_ONBOARDING_STATUS: { + state = { + ...state, + showOnboarding: action.payload.showOnboarding + } + break + } + case types.SAVE_ONBOARDING_RESULT: { + chrome.send('brave_rewards.saveOnboardingResult', [action.payload.result]) + chrome.send('brave_rewards.getAutoContributeProperties') + state = { + ...state, + showOnboarding: false + } + break + } } return state diff --git a/components/brave_rewards/resources/page/storage.ts b/components/brave_rewards/resources/page/storage.ts index f0f95728c06b..3ffaaef540e5 100644 --- a/components/brave_rewards/resources/page/storage.ts +++ b/components/brave_rewards/resources/page/storage.ts @@ -28,7 +28,7 @@ export const defaultState: Rewards.State = { paymentIdCheck: true, walletRecoveryStatus: null, walletServerProblem: false, - onBoardingDisplayed: false, + verifyOnboardingDisplayed: false, promosDismissed: {} }, autoContributeList: [], @@ -76,7 +76,8 @@ export const defaultState: Rewards.State = { }, initializing: true, paymentId: '', - recoveryKey: '' + recoveryKey: '', + showOnboarding: false } const cleanData = (state: Rewards.State) => { @@ -88,6 +89,15 @@ const cleanData = (state: Rewards.State) => { state.parameters = defaultState.parameters } + // Name change: onBoardingDisplayed -> verifyOnboardingDisplayed + if (state.ui.verifyOnboardingDisplayed === undefined) { + const { ui } = state as any + if (ui.onBoardingDisplayed) { + ui.verifyOnboardingDisplayed = true + ui.onBoardingDisplayed = undefined + } + } + state.ui.modalRedirect = 'hide' return state diff --git a/components/brave_rewards/resources/tip/components/icons/close_icon.tsx b/components/brave_rewards/resources/shared/components/icons/close_icon.tsx similarity index 100% rename from components/brave_rewards/resources/tip/components/icons/close_icon.tsx rename to components/brave_rewards/resources/shared/components/icons/close_icon.tsx diff --git a/components/brave_rewards/resources/shared/components/modal.tsx b/components/brave_rewards/resources/shared/components/modal.tsx new file mode 100644 index 000000000000..a9364f3d051b --- /dev/null +++ b/components/brave_rewards/resources/shared/components/modal.tsx @@ -0,0 +1,50 @@ +/* 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 styled from 'styled-components' + +const styles = { + root: styled.div` + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: rgba(0, 0, 0, 0.33); + z-index: 9999; + display: flex; + flex-direction: column; + align-items: center; + padding: 0 20px; + `, + + topSpacer: styled.div` + flex: 45 0 auto; + `, + + content: styled.div` + flex: 0 0 auto; + `, + + bottomSpacer: styled.div` + flex: 55 0 auto; + ` +} + +interface Props { + children: React.ReactNode +} + +export function Modal (props: Props) { + return ( + + + + {props.children} + + + + ) +} diff --git a/components/brave_rewards/resources/tip/components/new_tab_link.tsx b/components/brave_rewards/resources/shared/components/new_tab_link.tsx similarity index 100% rename from components/brave_rewards/resources/tip/components/new_tab_link.tsx rename to components/brave_rewards/resources/shared/components/new_tab_link.tsx diff --git a/components/brave_rewards/resources/shared/components/onboarding/assets/opt_in_modal_bg.svg b/components/brave_rewards/resources/shared/components/onboarding/assets/opt_in_modal_bg.svg new file mode 100644 index 000000000000..a8af77847104 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/assets/opt_in_modal_bg.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/brave_rewards/resources/shared/components/onboarding/icons/camera_icon.tsx b/components/brave_rewards/resources/shared/components/onboarding/icons/camera_icon.tsx new file mode 100644 index 000000000000..ca4c26ab3fa5 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/icons/camera_icon.tsx @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react' + +export function CameraIcon () { + return ( + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/onboarding/icons/id_icon.tsx b/components/brave_rewards/resources/shared/components/onboarding/icons/id_icon.tsx new file mode 100644 index 000000000000..9e8b2da7e46e --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/icons/id_icon.tsx @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react' + +export function IdIcon () { + return ( + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/onboarding/index.tsx b/components/brave_rewards/resources/shared/components/onboarding/index.tsx new file mode 100644 index 000000000000..422e7a5b7831 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/index.tsx @@ -0,0 +1,6 @@ +/* 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 { RewardsOptInModal } from './rewards_opt_in_modal' +export { TipOptInForm } from './tip_opt_in_form' diff --git a/components/brave_rewards/resources/shared/components/onboarding/main_button.style.ts b/components/brave_rewards/resources/shared/components/onboarding/main_button.style.ts new file mode 100644 index 000000000000..bce9322fc132 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/main_button.style.ts @@ -0,0 +1,22 @@ +/* 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` + button { + color: var(--brave-palette-white); + background: var(--brave-color-brandBat); + border: none; + padding: 10px 40px; + border-radius: 21px; + font-weight: 600; + font-size: 14px; + line-height: 21px; + + &:active { + background: var(--brave-color-brandBatActive); + } + } +` diff --git a/components/brave_rewards/resources/shared/components/onboarding/main_button.tsx b/components/brave_rewards/resources/shared/components/onboarding/main_button.tsx new file mode 100644 index 000000000000..f980fa5916c0 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/main_button.tsx @@ -0,0 +1,20 @@ +/* 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 * as style from './main_button.style' + +interface Props { + onClick: () => void + children: React.ReactNode +} + +export function MainButton (props: Props) { + return ( + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/onboarding/rewards_opt_in_modal.style.ts b/components/brave_rewards/resources/shared/components/onboarding/rewards_opt_in_modal.style.ts new file mode 100644 index 000000000000..3fd0e202fd07 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/rewards_opt_in_modal.style.ts @@ -0,0 +1,128 @@ +/* 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 modalBackground from './assets/opt_in_modal_bg.svg' + +export const root = styled.div` + flex: 0 0 auto; + max-width: 335px; + padding: 17px; + font-family: var(--brave-font-heading); + text-align: center; + background-color: var(--brave-palette-white); + background-image: url(${modalBackground}); + background-repeat: no-repeat; + background-position: 4px -11px; + background-size: auto 200px; + box-shadow: 0px 0px 16px rgba(99, 105, 110, 0.2); + border-radius: 8px; +` + +export const close = styled.div` + color: var(--brave-palette-neutral600); + text-align: right; + + button { + margin: 0; + padding: 2px; + background: none; + border: none; + cursor: pointer; + } + + .icon { + display: block; + width: 14px; + height: auto; + } +` + +export const header = styled.div` + margin-top: 17px; + color: var(--brave-palette-black); + font-weight: 600; + font-size: 18px; + line-height: 22px; +` + +export const batIcon = styled.div` + display: inline-block; + vertical-align: middle; + width: 26px; + margin-right: 7px; +` + +export const text = styled.div` + margin: 8px 6px 0; + color: var(--brave-palette-neutral700); + font-size: 14px; + line-height: 24px; +` + +export const enable = styled.div` + margin-top: 40px; +` + +// NOTE: The "Add Funds" flow is currently disabled +export const addFunds = styled.div` + display: none; + + margin-top: 18px; + color: var(--brave-color-brandBat); + + button { + font-weight: 600; + font-size: 14px; + line-height: 21px; + border: 0; + background: 0; + margin: 0; + padding: 0; + cursor: pointer; + } +` + +export const addFundsIcon = styled.span` + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + margin-right: 5px; + margin-bottom: 2px; +` + +export const terms = styled.div` + margin: 32px 14px 15px; + color: var(--brave-palette-neutral600); + font-size: 11px; + line-height: 16px; + + a { + color: var(--brave-color-brandBat); + font-weight: 600; + text-decoration: none; + } +` + +export const addFundsItem = styled.div` + margin-top: 24px; + color: var(--brave-palette-neutral900); + font-weight: 600; + font-size: 14px; + line-height: 20px; + + .icon { + color: var(--brave-palette-neutral600); + width: 24px; + vertical-align: middle; + margin-right: 16px; + margin-bottom: 3px; + } +` + +export const addFundsAction = styled.div` + margin-top: 31px; +` diff --git a/components/brave_rewards/resources/shared/components/onboarding/rewards_opt_in_modal.tsx b/components/brave_rewards/resources/shared/components/onboarding/rewards_opt_in_modal.tsx new file mode 100644 index 000000000000..4edfac63f3e9 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/rewards_opt_in_modal.tsx @@ -0,0 +1,99 @@ +/* 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 { BatColorIcon, WalletAddIcon } from 'brave-ui/components/icons' + +import { LocaleContext } from '../../lib/locale_context' +import { Modal } from '../modal' +import { CloseIcon } from '../icons/close_icon' +import { TermsOfService } from './terms_of_service' +import { MainButton } from './main_button' +import { IdIcon } from './icons/id_icon' +import { CameraIcon } from './icons/camera_icon' + +import * as style from './rewards_opt_in_modal.style' + +interface Props { + onClose: () => void + onEnable: () => void + onAddFunds: () => void +} + +export function RewardsOptInModal (props: Props) { + const { getString } = React.useContext(LocaleContext) + const [showAddFunds, setShowAddFunds] = React.useState(false) + + const onClickAddFunds = () => setShowAddFunds(true) + + const onClickClose = () => { + if (showAddFunds) { + setShowAddFunds(false) + } else { + props.onClose() + } + } + + const renderOptIn = () => ( + <> + + + {getString('onboardingEarnHeader')} + + + {getString('onboardingEarnText')} + + + + {getString('onboardingStartUsingRewards')} + + + + + + + ) + + const renderAddFunds = () => ( + <> + + {getString('onboardingAddFundsHeader')} + + + {getString('onboardingAddFundsText')} + + + {getString('onboardingAddFundsId')} + + + {getString('onboardingAddFundsPhoto')} + + + + {getString('onboardingGoToUphold')} + + + + ) + + return ( + + + + + + {showAddFunds ? renderAddFunds() : renderOptIn()} + + + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/onboarding/stories/index.tsx b/components/brave_rewards/resources/shared/components/onboarding/stories/index.tsx new file mode 100644 index 000000000000..eea4d03c28b4 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/stories/index.tsx @@ -0,0 +1,67 @@ +/* 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 { RewardsOptInModal } from '../rewards_opt_in_modal' +import { TipOptInForm } from '../tip_opt_in_form' + +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 { + width?: number + height?: number + children: React.ReactNode +} + +function StoryWrapper (props: StoryWrapperProps) { + return ( + + +
+ {props.children} +
+
+
+ ) +} + +storiesOf('Rewards/Onboarding', module) + .add('Opt-in Modal', () => { + return ( + + + + ) + }) + .add('Tip Opt-in', () => { + return ( + + + + ) + }) diff --git a/components/brave_rewards/resources/shared/components/onboarding/stories/locale_strings.ts b/components/brave_rewards/resources/shared/components/onboarding/stories/locale_strings.ts new file mode 100644 index 000000000000..60997417bd06 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/stories/locale_strings.ts @@ -0,0 +1,15 @@ +export const localeStrings = { + onboardingAddFunds: 'Add funds', + onboardingAddFundsHeader: 'Add funds with Uphold', + onboardingAddFundsId: 'Drivers license or passport', + onboardingAddFundsPhoto: 'A clear image of your face', + onboardingAddFundsText: 'If you don’t have an existing account with Uphold, you will need to show proof of identity with the following:', + onboardingEarnHeader: 'Earn Tokens & Give Back', + onboardingEarnText: 'Earn tokens by viewing privacy-respecting ads and support your favorite sites and content creators automatically.', + onboardingGoToUphold: 'Take me to Uphold', + onboardingMaybeLater: 'Maybe later', + onboardingStartUsingRewards: 'Start using Brave Rewards', + onboardingTerms: 'By proceeding, you agree to the $1Terms of Service$2 and $3Privacy Policy$4.', + onboardingTipHeader: 'Tip with Brave rewards', + onboardingTipText: 'By using Brave rewards, you will start to gain BAT that you can use to tip creators.' +} diff --git a/components/brave_rewards/resources/shared/components/onboarding/terms_of_service.tsx b/components/brave_rewards/resources/shared/components/onboarding/terms_of_service.tsx new file mode 100644 index 000000000000..b7ade65a98c2 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/terms_of_service.tsx @@ -0,0 +1,34 @@ +/* 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 { NewTabLink } from '../new_tab_link' + +function formatMessageParts ( + message: string, + fn: (...parts: string[]) => T +) { + return fn(...message.split(/\$\d/g)) +} + +export function TermsOfService () { + const { getString } = React.useContext(LocaleContext) + return formatMessageParts(getString('onboardingTerms'), + (before, link1, between, link2, ...after) => ( + <> + {before} + + {link1} + + {between} + + {link2} + + {after.join()} + + )) +} diff --git a/components/brave_rewards/resources/shared/components/onboarding/tip_opt_in_form.style.ts b/components/brave_rewards/resources/shared/components/onboarding/tip_opt_in_form.style.ts new file mode 100644 index 000000000000..2f7038bde2f2 --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/tip_opt_in_form.style.ts @@ -0,0 +1,72 @@ +/* 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` + height: 100%; + display: flex; + flex-direction: column; + background: var(--brave-palette-white); + font-family: var(--brave-font-heading); + text-align: center; +` + +export const content = styled.div` + flex: 1 0 auto; + margin-top: 22px; + padding: 0 30px; +` + +export const batIcon = styled.div` + height: 55px; +` + +export const heading = styled.div` + font-weight: 600; + font-size: 18px; + line-height: 22px; + margin-top: 8px; +` + +export const text = styled.div` + margin: 4px auto 0; + color: var(--brave-palette-neutral700); + font-size: 14px; + line-height: 24px; + max-width: 300px; +` + +export const enable = styled.div` + margin-top: 40px; +` + +export const dismiss = styled.div` + margin-top: 8px; + + button { + border: 0; + padding: 0; + background: none; + color: var(--brave-palette-neutral600); + font-weight: 600; + font-size: 14px; + line-height: 22px; + cursor: pointer; + } +` + +export const footer = styled.div` + flex: 0 0 auto; + padding: 54px 40px 20px; + color: var(--brave-palette-neutral600); + font-size: 11px; + line-height: 16px; + + a { + color: var(--brave-color-brandBat); + font-weight: 600; + text-decoration: none; + } +` diff --git a/components/brave_rewards/resources/shared/components/onboarding/tip_opt_in_form.tsx b/components/brave_rewards/resources/shared/components/onboarding/tip_opt_in_form.tsx new file mode 100644 index 000000000000..258a9d152dcf --- /dev/null +++ b/components/brave_rewards/resources/shared/components/onboarding/tip_opt_in_form.tsx @@ -0,0 +1,46 @@ +/* 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 { BatColorIcon } from 'brave-ui/components/icons' + +import { LocaleContext } from '../../lib/locale_context' +import { TermsOfService } from './terms_of_service' +import { MainButton } from './main_button' + +import * as style from './tip_opt_in_form.style' + +interface Props { + onEnable: () => void + onDismiss: () => void +} + +export function TipOptInForm (props: Props) { + const { getString } = React.useContext(LocaleContext) + return ( + + + + {getString('onboardingTipHeader')} + + {getString('onboardingTipText')} + + + + {getString('onboardingStartUsingRewards')} + + + + + + + + + + + ) +} diff --git a/components/brave_rewards/resources/shared/components/with_theme_variables.tsx b/components/brave_rewards/resources/shared/components/with_theme_variables.tsx new file mode 100644 index 000000000000..2d6c2f11bbfe --- /dev/null +++ b/components/brave_rewards/resources/shared/components/with_theme_variables.tsx @@ -0,0 +1,29 @@ +/* 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 styled from 'styled-components' +import defaultTheme from 'brave-ui/theme/brave-default' + +function createThemeRules () { + let list = [] + + for (const [key, value] of Object.entries(defaultTheme.color)) { + list.push(`--brave-color-${key}: ${String(value)};`) + } + for (const [key, value] of Object.entries(defaultTheme.palette)) { + list.push(`--brave-palette-${key}: ${String(value)};`) + } + for (const [key, value] of Object.entries(defaultTheme.fontFamily)) { + list.push(`--brave-font-${key}: ${String(value)};`) + } + + return list.join('\n') +} + +const Wrapper = styled.div`${createThemeRules()}` + +export function WithThemeVariables (props: { children: React.ReactNode }) { + return {props.children} +} diff --git a/components/brave_rewards/resources/tip/lib/locale_context.ts b/components/brave_rewards/resources/shared/lib/locale_context.ts similarity index 100% rename from components/brave_rewards/resources/tip/lib/locale_context.ts rename to components/brave_rewards/resources/shared/lib/locale_context.ts diff --git a/components/brave_rewards/resources/tip/components/app.tsx b/components/brave_rewards/resources/tip/components/app.tsx index 6670306e6a1b..92bb2870de0e 100644 --- a/components/brave_rewards/resources/tip/components/app.tsx +++ b/components/brave_rewards/resources/tip/components/app.tsx @@ -5,12 +5,12 @@ import * as React from 'react' import { HostContext } from '../lib/host_context' -import { injectThemeVariables } from '../lib/theme_loader' +import { WithThemeVariables } from '../../shared/components/with_theme_variables' import { AppError } from './app_error' import { PublisherBanner } from './publisher_banner' import { TipForm } from './tip_form' -import { CloseIcon } from './icons/close_icon' +import { CloseIcon } from '../../shared/components/icons/close_icon' import * as style from './app.style' @@ -24,15 +24,8 @@ export function App () { }) }) - function onMount (element: HTMLElement | null) { - if (!element) { - return - } - injectThemeVariables(element) - } - return ( -
+ @@ -44,6 +37,6 @@ export function App () { {hostError ? : } -
+ ) } diff --git a/components/brave_rewards/resources/tip/components/app_error.tsx b/components/brave_rewards/resources/tip/components/app_error.tsx index 7f24ccafa9a8..65bd01f42112 100644 --- a/components/brave_rewards/resources/tip/components/app_error.tsx +++ b/components/brave_rewards/resources/tip/components/app_error.tsx @@ -5,7 +5,7 @@ import * as React from 'react' import { HostError } from '../lib/interfaces' -import { LocaleContext } from '../lib/locale_context' +import { LocaleContext } from '../../shared/lib/locale_context' import * as style from './app_error.style' diff --git a/components/brave_rewards/resources/tip/components/bat_string.tsx b/components/brave_rewards/resources/tip/components/bat_string.tsx index 44aed8fbec00..564e2a793d84 100644 --- a/components/brave_rewards/resources/tip/components/bat_string.tsx +++ b/components/brave_rewards/resources/tip/components/bat_string.tsx @@ -5,7 +5,7 @@ import * as React from 'react' import { HostContext } from '../lib/host_context' -import { LocaleContext } from '../lib/locale_context' +import { LocaleContext } from '../../shared/lib/locale_context' interface Props { stringKey?: string diff --git a/components/brave_rewards/resources/tip/components/current_monthly_form.tsx b/components/brave_rewards/resources/tip/components/current_monthly_form.tsx index 0aa177283cd5..d995fe11933f 100644 --- a/components/brave_rewards/resources/tip/components/current_monthly_form.tsx +++ b/components/brave_rewards/resources/tip/components/current_monthly_form.tsx @@ -5,7 +5,7 @@ import * as React from 'react' import { HostContext } from '../lib/host_context' -import { LocaleContext } from '../lib/locale_context' +import { LocaleContext } from '../../shared/lib/locale_context' import { formatTokenAmount } from '../lib/formatting' import { FormSubmitButton } from './form_submit_button' diff --git a/components/brave_rewards/resources/tip/components/monthly_tip_form.tsx b/components/brave_rewards/resources/tip/components/monthly_tip_form.tsx index 0ccb5ce99e95..d832b8346986 100644 --- a/components/brave_rewards/resources/tip/components/monthly_tip_form.tsx +++ b/components/brave_rewards/resources/tip/components/monthly_tip_form.tsx @@ -6,7 +6,7 @@ import * as React from 'react' import { PaymentKind, RewardsParameters, PublisherInfo } from '../lib/interfaces' import { HostContext } from '../lib/host_context' -import { LocaleContext } from '../lib/locale_context' +import { LocaleContext } from '../../shared/lib/locale_context' import { formatExchangeAmount } from '../lib/formatting' import { CurrentMonthlyForm } from './current_monthly_form' diff --git a/components/brave_rewards/resources/tip/components/one_time_tip_form.tsx b/components/brave_rewards/resources/tip/components/one_time_tip_form.tsx index 982eab2531ca..aca56ef4229a 100644 --- a/components/brave_rewards/resources/tip/components/one_time_tip_form.tsx +++ b/components/brave_rewards/resources/tip/components/one_time_tip_form.tsx @@ -14,7 +14,7 @@ import { } from '../lib/interfaces' import { HostContext } from '../lib/host_context' -import { Locale, LocaleContext } from '../lib/locale_context' +import { Locale, LocaleContext } from '../../shared/lib/locale_context' import { formatExchangeAmount, formatLocaleTemplate } from '../lib/formatting' import { FormSubmitButton } from './form_submit_button' @@ -22,7 +22,7 @@ import { PaymentKindSwitch } from './payment_kind_switch' import { TipAmountSelector } from './tip_amount_selector' import { BatString } from './bat_string' import { TermsOfService } from './terms_of_service' -import { NewTabLink } from './new_tab_link' +import { NewTabLink } from '../../shared/components/new_tab_link' import { PaperAirplaneIcon } from './icons/paper_airplane_icon' import * as style from './one_time_tip_form.style' diff --git a/components/brave_rewards/resources/tip/components/opt_in_form.style.ts b/components/brave_rewards/resources/tip/components/opt_in_form.style.ts new file mode 100644 index 000000000000..9ab709d2b83e --- /dev/null +++ b/components/brave_rewards/resources/tip/components/opt_in_form.style.ts @@ -0,0 +1,39 @@ +/* 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` + display: flex; + flex-direction: column; + height: 100%; +` + +export const topBar = styled.div` + flex: 0 0 auto; + text-align: center; + background: var(--brave-palette-grey200); + color: var(--brave-palette-neutral600); + font-weight: 600; + font-size: 14px; + line-height: 18px; + padding: 17px 10px; +` + +export const sadIcon = styled.span` + display: inline-block; + height: 22px; + width: 22px; + vertical-align: middle; + margin-bottom: 1px; + margin-right: 7px; +` + +export const content = styled.div` + flex: 1 0 auto; +` + +export const batIcon = styled.div` + height: 47px; +` diff --git a/components/brave_rewards/resources/tip/components/opt_in_form.tsx b/components/brave_rewards/resources/tip/components/opt_in_form.tsx new file mode 100644 index 000000000000..07f20a0cc5f6 --- /dev/null +++ b/components/brave_rewards/resources/tip/components/opt_in_form.tsx @@ -0,0 +1,34 @@ +/* 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 { EmoteSadIcon } from 'brave-ui/components/icons' + +import { HostContext } from '../lib/host_context' +import { LocaleContext } from '../../shared/lib/locale_context' + +import { TipOptInForm } from '../../shared/components/onboarding' + +import * as style from './opt_in_form.style' + +export function OptInForm () { + const host = React.useContext(HostContext) + const { getString } = React.useContext(LocaleContext) + + const onEnable = () => host.saveOnboardingResult('opted-in') + const onDismiss = () => host.saveOnboardingResult('dismissed') + + return ( + + + + {getString('optInRequired')} + + + + + + ) +} diff --git a/components/brave_rewards/resources/tip/components/publisher_banner.tsx b/components/brave_rewards/resources/tip/components/publisher_banner.tsx index d992eb0a45ef..1549af5611eb 100644 --- a/components/brave_rewards/resources/tip/components/publisher_banner.tsx +++ b/components/brave_rewards/resources/tip/components/publisher_banner.tsx @@ -24,11 +24,11 @@ import { } from '../lib/interfaces' import { HostContext } from '../lib/host_context' -import { Locale, LocaleContext } from '../lib/locale_context' +import { Locale, LocaleContext } from '../../shared/lib/locale_context' import { formatLocaleTemplate } from '../lib/formatting' import { MediaCard } from './media_card' -import { NewTabLink } from './new_tab_link' +import { NewTabLink } from '../../shared/components/new_tab_link' import { UnverifiedIcon } from './icons/unverified_icon' import { VerifiedIcon } from './icons/verified_icon' diff --git a/components/brave_rewards/resources/tip/components/terms_of_service.style.ts b/components/brave_rewards/resources/tip/components/terms_of_service.style.ts index 363740811923..2067e46061c4 100644 --- a/components/brave_rewards/resources/tip/components/terms_of_service.style.ts +++ b/components/brave_rewards/resources/tip/components/terms_of_service.style.ts @@ -6,13 +6,13 @@ import styled from 'styled-components' export const terms = styled.div` text-align: center; - padding: 0 10px 7px; - font-size: 10px; - line-height: 14px; - color: var(--brave-palette-grey700); + padding: 0 30px 7px; + font-size: 11px; + line-height: 16px; + color: var(--brave-palette-neutral600); a { font-weight: 600; - color: inherit; + color: var(--brave-color-brandBat); } ` diff --git a/components/brave_rewards/resources/tip/components/terms_of_service.tsx b/components/brave_rewards/resources/tip/components/terms_of_service.tsx index c95fcefcc9b9..d0d191e7eadf 100644 --- a/components/brave_rewards/resources/tip/components/terms_of_service.tsx +++ b/components/brave_rewards/resources/tip/components/terms_of_service.tsx @@ -4,9 +4,9 @@ import * as React from 'react' -import { LocaleContext } from '../lib/locale_context' +import { LocaleContext } from '../../shared/lib/locale_context' -import { NewTabLink } from './new_tab_link' +import { NewTabLink } from '../../shared/components/new_tab_link' import * as style from './terms_of_service.style' diff --git a/components/brave_rewards/resources/tip/components/tip_complete.tsx b/components/brave_rewards/resources/tip/components/tip_complete.tsx index 179d6d4c865f..44a0acce465c 100644 --- a/components/brave_rewards/resources/tip/components/tip_complete.tsx +++ b/components/brave_rewards/resources/tip/components/tip_complete.tsx @@ -8,7 +8,7 @@ import { TwitterColorIcon } from 'brave-ui/components/icons' import { TipKind } from '../lib/interfaces' import { HostContext } from '../lib/host_context' -import { LocaleContext } from '../lib/locale_context' +import { LocaleContext } from '../../shared/lib/locale_context' import { formatTokenAmount } from '../lib/formatting' import { BatString } from './bat_string' diff --git a/components/brave_rewards/resources/tip/components/tip_form.tsx b/components/brave_rewards/resources/tip/components/tip_form.tsx index c3f9c828d8b0..511de1e6119b 100644 --- a/components/brave_rewards/resources/tip/components/tip_form.tsx +++ b/components/brave_rewards/resources/tip/components/tip_form.tsx @@ -8,10 +8,11 @@ import { TwitterColorIcon, RedditColorIcon } from 'brave-ui/components/icons' import { TipKind, MediaMetaData } from '../lib/interfaces' import { HostContext } from '../lib/host_context' -import { Locale, LocaleContext } from '../lib/locale_context' +import { Locale, LocaleContext } from '../../shared/lib/locale_context' import { SliderSwitch, SliderSwitchOption } from './slider_switch' import { TipComplete } from './tip_complete' +import { OptInForm } from './opt_in_form' import { OneTimeTipForm } from './one_time_tip_form' import { MonthlyTipForm } from './monthly_tip_form' @@ -66,6 +67,8 @@ export function TipForm () { host.state.publisherInfo) const [rewardsParameters, setRewardsParameters] = React.useState( host.state.rewardsParameters) + const [showOnboarding, setShowOnboarding] = React.useState( + host.state.showOnboarding) const [currentMonthlyTip, setCurrentMonthlyTip] = React.useState( host.state.currentMonthlyTip || 0) @@ -85,6 +88,7 @@ export function TipForm () { setRewardsParameters(state.rewardsParameters) setPublisherInfo(state.publisherInfo) setBalanceInfo(state.balanceInfo) + setShowOnboarding(state.showOnboarding) setCurrentMonthlyTip(state.currentMonthlyTip || 0) }) }, [host]) @@ -97,6 +101,10 @@ export function TipForm () { return } + if (showOnboarding) { + return + } + const { mediaMetaData } = host.getDialogArgs() function onTipKindSelect (value: TipKind) { diff --git a/components/brave_rewards/resources/tip/lib/host.ts b/components/brave_rewards/resources/tip/lib/host.ts index ca3204a93329..ff01620a1a00 100644 --- a/components/brave_rewards/resources/tip/lib/host.ts +++ b/components/brave_rewards/resources/tip/lib/host.ts @@ -14,7 +14,8 @@ import { DialogArgs, EntryPoint, TipKind, - ShareTarget + ShareTarget, + OnboardingResult } from './interfaces' interface RecurringTipInfo { @@ -77,6 +78,7 @@ export function createHost (): Host { chrome.send('getOnlyAnonWallet') chrome.send('getRecurringTips') chrome.send('getPublisherBanner', [publisherKey]) + chrome.send('getOnboardingStatus') }, externalWalletUpdated (externalWalletInfo: ExternalWalletInfo) { @@ -91,6 +93,12 @@ export function createHost (): Host { stateManager.update({ rewardsParameters }) }, + onboardingStatusUpdated (result: { showOnboarding: boolean }) { + stateManager.update({ + showOnboarding: result.showOnboarding + }) + }, + recurringTipsUpdated (tips?: RecurringTipInfo[]) { if (!tips) { return @@ -182,6 +190,11 @@ export function createHost (): Host { chrome.send('dialogClose') }, + saveOnboardingResult (result: OnboardingResult) { + chrome.send('saveOnboardingResult', [result]) + stateManager.update({ showOnboarding: false }) + }, + processTip (amount: number, kind: TipKind) { if (!dialogArgs.publisherKey) { stateManager.update({ diff --git a/components/brave_rewards/resources/tip/lib/interfaces.ts b/components/brave_rewards/resources/tip/lib/interfaces.ts index b5a2fa34112b..16dab506579a 100644 --- a/components/brave_rewards/resources/tip/lib/interfaces.ts +++ b/components/brave_rewards/resources/tip/lib/interfaces.ts @@ -51,6 +51,8 @@ export type TipKind = 'one-time' | 'monthly' export type PaymentKind = 'bat' +export type OnboardingResult = 'opted-in' | 'dismissed' + export enum PublisherStatus { NOT_VERIFIED = 0, CONNECTED = 1, @@ -116,6 +118,7 @@ export interface HostState { nextReconcileDate?: Date currentMonthlyTip?: number onlyAnonWallet?: boolean + showOnboarding?: boolean tipProcessed?: boolean tipAmount?: number } @@ -127,6 +130,7 @@ export interface Host { getString: (key: string) => string getDialogArgs: () => DialogArgs closeDialog: () => void + saveOnboardingResult: (result: OnboardingResult) => void processTip: (amount: number, kind: TipKind) => void shareTip: (target: ShareTarget) => void addListener: (callback: HostListener) => () => void diff --git a/components/brave_rewards/resources/tip/lib/theme_loader.ts b/components/brave_rewards/resources/tip/lib/theme_loader.ts deleted file mode 100644 index 2fbc9a5d39ea..000000000000 --- a/components/brave_rewards/resources/tip/lib/theme_loader.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* 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 defaultTheme from 'brave-ui/theme/brave-default' - -export function injectThemeVariables (element: HTMLElement) { - for (const [key, value] of Object.entries(defaultTheme.color)) { - element.style.setProperty(`--brave-color-${key}`, String(value)) - } - for (const [key, value] of Object.entries(defaultTheme.palette)) { - element.style.setProperty(`--brave-palette-${key}`, String(value)) - } - for (const [key, value] of Object.entries(defaultTheme.fontFamily)) { - element.style.setProperty(`--brave-font-${key}`, String(value)) - } -} diff --git a/components/brave_rewards/resources/tip/main.tsx b/components/brave_rewards/resources/tip/main.tsx index 7be5370d85f6..e19a4546ce63 100644 --- a/components/brave_rewards/resources/tip/main.tsx +++ b/components/brave_rewards/resources/tip/main.tsx @@ -8,7 +8,7 @@ import * as ReactDOM from 'react-dom' import { App } from './components/app' import { createHost } from './lib/host' import { HostContext } from './lib/host_context' -import { LocaleContext } from './lib/locale_context' +import { LocaleContext } from '../shared/lib/locale_context' const host = createHost() diff --git a/components/brave_rewards/resources/tip/stories/index.tsx b/components/brave_rewards/resources/tip/stories/index.tsx index e479b2ba367b..abd85c3da2f0 100644 --- a/components/brave_rewards/resources/tip/stories/index.tsx +++ b/components/brave_rewards/resources/tip/stories/index.tsx @@ -8,7 +8,7 @@ import { storiesOf } from '@storybook/react' import { App } from '../components/app' import { DialogArgs, Host, HostState, MediaMetaData } from '../lib/interfaces' import { HostContext } from '../lib/host_context' -import { LocaleContext } from '../lib/locale_context' +import { LocaleContext } from '../../shared/lib/locale_context' import { localeStrings } from './locale_strings' @@ -97,6 +97,7 @@ function createHostState (): HostState { hostError: undefined, nextReconcileDate: new Date(Date.now() + 15 * 14 * 60 * 60 * 1000), onlyAnonWallet: false, + showOnboarding: false, tipProcessed: false, currentMonthlyTip: 0 } @@ -119,6 +120,9 @@ function createHost (): Host { closeDialog () { console.log('closeDialog') }, + saveOnboardingResult (result) { + console.log('saveOnboardingResult', result) + }, processTip (amount, kind) { console.log('processTip', amount, kind) }, diff --git a/components/brave_rewards/resources/tip/stories/locale_strings.ts b/components/brave_rewards/resources/tip/stories/locale_strings.ts index 93b2a9000807..e70b1f8db556 100644 --- a/components/brave_rewards/resources/tip/stories/locale_strings.ts +++ b/components/brave_rewards/resources/tip/stories/locale_strings.ts @@ -23,8 +23,14 @@ export const localeStrings = { notEnoughTokens: 'Not enough {{currency}}.', notEnoughTokensLink: 'Not enough {{currency}}. Please', on: 'on', + onboardingMaybeLater: 'Maybe later', + onboardingStartUsingRewards: 'Start using Brave Rewards', + onboardingTerms: 'By proceeding, you agree to the $1Terms of Service$2 and $3Privacy Policy$4.', + onboardingTipHeader: 'Tip with Brave rewards', + onboardingTipText: 'By using Brave rewards, you will start to gain BAT that you can use to tip creators.', oneTimeTip: 'One Time Tip', oneTimeTipAmount: 'One time tip amount:', + optInRequired: 'Hold on, you can’t tip yet', points: 'points', postHeader: '{{user}}’s post', postHeaderTwitter: '{{user}}’s tweet', @@ -35,7 +41,7 @@ export const localeStrings = { siteBannerNoticeText: 'This creator has not yet signed up to receive contributions from Brave users. Your browser will keep trying to contribute until they verify, or until 90 days have passed.', sorryToSeeYouGo: 'Sorry to see you go…', supportThisCreator: 'Support this creator', - termsOfService: 'By proceeding, you agree to the $1Terms of Service$2.', + termsOfService: 'By proceeding, you agree to the $1Terms of Service$2 and $3Privacy Policy$4.', thanksForTheSupport: 'Thanks for the support!', tipHasBeenSent: 'Your one time tip has been sent.', tipPostSubtitle: 'for their post', diff --git a/components/definitions/chromel.d.ts b/components/definitions/chromel.d.ts index c9a711aba44a..c1e3615378e3 100644 --- a/components/definitions/chromel.d.ts +++ b/components/definitions/chromel.d.ts @@ -154,6 +154,8 @@ declare namespace chrome.braveRewards { addListener: (callback: (result: RewardsExtension.Result) => void) => void } const isInitialized: (callback: (initialized: boolean) => void) => {} + const shouldShowOnboarding: (callback: (showOnboarding: boolean) => void) => {} + const saveOnboardingResult: (result: 'opted-in' | 'dismissed') => {} } declare namespace chrome.binance { diff --git a/components/definitions/rewards.d.ts b/components/definitions/rewards.d.ts index bbf3eafd9592..9aea20c514a9 100644 --- a/components/definitions/rewards.d.ts +++ b/components/definitions/rewards.d.ts @@ -62,6 +62,7 @@ declare namespace Rewards { recurringList: Publisher[] recurringLoad: boolean safetyNetFailed?: boolean + showOnboarding?: boolean tipsList: Publisher[] tipsLoad: boolean ui: { @@ -74,7 +75,7 @@ declare namespace Rewards { } walletRecoveryStatus: number | null walletServerProblem: boolean - onBoardingDisplayed?: boolean + verifyOnboardingDisplayed?: boolean onlyAnonWallet?: boolean } } diff --git a/components/definitions/rewardsExtensions.d.ts b/components/definitions/rewardsExtensions.d.ts index 2258cceec7af..69f3f2588042 100644 --- a/components/definitions/rewardsExtensions.d.ts +++ b/components/definitions/rewardsExtensions.d.ts @@ -13,6 +13,7 @@ declare namespace RewardsExtension { tipAmounts: Record externalWallet?: ExternalWallet initializing: boolean + showOnboarding: boolean } interface ApplicationState { diff --git a/components/resources/brave_components_strings.grd b/components/resources/brave_components_strings.grd index f5e15029db2a..072917164354 100644 --- a/components/resources/brave_components_strings.grd +++ b/components/resources/brave_components_strings.grd @@ -525,6 +525,7 @@ Next contribution date: One Time Tip One time tip amount: + Hold on, you can’t tip yet {{user}}’s post {{user}}’s tweet Sorry to see you go… @@ -535,6 +536,15 @@ for their post Tweet about your support + + Earn Tokens & Give Back + Earn tokens by viewing privacy-respecting ads and support your favorite sites and content creators automatically. + Maybe Later + Start using Brave Rewards + By proceeding, you agree to the $1Terms of Service$2 and $3Privacy Policy$4. + Tip with Brave rewards + By using Brave rewards, you will start to gain BAT that you can use to tip creators. + about Accept