diff --git a/browser/extensions/api/brave_rewards_api.cc b/browser/extensions/api/brave_rewards_api.cc index 21e4ece3fd9c..1393c79367f0 100644 --- a/browser/extensions/api/brave_rewards_api.cc +++ b/browser/extensions/api/brave_rewards_api.cc @@ -405,6 +405,19 @@ ExtensionFunction::ResponseAction BraveRewardsGetPublisherDataFunction::Run() { return RespondNow(NoArguments()); } +BraveRewardsGetWalletPropertiesFunction:: +~BraveRewardsGetWalletPropertiesFunction() = default; + +ExtensionFunction::ResponseAction +BraveRewardsGetWalletPropertiesFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + auto* rewards_service = RewardsServiceFactory::GetForProfile(profile); + if (rewards_service) { + rewards_service->FetchWalletProperties(); + } + return RespondNow(NoArguments()); +} + BraveRewardsGetCurrentReportFunction::~BraveRewardsGetCurrentReportFunction() { } diff --git a/browser/extensions/api/brave_rewards_api.h b/browser/extensions/api/brave_rewards_api.h index 5a4551dc291f..10f182dd888c 100644 --- a/browser/extensions/api/brave_rewards_api.h +++ b/browser/extensions/api/brave_rewards_api.h @@ -114,6 +114,16 @@ class BraveRewardsGetPublisherDataFunction : public ExtensionFunction { ResponseAction Run() override; }; +class BraveRewardsGetWalletPropertiesFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.getWalletProperties", UNKNOWN) + + protected: + ~BraveRewardsGetWalletPropertiesFunction() override; + + ResponseAction Run() override; +}; + class BraveRewardsGetCurrentReportFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("braveRewards.getCurrentReport", UNKNOWN) diff --git a/browser/ui/webui/brave_tip_ui.cc b/browser/ui/webui/brave_tip_ui.cc index fb56aa79c410..829f2970292c 100644 --- a/browser/ui/webui/brave_tip_ui.cc +++ b/browser/ui/webui/brave_tip_ui.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include "base/memory/weak_ptr.h" #include "base/strings/utf_string_conversions.h" @@ -177,6 +178,15 @@ void RewardsTipDOMHandler::GetWalletProperties(const base::ListValue* args) { rewards_service_->FetchWalletProperties(); } +static std::unique_ptr CreateListOfDoubles( + const std::vector& items) { + auto result = std::make_unique(); + for (double const& item : items) { + result->AppendDouble(item); + } + return result; +} + void RewardsTipDOMHandler::OnWalletProperties( brave_rewards::RewardsService* rewards_service, int error_code, @@ -191,11 +201,12 @@ void RewardsTipDOMHandler::OnWalletProperties( auto walletInfo = std::make_unique(); if (error_code == 0 && wallet_properties) { - auto choices = std::make_unique(); - for (double const& choice : wallet_properties->parameters_choices) { - choices->AppendDouble(choice); - } - walletInfo->SetList("choices", std::move(choices)); + walletInfo->SetList("choices", + CreateListOfDoubles(wallet_properties->parameters_choices)); + walletInfo->SetList("defaultTipChoices", + CreateListOfDoubles(wallet_properties->default_tip_choices)); + walletInfo->SetList("defaultMonthlyTipChoices", + CreateListOfDoubles(wallet_properties->default_monthly_tip_choices)); } result.SetDictionary("wallet", std::move(walletInfo)); diff --git a/common/extensions/api/brave_rewards.json b/common/extensions/api/brave_rewards.json index d18d8766b759..d938d6029593 100644 --- a/common/extensions/api/brave_rewards.json +++ b/common/extensions/api/brave_rewards.json @@ -156,34 +156,20 @@ "name": "properties", "type": "object", "properties": { - "probi": { - "type": "string", - "description": "balance represented in probis" - }, - "balance": { - "type": "double", - "description": "balance" + "defaultTipChoices": { + "type": "array", + "description": "default tip choices for one-time tips", + "items": { + "type": "number", + "minimum": 0 + } }, - "rates": { - "type": "object", - "description": "rates for different currencies", - "properties": { - "BTC": { - "type": "double", - "description": "BTC rate" - }, - "ETH": { - "type": "double", - "description": "ETH rate" - }, - "USD": { - "type": "double", - "description": "USD rate" - }, - "EUR": { - "type": "double", - "description": "EUR rate" - } + "defaultMonthlyTipChoices": { + "type": "array", + "description": "default tip choices for recurring tips", + "items": { + "type": "number", + "minimum": 0 } } } @@ -566,6 +552,12 @@ } ] }, + { + "name": "getWalletProperties", + "type": "function", + "description": "Get default values that we get from the server", + "parameters": [] + }, { "name": "getCurrentReport", "type": "function", diff --git a/components/brave_rewards/browser/extension_rewards_service_observer.cc b/components/brave_rewards/browser/extension_rewards_service_observer.cc index 1b9468f3b89f..030bcbf25668 100644 --- a/components/brave_rewards/browser/extension_rewards_service_observer.cc +++ b/components/brave_rewards/browser/extension_rewards_service_observer.cc @@ -53,6 +53,10 @@ void ExtensionRewardsServiceObserver::OnWalletProperties( std::unique_ptr wallet_properties) { auto* event_router = extensions::EventRouter::Get(profile_); + if (!event_router) { + return; + } + if (error_code == 17) { // ledger::Result::CORRUPT_WALLET std::unique_ptr args( extensions::api::brave_rewards::OnWalletInitialized::Create( @@ -64,6 +68,26 @@ void ExtensionRewardsServiceObserver::OnWalletProperties( event_router->BroadcastEvent(std::move(event)); return; } + + if (!wallet_properties) { + return; + } + + extensions::api::brave_rewards::OnWalletProperties::Properties properties; + + properties.default_tip_choices = wallet_properties->default_tip_choices; + properties.default_monthly_tip_choices = + wallet_properties->default_monthly_tip_choices; + + std::unique_ptr args( + extensions::api::brave_rewards::OnWalletProperties::Create(properties) + .release()); + + std::unique_ptr event(new extensions::Event( + extensions::events::BRAVE_ON_WALLET_PROPERTIES, + extensions::api::brave_rewards::OnWalletProperties::kEventName, + std::move(args))); + event_router->BroadcastEvent(std::move(event)); } void ExtensionRewardsServiceObserver::OnGetCurrentBalanceReport( diff --git a/components/brave_rewards/browser/rewards_service_browsertest.cc b/components/brave_rewards/browser/rewards_service_browsertest.cc index 6fdb8fa0f916..1a10489a024b 100644 --- a/components/brave_rewards/browser/rewards_service_browsertest.cc +++ b/components/brave_rewards/browser/rewards_service_browsertest.cc @@ -136,6 +136,8 @@ bool URLMatches(const std::string& url, return (url.find(target_url) == 0); } +enum class ContributionType { OneTimeTip, MonthlyTip }; + } // namespace namespace brave_test_resp { @@ -146,6 +148,7 @@ namespace brave_test_resp { std::string promotion_tokens_; std::string captcha_; std::string wallet_properties_; + std::string wallet_properties_defaults_; std::string uphold_auth_resp_; std::string uphold_transactions_resp_; std::string uphold_commit_resp_; @@ -273,6 +276,50 @@ class BraveRewardsBrowserTest verified.c_str()); } + std::vector GetSiteBannerTipOptions( + content::WebContents* site_banner) { + WaitForSelector(site_banner, "[data-test-id=amount-wrapper] div span"); + auto options = content::EvalJs( + site_banner, + R"( + const delay = t => new Promise(resolve => setTimeout(resolve, t)); + delay(500).then(() => Array.prototype.map.call( + document.querySelectorAll( + "[data-test-id=amount-wrapper] div span"), + node => parseFloat(node.innerText))) + )", + content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, + content::ISOLATED_WORLD_ID_CONTENT_END).ExtractList(); + + std::vector result; + for (const auto& value : options.GetList()) { + result.push_back(value.GetDouble()); + } + return result; + } + + static std::vector GetRewardsPopupTipOptions( + content::WebContents* popup) { + auto options = content::EvalJs( + popup, + R"_( + const delay = t => new Promise(resolve => setTimeout(resolve, t)); + delay(0).then(() => + Array.prototype.map.call( + document.querySelectorAll("option:not(:disabled)"), + node => parseFloat(node.value))) + )_", + content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, + content::ISOLATED_WORLD_ID_CONTENT_END).ExtractList(); + + std::vector result; + for (const auto& value : options.GetList()) { + result.push_back(value.GetDouble()); + } + return result; + } + + void GetTestResponse(const std::string& url, int32_t method, int* response_status_code, @@ -293,7 +340,11 @@ class BraveRewardsBrowserTest *response = brave_test_resp::verification_; } else if (URLMatches(url, WALLET_PROPERTIES, PREFIX_V2, ServerTypes::BALANCE)) { - *response = brave_test_resp::wallet_properties_; + if (show_defaults_in_properties_) { + *response = brave_test_resp::wallet_properties_defaults_; + } else { + *response = brave_test_resp::wallet_properties_; + } } else if (URLMatches(url, "/promotions?", PREFIX_V1, ServerTypes::kPromotion)) { *response = brave_test_resp::promotions_; @@ -313,7 +364,9 @@ class BraveRewardsBrowserTest *response = "[" "[\"bumpsmack.com\",\"publisher_verified\",false,\"address1\",{}]," - "[\"duckduckgo.com\",\"wallet_connected\",false,\"address2\",{}]" + "[\"duckduckgo.com\",\"wallet_connected\",false,\"address2\",{}]," + "[\"laurenwags.github.io\",\"wallet_connected\",false,\"address2\"," + "{\"donationAmounts\": [5,10,20]}]" "]"; } else { *response = @@ -323,7 +376,9 @@ class BraveRewardsBrowserTest "[\"3zsistemi.si\",\"wallet_connected\",false,\"address3\",{}]," "[\"site1.com\",\"wallet_connected\",false,\"address4\",{}]," "[\"site2.com\",\"wallet_connected\",false,\"address5\",{}]," - "[\"site3.com\",\"wallet_connected\",false,\"address6\",{}]" + "[\"site3.com\",\"wallet_connected\",false,\"address6\",{}]," + "[\"laurenwags.github.io\",\"wallet_connected\",false,\"address2\"," + "{\"donationAmounts\": [5,10,20]}]" "]"; } } else if (base::StartsWith( @@ -829,6 +884,9 @@ class BraveRewardsBrowserTest ASSERT_TRUE( base::ReadFileToString(path.AppendASCII("wallet_properties_resp.json"), &brave_test_resp::wallet_properties_)); + ASSERT_TRUE(base::ReadFileToString( + path.AppendASCII("wallet_properties_resp_defaults.json"), + &brave_test_resp::wallet_properties_defaults_)); ASSERT_TRUE(base::ReadFileToString( path.AppendASCII("uphold_auth_resp.json"), &brave_test_resp::uphold_auth_resp_)); @@ -874,7 +932,7 @@ class BraveRewardsBrowserTest return BalanceDoubleToString(external_balance_); } - std::string GetAnonBalance() { + std::string GetAnonBalance() const { return BalanceDoubleToString(balance_); } @@ -1180,31 +1238,16 @@ class BraveRewardsBrowserTest ASSERT_TRUE(js_result.ExtractBool()); } - void TipPublisher( - const std::string& publisher, - bool should_contribute = false, - bool monthly = false, - int32_t selection = 0) { - // we shouldn't be adding publisher to AC list, - // so that we can focus only on tipping part - rewards_service_->SetPublisherMinVisitTime(8); - - // Navigate to a site in a new tab - GURL url = https_server()->GetURL(publisher, "/index.html"); - ui_test_utils::NavigateToURLWithDisposition( - browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, - ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - - // Open the Rewards popup + content::WebContents* OpenSiteBanner(ContributionType banner_type) const { content::WebContents* popup_contents = OpenRewardsPopup(); - ASSERT_TRUE(popup_contents); - // Construct an observer to wait for the site banner to load + // Construct an observer to wait for the site banner to load. content::WindowedNotificationObserver site_banner_observer( content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, content::NotificationService::AllSources()); - const std::string buttonSelector = monthly + const std::string buttonSelector = + banner_type == ContributionType::MonthlyTip ? "[type='tip-monthly']" : "[type='tip']"; @@ -1240,10 +1283,37 @@ class BraveRewardsBrowserTest static_cast&>( site_banner_observer.source()); - content::WebContents* site_banner_contents = site_banner_source.ptr(); + // Allow the site banner to update its UI. We cannot use ExecJs here, + // because it does not resolve promises. + (void)EvalJs(site_banner_source.ptr(), + "new Promise(resolve => setTimeout(resolve, 0))", + content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, + content::ISOLATED_WORLD_ID_CONTENT_END); + + return site_banner_source.ptr(); + } + + void TipPublisher( + const std::string& publisher, + ContributionType type, + bool should_contribute = false, + int32_t selection = 0) { + // we shouldn't be adding publisher to AC list, + // so that we can focus only on tipping part + rewards_service_->SetPublisherMinVisitTime(8); + + // Navigate to a site in a new tab + GURL url = https_server()->GetURL(publisher, "/index.html"); + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + content::WebContents* site_banner_contents = OpenSiteBanner(type); ASSERT_TRUE(site_banner_contents); - const double amount = tip_amounts_.at(selection); + std::vector tip_options = + GetSiteBannerTipOptions(site_banner_contents); + const double amount = tip_options.at(selection); const std::string amount_str = base::StringPrintf("%2.1f", amount); // Select the tip amount (default is 1.0 BAT) @@ -1270,18 +1340,18 @@ class BraveRewardsBrowserTest // Signal that direct tip was made and update wallet with new // balance - if (!monthly && !should_contribute) { + if (type == ContributionType::OneTimeTip && !should_contribute) { UpdateContributionBalance(amount, should_contribute); } // Wait for thank you banner to load ASSERT_TRUE(WaitForLoadStop(site_banner_contents)); - const std::string confirmationText = monthly + const std::string confirmationText = type == ContributionType::MonthlyTip ? "Monthly contribution has been set!" : "Tip sent!"; - if (monthly) { + if (type == ContributionType::MonthlyTip) { WaitForRecurringTipToBeSaved(); // Trigger contribution process rewards_service()->StartMonthlyContributionForTest(); @@ -1300,7 +1370,7 @@ class BraveRewardsBrowserTest if (!should_contribute) { UpdateContributionBalance(amount, should_contribute); } - } else if (!monthly && should_contribute) { + } else if (type == ContributionType::OneTimeTip && should_contribute) { // Wait for reconciliation to complete WaitForTipReconcileCompleted(); ASSERT_EQ(tip_reconcile_status_, ledger::Result::LEDGER_OK); @@ -1334,7 +1404,7 @@ class BraveRewardsBrowserTest ASSERT_EQ(RewardsPageBalance(), ExpectedBalanceString()); // Check that tip table shows the appropriate tip amount - const std::string selector = monthly + const std::string selector = type == ContributionType::MonthlyTip ? "[data-test-id='summary-donation']" : "[data-test-id='summary-tips']"; @@ -1563,8 +1633,6 @@ class BraveRewardsBrowserTest rewards_service_->OnTip(publisher_key, amount, recurring, std::move(site)); } - const std::vector tip_amounts_ = {1.0, 5.0, 10.0}; - MOCK_METHOD1(OnGetEnvironment, void(ledger::Environment)); MOCK_METHOD1(OnGetDebug, void(bool)); MOCK_METHOD1(OnGetReconcileTime, void(int32_t)); @@ -1618,6 +1686,7 @@ class BraveRewardsBrowserTest bool last_publisher_added_ = false; bool alter_publisher_list_ = false; + bool show_defaults_in_properties_ = false; bool request_made_ = false; double balance_ = 0; double reconciled_tip_total_ = 0; @@ -2193,7 +2262,7 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, TipVerifiedPublisher) { ClaimPromotion(use_panel); // Tip verified publisher - TipPublisher("duckduckgo.com", true); + TipPublisher("duckduckgo.com", ContributionType::OneTimeTip, true); // Stop observing the Rewards service rewards_service_->RemoveObserver(this); @@ -2212,7 +2281,7 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, TipUnverifiedPublisher) { ClaimPromotion(use_panel); // Tip unverified publisher - TipPublisher("brave.com"); + TipPublisher("brave.com", ContributionType::OneTimeTip); // Stop observing the Rewards service rewards_service_->RemoveObserver(this); @@ -2232,8 +2301,7 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, ClaimPromotion(use_panel); // Tip verified publisher - const bool monthly = true; - TipPublisher("duckduckgo.com", true, monthly); + TipPublisher("duckduckgo.com", ContributionType::MonthlyTip, true); // Stop observing the Rewards service rewards_service_->RemoveObserver(this); @@ -2253,8 +2321,7 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, ClaimPromotion(use_panel); // Tip verified publisher - const bool monthly = true; - TipPublisher("brave.com", false, monthly); + TipPublisher("brave.com", ContributionType::MonthlyTip, false); // Stop observing the Rewards service rewards_service_->RemoveObserver(this); @@ -2440,7 +2507,7 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, ClaimPromotion(use_panel); // Tip unverified publisher - TipPublisher(publisher); + TipPublisher(publisher, ContributionType::OneTimeTip); // Check that link for pending is shown { @@ -2638,13 +2705,13 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, ClaimPromotion(true); // Tip unverified publisher - TipPublisher("brave.com"); + TipPublisher("brave.com", ContributionType::OneTimeTip); rewards_service_->OnTip("brave.com", 5.0, false); UpdateContributionBalance(5.0, false); // update pending balance - TipPublisher("3zsistemi.si", false, false, 2); - TipPublisher("3zsistemi.si", false, false, 1); - TipPublisher("3zsistemi.si", false, false, 2); - TipPublisher("3zsistemi.si", false, false, 2); + TipPublisher("3zsistemi.si", ContributionType::OneTimeTip, false, 2); + TipPublisher("3zsistemi.si", ContributionType::OneTimeTip, false, 1); + TipPublisher("3zsistemi.si", ContributionType::OneTimeTip, false, 2); + TipPublisher("3zsistemi.si", ContributionType::OneTimeTip, false, 2); // Make sure that pending contribution box shows the correct // amount @@ -2714,6 +2781,72 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, rewards_service_->RemoveObserver(this); } +IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, RewardsPanelDefaultTipChoices) { + show_defaults_in_properties_ = true; + rewards_service_->AddObserver(this); + EnableRewards(); + + bool use_panel = true; + ClaimPromotion(use_panel); + + GURL url = https_server()->GetURL("3zsistemi.si", "/index.html"); + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + // Add a recurring tip of 10 BAT. + bool monthly = true; + TipViaCode("3zsistemi.si", 10, monthly, ledger::PublisherStatus::VERIFIED); + + content::WebContents* popup = OpenRewardsPopup(); + const auto tip_options = GetRewardsPopupTipOptions(popup); + ASSERT_EQ(tip_options, std::vector({ 0, 10, 20, 50 })); + + rewards_service_->RemoveObserver(this); +} + +IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, SiteBannerDefaultTipChoices) { + show_defaults_in_properties_ = true; + rewards_service_->AddObserver(this); + EnableRewards(); + + GURL url = https_server()->GetURL("3zsistemi.si", "/index.html"); + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + content::WebContents* site_banner = + OpenSiteBanner(ContributionType::OneTimeTip); + auto tip_options = GetSiteBannerTipOptions(site_banner); + ASSERT_EQ(tip_options, std::vector({ 5, 10, 20 })); + + site_banner = OpenSiteBanner(ContributionType::MonthlyTip); + tip_options = GetSiteBannerTipOptions(site_banner); + ASSERT_EQ(tip_options, std::vector({ 10, 20, 50 })); + + rewards_service_->RemoveObserver(this); +} + +IN_PROC_BROWSER_TEST_F( + BraveRewardsBrowserTest, + SiteBannerDefaultPublisherAmounts) { + show_defaults_in_properties_ = true; + rewards_service_->AddObserver(this); + EnableRewards(); + + GURL url = https_server()->GetURL("laurenwags.github.io", "/index.html"); + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + content::WebContents* site_banner = + OpenSiteBanner(ContributionType::OneTimeTip); + const auto tip_options = GetSiteBannerTipOptions(site_banner); + ASSERT_EQ(tip_options, std::vector({ 5, 10, 20 })); + + rewards_service_->RemoveObserver(this); +} + IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, NotVerifedWallet) { EnableRewards(); @@ -2777,7 +2910,7 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, EnableRewards(); // Tip verified publisher - TipPublisher("duckduckgo.com", true); + TipPublisher("duckduckgo.com", ContributionType::OneTimeTip, true); // Stop observing the Rewards service rewards_service()->RemoveObserver(this); @@ -2795,7 +2928,7 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, TipConnectedPublisherAnon) { ClaimPromotion(use_panel); // Tip verified publisher - TipPublisher("bumpsmack.com", true); + TipPublisher("bumpsmack.com", ContributionType::OneTimeTip, true); // Stop observing the Rewards service rewards_service_->RemoveObserver(this); @@ -2826,7 +2959,7 @@ IN_PROC_BROWSER_TEST_F( ClaimPromotion(use_panel); // Tip verified publisher - TipPublisher("bumpsmack.com", true); + TipPublisher("bumpsmack.com", ContributionType::OneTimeTip, true); // Stop observing the Rewards service rewards_service_->RemoveObserver(this); @@ -2853,7 +2986,7 @@ IN_PROC_BROWSER_TEST_F( EnableRewards(); // Tip verified publisher - TipPublisher("bumpsmack.com", false); + TipPublisher("bumpsmack.com", ContributionType::OneTimeTip, false); // Stop observing the Rewards service rewards_service_->RemoveObserver(this); @@ -2880,7 +3013,7 @@ IN_PROC_BROWSER_TEST_F( EnableRewards(); // Tip verified publisher - TipPublisher("bumpsmack.com", false); + TipPublisher("bumpsmack.com", ContributionType::OneTimeTip, false); // Stop observing the Rewards service rewards_service_->RemoveObserver(this); diff --git a/components/brave_rewards/browser/rewards_service_impl.cc b/components/brave_rewards/browser/rewards_service_impl.cc index d3e813321ca4..590079e9fbc2 100644 --- a/components/brave_rewards/browser/rewards_service_impl.cc +++ b/components/brave_rewards/browser/rewards_service_impl.cc @@ -947,6 +947,9 @@ void RewardsServiceImpl::OnWalletProperties( wallet_properties.reset(new brave_rewards::WalletProperties); wallet_properties->parameters_choices = properties->parameters_choices; wallet_properties->monthly_amount = properties->fee_amount; + wallet_properties->default_tip_choices = properties->default_tip_choices; + wallet_properties->default_monthly_tip_choices = + properties->default_monthly_tip_choices; } // webui observer.OnWalletProperties(this, diff --git a/components/brave_rewards/browser/wallet_properties.cc b/components/brave_rewards/browser/wallet_properties.cc index 1ff94c8ed7bb..e5b37e845428 100644 --- a/components/brave_rewards/browser/wallet_properties.cc +++ b/components/brave_rewards/browser/wallet_properties.cc @@ -13,6 +13,8 @@ namespace brave_rewards { WalletProperties::WalletProperties(const WalletProperties &properties) { parameters_choices = properties.parameters_choices; + default_tip_choices = properties.default_tip_choices; + default_monthly_tip_choices = properties.default_monthly_tip_choices; } } // namespace brave_rewards diff --git a/components/brave_rewards/browser/wallet_properties.h b/components/brave_rewards/browser/wallet_properties.h index c4964a2e3aef..4fd4e52fd057 100644 --- a/components/brave_rewards/browser/wallet_properties.h +++ b/components/brave_rewards/browser/wallet_properties.h @@ -19,6 +19,8 @@ struct WalletProperties { double monthly_amount; std::vector parameters_choices; + std::vector default_tip_choices; + std::vector default_monthly_tip_choices; }; } // namespace brave_rewards 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 12594d39c5d5..ab1f17199ae1 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 @@ -24,6 +24,10 @@ export const onPublisherData = (windowId: number, publisher: RewardsExtension.Pu publisher }) +export const onWalletProperties = (properties: RewardsExtension.WalletProperties) => action(types.ON_WALLET_PROPERTIES, { + properties +}) + export const getCurrentReport = () => action(types.GET_CURRENT_REPORT, {}) export const onCurrentReport = (properties: RewardsExtension.Report) => action(types.ON_CURRENT_REPORT, { diff --git a/components/brave_rewards/resources/extension/brave_rewards/background/events/rewardsEvents.ts b/components/brave_rewards/resources/extension/brave_rewards/background/events/rewardsEvents.ts index ced395fa8f62..1809c44096ef 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/background/events/rewardsEvents.ts +++ b/components/brave_rewards/resources/extension/brave_rewards/background/events/rewardsEvents.ts @@ -110,6 +110,10 @@ chrome.braveRewards.onPromotionFinish.addListener((result: RewardsExtension.Resu rewardsPanelActions.promotionFinished(result, promotion) }) +chrome.braveRewards.onWalletProperties.addListener((properties: RewardsExtension.WalletProperties) => { + rewardsPanelActions.onWalletProperties(properties) +}) + // Fetch initial data required to refresh state, keeping in mind // that the extension process be restarted at any time. // TODO(petemill): Move to initializer function or single 'init' action. @@ -126,5 +130,6 @@ chrome.braveRewards.getRewardsMainEnabled((enabledMain: boolean) => { chrome.braveRewards.getAllNotifications((list: RewardsExtension.Notification[]) => { rewardsPanelActions.onAllNotifications(list) }) + chrome.braveRewards.getWalletProperties() } }) 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 c99f360371be..442c3be68487 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 @@ -451,6 +451,18 @@ export const rewardsPanelReducer = (state: RewardsExtension.State | undefined, a } else if (payload.result === RewardsExtension.Result.WALLET_CREATED) { state.walletCreated = true } + break + } + case types.ON_WALLET_PROPERTIES: { + state = { + ...state, + walletCreated: true, + walletCreateFailed: false, + walletCreating: false, + walletCorrupted: false, + walletProperties: payload.properties + } + break } } return state 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 6b0af3252ed2..8fa3cb03bc0c 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/background/storage.ts +++ b/components/brave_rewards/resources/extension/brave_rewards/background/storage.ts @@ -13,6 +13,9 @@ export const defaultState: RewardsExtension.State = { walletCreating: false, walletCreateFailed: false, publishers: {}, + walletProperties: { + defaultMonthlyTipChoices: [] + }, report: { ads: '0', closing: '0', 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 78b134e6f9df..37d611a3ff90 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/components/panel.tsx +++ b/components/brave_rewards/resources/extension/brave_rewards/components/panel.tsx @@ -503,16 +503,25 @@ export class Panel extends React.Component { } generateAmounts = (publisher?: RewardsExtension.Publisher) => { - const { tipAmounts } = this.props.rewardsPanelData + const { tipAmounts, walletProperties } = this.props.rewardsPanelData const { rates } = this.props.rewardsPanelData.balance const publisherKey = publisher && publisher.publisher_key - const initialAmounts = ( - !publisherKey || - !tipAmounts || - !tipAmounts[publisherKey] || - tipAmounts[publisherKey].length === 0 - ) ? this.defaultTipAmounts : tipAmounts[publisherKey] + let publisherAmounts = null + if (publisherKey && tipAmounts && tipAmounts[publisherKey] && tipAmounts[publisherKey].length) { + publisherAmounts = tipAmounts[publisherKey] + } + + // Prefer the publisher amounts, then the wallet's defaults. Fall back to defaultTipAmounts. + let initialAmounts = this.defaultTipAmounts + if (publisherAmounts) { + initialAmounts = publisherAmounts + } else if (walletProperties) { + const walletAmounts = walletProperties.defaultMonthlyTipChoices + if (walletAmounts.length) { + initialAmounts = walletAmounts + } + } const amounts = [0, ...initialAmounts] 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 130bdec9015a..c1211a87cad8 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 @@ -7,6 +7,7 @@ export const enum types { ON_WALLET_INITIALIZED = '@@rewards_panel/ON_WALLET_INITIALIZED', ON_TAB_RETRIEVED = '@@rewards_panel/ON_TAB_RETRIEVED', ON_PUBLISHER_DATA = '@@rewards_panel/ON_PUBLISHER_DATA', + ON_WALLET_PROPERTIES = '@@rewards_panel/ON_WALLET_PROPERTIES', GET_CURRENT_REPORT = '@@rewards_panel/GET_CURRENT_REPORT', ON_CURRENT_REPORT = '@@rewards_panel/ON_CURRENT_REPORT', ON_NOTIFICATION_ADDED = '@@rewards_panel/ON_NOTIFICATION_ADDED', diff --git a/components/brave_rewards/resources/tip/components/siteBanner.tsx b/components/brave_rewards/resources/tip/components/siteBanner.tsx index 7a84714468ab..9e18dc95fd35 100644 --- a/components/brave_rewards/resources/tip/components/siteBanner.tsx +++ b/components/brave_rewards/resources/tip/components/siteBanner.tsx @@ -30,6 +30,7 @@ interface State { } class Banner extends React.Component { + readonly defaultTipAmounts = [1, 5, 10] constructor (props: Props) { super(props) this.state = { @@ -54,12 +55,23 @@ class Banner extends React.Component { } generateAmounts = () => { - const { balance } = this.props.rewardsDonateData - - let amounts = [1, 5, 10] - const amount = this.props.publisher.amounts - if (amount && amount.length) { - amounts = amount + const { monthly } = this.props + const { balance, walletInfo } = this.props.rewardsDonateData + + const publisherAmounts = this.props.publisher.amounts + + // Prefer the publisher amounts, then the wallet's defaults. Fall back to defaultTipAmounts. + let amounts = this.defaultTipAmounts + if (publisherAmounts && publisherAmounts.length) { + amounts = publisherAmounts + } else if (walletInfo) { + const walletAmounts = monthly + ? walletInfo.defaultMonthlyTipChoices + : walletInfo.defaultTipChoices + + if (walletAmounts.length) { + amounts = walletAmounts + } } return amounts.map((value: number) => { diff --git a/components/brave_rewards/resources/tip/reducers/tip_reducer.ts b/components/brave_rewards/resources/tip/reducers/tip_reducer.ts index 21ad3df39c50..263a87d38b9b 100644 --- a/components/brave_rewards/resources/tip/reducers/tip_reducer.ts +++ b/components/brave_rewards/resources/tip/reducers/tip_reducer.ts @@ -15,7 +15,9 @@ export const defaultState: RewardsTip.State = { currentTipRecurring: false, recurringDonations: [], walletInfo: { - choices: [] + choices: [], + defaultTipChoices: [], + defaultMonthlyTipChoices: [] }, reconcileStamp: 0, balance: { diff --git a/components/brave_rewards/resources/ui/components/amount/index.tsx b/components/brave_rewards/resources/ui/components/amount/index.tsx index 2dfb4a2f003a..badfee447b34 100644 --- a/components/brave_rewards/resources/ui/components/amount/index.tsx +++ b/components/brave_rewards/resources/ui/components/amount/index.tsx @@ -19,8 +19,6 @@ export interface Props { } export default class Amount extends React.PureComponent { - private selectedAmount: HTMLDivElement | null - static defaultProps = { type: 'small', currency: 'USD', @@ -29,21 +27,6 @@ export default class Amount extends React.PureComponent { constructor (props: Props) { super(props) - this.selectedAmount = null - } - - componentDidMount () { - if (this.selectedAmount) { - this.selectedAmount.click() - } - } - - selectedNodeRef = (node: HTMLDivElement) => { - const { selected } = this.props - - if (selected) { - this.selectedAmount = node - } } getAboutText = (isMobile?: boolean) => { @@ -68,7 +51,6 @@ export default class Amount extends React.PureComponent { id={id} onClick={onSelect.bind(this, amount)} data-test-id={'amount-wrapper'} - innerRef={this.selectedNodeRef} > {amount} {this.getBatString()} diff --git a/components/brave_rewards/resources/ui/components/donate/index.tsx b/components/brave_rewards/resources/ui/components/donate/index.tsx index 844927d8e0fb..2c5689043ce9 100644 --- a/components/brave_rewards/resources/ui/components/donate/index.tsx +++ b/components/brave_rewards/resources/ui/components/donate/index.tsx @@ -52,6 +52,7 @@ export interface Props { interface State { missingFunds: boolean amountChanged: boolean + displayedAmounts: Donation[] } export default class Donate extends React.PureComponent { @@ -61,7 +62,8 @@ export default class Donate extends React.PureComponent { super(props) this.state = { missingFunds: false, - amountChanged: false + amountChanged: false, + displayedAmounts: props.donationAmounts } this.sendButton = null } @@ -70,18 +72,49 @@ export default class Donate extends React.PureComponent { if (this.sendButton) { this.sendButton.focus() } + + this.donationAmountsDidChange() + } + + donationAmountsAreEqual = (a: Donation[], b: Donation[]) => { + if (a.length !== b.length) { + return false + } + + return a.every(({}, index) => a[index].tokens === b[index].tokens && a[index].converted === b[index].converted) } componentDidUpdate (prevProps: Props) { - if ( - this.props.balance !== prevProps.balance || - this.props.donationAmounts !== prevProps.donationAmounts || - this.props.currentAmount !== prevProps.currentAmount - ) { + if (!this.donationAmountsAreEqual(this.props.donationAmounts, prevProps.donationAmounts)) { + this.donationAmountsDidChange() + return + } + + if (this.props.balance !== prevProps.balance || + this.props.currentAmount !== prevProps.currentAmount) { this.validateAmount(this.props.balance) } } + donationAmountsDidChange = () => { + // If the user has already made a selection, don't change the UI. + if (this.state.amountChanged) { + return + } + + const amounts = this.props.donationAmounts + this.setState({ displayedAmounts: amounts }) + + // If there are 3 options, select the middle one. + if (amounts.length === 3) { + const amountToSelect = amounts[1].tokens + this.validateAmount(this.props.balance, amountToSelect) + if (this.props.onAmountSelection) { + this.props.onAmountSelection(amountToSelect) + } + } + } + sendNodeRef = (node: HTMLButtonElement) => { this.sendButton = node } @@ -150,7 +183,6 @@ export default class Donate extends React.PureComponent { render () { const { id, - donationAmounts, actionText, children, title, @@ -161,6 +193,8 @@ export default class Donate extends React.PureComponent { onlyAnonWallet } = this.props + const { displayedAmounts } = this.state + const isMonthly = type === 'monthly' const disabled = parseInt(currentAmount, 10) === 0 const SendButton = isMonthly ? StyledMonthlySendButton : StyledSendButton @@ -171,7 +205,7 @@ export default class Donate extends React.PureComponent { {title} { - donationAmounts && donationAmounts.map((donation: Donation, index: number) => { + displayedAmounts && displayedAmounts.map((donation: Donation, index: number) => { const isCurrentAmount = donation.tokens === currentAmount.toString() const isDefaultAmount = index === 1 && !this.state.amountChanged diff --git a/components/definitions/chromel.d.ts b/components/definitions/chromel.d.ts index f7c73739e5f8..55dac044620c 100644 --- a/components/definitions/chromel.d.ts +++ b/components/definitions/chromel.d.ts @@ -60,6 +60,7 @@ declare namespace chrome.settingsPrivate { declare namespace chrome.braveRewards { const createWallet: () => {} + const getWalletProperties: () => {} const tipSite: (tabId: number, publisherKey: string, monthly: boolean) => {} const tipTwitterUser: (tabId: number, mediaMetaData: RewardsTip.MediaMetaData) => {} const tipRedditUser: (tabId: number, mediaMetaData: RewardsTip.MediaMetaData) => {} @@ -81,6 +82,9 @@ declare namespace chrome.braveRewards { const onPromotionFinish: { addListener: (callback: (result: RewardsExtension.Result, promotion: RewardsExtension.Promotion) => void) => void } + const onWalletProperties: { + addListener: (callback: (properties: RewardsExtension.WalletProperties) => void) => void + } const includeInAutoContribution: (publisherKey: string, exclude: boolean) => {} const fetchPromotions: () => {} const claimPromotion: (promotionId: string, callback: (properties: RewardsExtension.Captcha) => void) => {} diff --git a/components/definitions/rewardsExtensions.d.ts b/components/definitions/rewardsExtensions.d.ts index c218a19633fc..4767268bd243 100644 --- a/components/definitions/rewardsExtensions.d.ts +++ b/components/definitions/rewardsExtensions.d.ts @@ -13,6 +13,7 @@ declare namespace RewardsExtension { walletCreated: boolean walletCreating: boolean walletCreateFailed: boolean + walletProperties: WalletProperties recurringTips: Record[] tipAmounts: Record externalWallet?: ExternalWallet @@ -111,6 +112,10 @@ declare namespace RewardsExtension { hint: string } + export interface WalletProperties { + defaultMonthlyTipChoices: number[] + } + export interface Report { ads: string closing: string diff --git a/components/definitions/rewardsTip.d.ts b/components/definitions/rewardsTip.d.ts index f4fe9ca4d026..53062908c781 100644 --- a/components/definitions/rewardsTip.d.ts +++ b/components/definitions/rewardsTip.d.ts @@ -71,6 +71,8 @@ declare namespace RewardsTip { export interface WalletProperties { choices: number[] + defaultTipChoices: number[] + defaultMonthlyTipChoices: number[] } export interface Grant { diff --git a/test/data/rewards-data/wallet_properties_resp_defaults.json b/test/data/rewards-data/wallet_properties_resp_defaults.json new file mode 100644 index 000000000000..a871823c2135 --- /dev/null +++ b/test/data/rewards-data/wallet_properties_resp_defaults.json @@ -0,0 +1,44 @@ +{ + "altcurrency": "BAT", + "probi": "0", + "balance": "0.0000", + "unconfirmed": "0.0000", + "rates": { + "BTC": 0.00003105, + "ETH": 0.0007520713830465265, + "XRP": 0.6385015608740894, + "BCH": 0.000398527449465635, + "LTC": 0.003563298490127758, + "DASH": 0.0011736801836266257, + "BTG": 0.009819171067370777, + "USD": 0.214100307359946, + "EUR": 0.18357217273398782 + }, + "parameters": { + "adFree": { + "currency": "BAT", + "fee": { + "BAT": 20 + }, + "choices": { + "BAT": [ + 10, + 15, + 20, + 30, + 50, + 100 + ] + }, + "range": { + "BAT": [ + 10, + 100 + ] + }, + "days": 30 + }, + "defaultTipChoices": ["5", "10", "20"], + "defaultMonthlyChoices": ["10", "20", "50"] + } +} \ No newline at end of file diff --git a/vendor/bat-native-ledger/include/bat/ledger/public/interfaces/ledger.mojom b/vendor/bat-native-ledger/include/bat/ledger/public/interfaces/ledger.mojom index 7c6bd902317c..afd47232f428 100644 --- a/vendor/bat-native-ledger/include/bat/ledger/public/interfaces/ledger.mojom +++ b/vendor/bat-native-ledger/include/bat/ledger/public/interfaces/ledger.mojom @@ -87,6 +87,8 @@ struct VisitData { struct WalletProperties { double fee_amount; array parameters_choices; + array default_tip_choices; + array default_monthly_tip_choices; }; struct Balance { diff --git a/vendor/bat-native-ledger/src/bat/ledger/internal/state/wallet_state.cc b/vendor/bat-native-ledger/src/bat/ledger/internal/state/wallet_state.cc index c8132919918b..bf9fb71defdf 100644 --- a/vendor/bat-native-ledger/src/bat/ledger/internal/state/wallet_state.cc +++ b/vendor/bat-native-ledger/src/bat/ledger/internal/state/wallet_state.cc @@ -20,6 +20,10 @@ const char kBatKey[] = "BAT"; const char kChoicesKey[] = "choices"; const char kChoicesBatPath[] = "parameters.adFree.choices.BAT"; const char kFeeBatPath[] = "parameters.adFree.fee.BAT"; +const char kDefaultTipChoiceKey[] = "defaultTipChoices"; +const char kDefaultTipChoicePath[] = "parameters.defaultTipChoices"; +const char kDefaultMonthlyChoiceKey[] = "defaultMonthlyChoices"; +const char kDefaultMonthlyChoicePath[] = "parameters.defaultMonthlyChoices"; } // namespace @@ -89,6 +93,27 @@ bool WalletState::FromDict( wallet_properties.parameters_choices.push_back(bat); } + // Default tip choices + const auto* tip_choices_list = + dictionary->FindListPath(kDefaultTipChoicePath); + if (tip_choices_list) { + for (const auto& item : tip_choices_list->GetList()) { + const std::string amount = item.GetString(); + wallet_properties.default_tip_choices.push_back(std::stod(amount)); + } + } + + // Default monthly tip choices + const auto* monthly_choices_list = + dictionary->FindListPath(kDefaultMonthlyChoicePath); + if (monthly_choices_list) { + for (const auto& item : monthly_choices_list->GetList()) { + const std::string amount = item.GetString(); + wallet_properties.default_monthly_tip_choices.push_back( + std::stod(amount)); + } + } + *properties = wallet_properties; return true; @@ -129,8 +154,24 @@ bool WalletState::ToJson( } writer->EndArray(); writer->EndObject(); - writer->EndObject(); + + writer->String(kDefaultTipChoiceKey); + + writer->StartArray(); + for (const auto& item : properties.default_tip_choices) { + writer->String(std::to_string(item).c_str()); + } + writer->EndArray(); + + writer->String(kDefaultMonthlyChoiceKey); + + writer->StartArray(); + for (const auto& item : properties.default_monthly_tip_choices) { + writer->String(std::to_string(item).c_str()); + } + writer->EndArray(); + writer->EndObject(); writer->EndObject(); diff --git a/vendor/bat-native-ledger/src/bat/ledger/internal/state/wallet_state_unittest.cc b/vendor/bat-native-ledger/src/bat/ledger/internal/state/wallet_state_unittest.cc index 3481c90cf7ad..f6259f592113 100644 --- a/vendor/bat-native-ledger/src/bat/ledger/internal/state/wallet_state_unittest.cc +++ b/vendor/bat-native-ledger/src/bat/ledger/internal/state/wallet_state_unittest.cc @@ -25,6 +25,8 @@ TEST(WalletStateTest, ToJsonSerialization) { 50.0, 100.0 }; + wallet_properties.default_tip_choices = { 5.0, 10.0, 15.0 }; + wallet_properties.default_monthly_tip_choices = { 15.0, 20.0, 30.0 }; // Act const WalletState wallet_state; @@ -49,8 +51,10 @@ TEST(WalletStateTest, FromJsonDeserialization) { 50.0, 100.0 }; + wallet_properties.default_tip_choices = { 5.0, 10.0, 15.0 }; + wallet_properties.default_monthly_tip_choices = { 15.0, 20.0, 30.0 }; - const std::string json = "{\"fee_amount\":1.7976931348623157e308,\"parameters\":{\"adFree\":{\"fee\":{\"BAT\":1.7976931348623157e308},\"choices\":{\"BAT\":[5.0,10.0,15.0,20.0,25.0,50.0,100.0]}}}}"; // NOLINT + const std::string json = "{\"fee_amount\":1.7976931348623157e308,\"parameters\":{\"adFree\":{\"fee\":{\"BAT\":1.7976931348623157e308},\"choices\":{\"BAT\":[5.0,10.0,15.0,20.0,25.0,50.0,100.0]}},\"defaultTipChoices\":[\"5\",\"10\",\"15\"],\"defaultMonthlyChoices\":[\"15\",\"20\",\"30\"]}}"; // NOLINT // Act WalletProperties expected_wallet_properties; diff --git a/vendor/bat-native-ledger/src/bat/ledger/internal/wallet/wallet.cc b/vendor/bat-native-ledger/src/bat/ledger/internal/wallet/wallet.cc index 9fbf8c2b9325..fc4759dc3544 100644 --- a/vendor/bat-native-ledger/src/bat/ledger/internal/wallet/wallet.cc +++ b/vendor/bat-native-ledger/src/bat/ledger/internal/wallet/wallet.cc @@ -103,6 +103,9 @@ ledger::WalletPropertiesPtr Wallet::WalletPropertiesToWalletInfo( ledger::WalletPropertiesPtr wallet = ledger::WalletProperties::New(); wallet->parameters_choices = properties.parameters_choices; wallet->fee_amount = ledger_->GetContributionAmount(); + wallet->default_tip_choices = properties.default_tip_choices; + wallet->default_monthly_tip_choices = properties.default_monthly_tip_choices; + return wallet; }