diff --git a/app/vector_icons/BUILD.gn b/app/vector_icons/BUILD.gn index ca6f6ce8c01f..f951cb85b364 100644 --- a/app/vector_icons/BUILD.gn +++ b/app/vector_icons/BUILD.gn @@ -10,6 +10,7 @@ aggregate_vector_icons("brave_vector_icons") { icon_directory = "." sources = [ + "bat.icon", "brave_ads_close_button.icon", "brave_ads_dark_mode_info_button.icon", "brave_ads_light_mode_info_button.icon", diff --git a/app/vector_icons/bat.icon b/app/vector_icons/bat.icon new file mode 100644 index 000000000000..d82eed51da7b --- /dev/null +++ b/app/vector_icons/bat.icon @@ -0,0 +1,39 @@ +CANVAS_DIMENSIONS, 16, +PATH_COLOR_ARGB, 0xFF, 0xFF, 0x47, 0x24, +MOVE_TO, 0.03f, 14.56f, +R_LINE_TO, 4.68f, -2.75f, +LINE_TO, 7.94f, 6.15f, +V_LINE_TO, 0.69f, +R_CUBIC_TO, -0.09f, 0, -0.17f, 0.06f, -0.23f, 0.17f, +LINE_TO, 3.88f, 7.57f, +LINE_TO, 0.06f, 14.27f, +R_CUBIC_TO, -0.06f, 0.11f, -0.07f, 0.22f, -0.03f, 0.29f, +CLOSE, +NEW_PATH, +PATH_COLOR_ARGB, 0xFF, 0x9E, 0x1F, 0x63, +MOVE_TO, 7.94f, 0.69f, +V_LINE_TO, 6.15f, +R_LINE_TO, 3.23f, 5.66f, +R_LINE_TO, 4.68f, 2.75f, +R_CUBIC_TO, 0.04f, -0.07f, 0.04f, -0.18f, -0.03f, -0.29f, +R_LINE_TO, -3.83f, -6.7f, +LINE_TO, 8.18f, 0.86f, +R_CUBIC_TO, -0.06f, -0.11f, -0.15f, -0.17f, -0.23f, -0.17f, +CLOSE, +NEW_PATH, +PATH_COLOR_ARGB, 0xFF, 0x66, 0x2D, 0x91, +MOVE_TO, 15.85f, 14.56f, +R_LINE_TO, -4.68f, -2.75f, +H_LINE_TO, 4.71f, +LINE_TO, 0.03f, 14.56f, +R_CUBIC_TO, 0.04f, 0.08f, 0.13f, 0.12f, 0.26f, 0.12f, +H_LINE_TO, 15.59f, +R_CUBIC_TO, 0.13f, 0, 0.22f, -0.05f, 0.26f, -0.12f, +CLOSE, +NEW_PATH, +PATH_COLOR_ARGB, 0xFF, 0xFF, 0xFF, 0xFF, +MOVE_TO, 4.71f, 11.81f, +R_H_LINE_TO, 6.46f, +LINE_TO, 7.94f, 6.15f, +LINE_TO, 4.71f, 11.81f, +CLOSE diff --git a/browser/about_flags.cc b/browser/about_flags.cc index 968adddee7a0..792156ef5c67 100644 --- a/browser/about_flags.cc +++ b/browser/about_flags.cc @@ -220,6 +220,11 @@ constexpr char kBraveRewardsGeminiDescription[] = "Enables support for Gemini as an external wallet provider for Brave"; #endif +constexpr char kBraveRewardsWebUIPanelName[] = "Use WebUI Rewards Panel"; +constexpr char kBraveRewardsWebUIPanelDescription[] = + "When enabled, the Brave Rewards panel will be displayed using WebUI " + "instead of the built-in Rewards extension."; + constexpr char kBraveRewardsVerboseLoggingName[] = "Enable Brave Rewards verbose logging"; constexpr char kBraveRewardsVerboseLoggingDescription[] = @@ -548,6 +553,11 @@ const flags_ui::FeatureEntry::Choice kBraveSkusEnvChoices[] = { flag_descriptions::kBraveRewardsVerboseLoggingDescription, \ kOsDesktop | kOsAndroid, \ FEATURE_VALUE_TYPE(brave_rewards::features::kVerboseLoggingFeature)}, \ + {"brave-rewards-webui-panel", \ + flag_descriptions::kBraveRewardsWebUIPanelName, \ + flag_descriptions::kBraveRewardsWebUIPanelDescription, \ + kOsDesktop, \ + FEATURE_VALUE_TYPE(brave_rewards::features::kWebUIPanelFeature)}, \ {"brave-ads-custom-push-notifications-ads", \ flag_descriptions::kBraveAdsCustomNotificationsName, \ flag_descriptions::kBraveAdsCustomNotificationsDescription, \ diff --git a/browser/brave_adaptive_captcha/brave_adaptive_captcha_service_factory.cc b/browser/brave_adaptive_captcha/brave_adaptive_captcha_service_factory.cc index d9aff6cfe3b2..04bd863ad6d8 100644 --- a/browser/brave_adaptive_captcha/brave_adaptive_captcha_service_factory.cc +++ b/browser/brave_adaptive_captcha/brave_adaptive_captcha_service_factory.cc @@ -10,7 +10,8 @@ #include #include "base/memory/raw_ptr.h" -#include "brave/browser/brave_rewards/rewards_panel_helper.h" +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "brave/browser/brave_rewards/rewards_panel_service_factory.h" #include "brave/browser/brave_rewards/rewards_service_factory.h" #include "brave/browser/profiles/profile_util.h" #include "brave/components/brave_adaptive_captcha/brave_adaptive_captcha_service.h" @@ -30,7 +31,10 @@ class CaptchaDelegate bool ShowScheduledCaptcha(const std::string& payment_id, const std::string& captcha_id) override { - return brave_rewards::ShowRewardsPanel(context_, true); + auto* service = brave_rewards::RewardsPanelServiceFactory::GetForProfile( + Profile::FromBrowserContext(context_)); + + return service && service->ShowAdaptiveCaptcha(); } private: diff --git a/browser/brave_ads/brave_ads_host.cc b/browser/brave_ads/brave_ads_host.cc index 58fe68c5d003..655b17fc0c34 100644 --- a/browser/brave_ads/brave_ads_host.cc +++ b/browser/brave_ads/brave_ads_host.cc @@ -5,35 +5,23 @@ #include "brave/browser/brave_ads/brave_ads_host.h" -#include #include #include #include "brave/browser/brave_ads/ads_service_factory.h" #include "brave/browser/brave_ads/search_result_ad/search_result_ad_service_factory.h" +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "brave/browser/brave_rewards/rewards_panel_service_factory.h" #include "brave/browser/brave_rewards/rewards_service_factory.h" -#include "brave/browser/extensions/api/brave_action_api.h" -#include "brave/browser/extensions/brave_component_loader.h" #include "brave/components/brave_ads/browser/ads_service.h" #include "brave/components/brave_ads/content/browser/search_result_ad/search_result_ad_service.h" #include "brave/components/brave_rewards/browser/rewards_service.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "components/sessions/content/session_tab_helper.h" #include "content/public/browser/web_contents.h" -#include "extensions/browser/extension_system.h" -#include "extensions/common/constants.h" namespace brave_ads { -namespace { - -constexpr char kAdsEnableRelativeUrl[] = "request_ads_enabled_panel.html"; - -} // namespace - BraveAdsHost::BraveAdsHost(Profile* profile, content::WebContents* web_contents) : profile_(profile), tab_id_(sessions::SessionTabHelper::IdForTab(web_contents)) { @@ -71,16 +59,17 @@ void BraveAdsHost::RequestAdsEnabled(RequestAdsEnabledCallback callback) { const AdsService* ads_service = AdsServiceFactory::GetForProfile(profile_); brave_rewards::RewardsService* rewards_service = brave_rewards::RewardsServiceFactory::GetForProfile(profile_); - if (!rewards_service || !ads_service || !ads_service->IsSupportedLocale()) { + auto* panel_service = + brave_rewards::RewardsPanelServiceFactory::GetForProfile(profile_); + if (!rewards_service || !panel_service || !ads_service || + !ads_service->IsSupportedLocale()) { std::move(callback).Run(false); return; } - if (ads_service->IsEnabled()) { std::move(callback).Run(true); return; } - if (!callbacks_.empty()) { callbacks_.push_back(std::move(callback)); return; @@ -90,7 +79,7 @@ void BraveAdsHost::RequestAdsEnabled(RequestAdsEnabledCallback callback) { rewards_service_observation_.Observe(rewards_service); - if (!ShowRewardsPopup(rewards_service)) { + if (!panel_service->ShowBraveTalkOptIn()) { RunCallbacksAndReset(false); } } @@ -109,36 +98,6 @@ void BraveAdsHost::OnAdsEnabled(brave_rewards::RewardsService* rewards_service, RunCallbacksAndReset(ads_enabled); } -bool BraveAdsHost::ShowRewardsPopup( - brave_rewards::RewardsService* rewards_service) { - DCHECK(rewards_service); - - Browser* browser = chrome::FindBrowserWithProfile(profile_); - DCHECK(browser); - - auto* extension_service = - extensions::ExtensionSystem::Get(profile_)->extension_service(); - if (!extension_service) { - return false; - } - - extensions::BraveComponentLoader* component_loader = - static_cast( - extension_service->component_loader()); - DCHECK(component_loader); - - if (!component_loader->Exists(brave_rewards_extension_id)) { - component_loader->AddRewardsExtension(); - - rewards_service->StartProcess(base::DoNothing()); - } - - std::string error; - return extensions::BraveActionAPI::ShowActionUI( - browser, brave_rewards_extension_id, - std::make_unique(kAdsEnableRelativeUrl), &error); -} - void BraveAdsHost::RunCallbacksAndReset(bool result) { DCHECK(!callbacks_.empty()); diff --git a/browser/brave_ads/request_ads_enabled_api_browsertest.cc b/browser/brave_ads/request_ads_enabled_api_browsertest.cc index be96e08319af..0670e483b647 100644 --- a/browser/brave_ads/request_ads_enabled_api_browsertest.cc +++ b/browser/brave_ads/request_ads_enabled_api_browsertest.cc @@ -8,6 +8,7 @@ #include "base/test/scoped_feature_list.h" #include "bat/ads/pref_names.h" #include "brave/browser/brave_ads/ads_service_factory.h" +#include "brave/browser/brave_rewards/rewards_panel_service.h" #include "brave/browser/brave_rewards/rewards_service_factory.h" #include "brave/components/brave_ads/browser/ads_service.h" #include "brave/components/brave_ads/common/features.h" @@ -27,7 +28,6 @@ #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_mock_cert_verifier.h" -#include "extensions/common/constants.h" #include "net/dns/mock_host_resolver.h" // npm run test -- brave_browser_tests --filter=RequestAdsEnabledApiTest* @@ -121,11 +121,8 @@ class RequestAdsEnabledApiTestBase : public InProcessBrowserTest { // Check that this notification is for the Rewards panel and not, say, // the extension background page. - std::string url = (*popup_contents)->GetLastCommittedURL().spec(); - std::string rewards_panel_url = std::string("chrome-extension://") + - brave_rewards_extension_id + - "/request_ads_enabled_panel.html"; - return url == rewards_panel_url; + return brave_rewards::RewardsPanelService::IsRewardsPanelURLForTesting( + (*popup_contents)->GetLastCommittedURL()); }; content::WindowedNotificationObserver popup_observer( diff --git a/browser/brave_content_browser_client.cc b/browser/brave_content_browser_client.cc index 287022270852..84e35b2183e8 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -192,11 +192,13 @@ using extensions::ChromeContentBrowserClientExtensionsPart; #include "brave/browser/new_tab/new_tab_shows_navigation_throttle.h" #include "brave/browser/ui/webui/brave_federated/federated_internals.mojom.h" #include "brave/browser/ui/webui/brave_federated/federated_internals_ui.h" +#include "brave/browser/ui/webui/brave_rewards/rewards_panel_ui.h" #include "brave/browser/ui/webui/brave_shields/shields_panel_ui.h" #include "brave/browser/ui/webui/brave_wallet/wallet_page_ui.h" #include "brave/browser/ui/webui/brave_wallet/wallet_panel_ui.h" #include "brave/browser/ui/webui/new_tab_page/brave_new_tab_ui.h" #include "brave/components/brave_new_tab_ui/brave_new_tab_page.mojom.h" +#include "brave/components/brave_rewards/common/brave_rewards_panel.mojom.h" #include "brave/components/brave_shields/common/brave_shields_panel.mojom.h" #include "brave/components/brave_today/common/brave_news.mojom.h" #include "brave/components/brave_today/common/features.h" @@ -540,6 +542,8 @@ void BraveContentBrowserClient::RegisterBrowserInterfaceBindersForFrame( chrome::internal::RegisterWebUIControllerInterfaceBinder< brave_shields::mojom::PanelHandlerFactory, ShieldsPanelUI>(map); } + chrome::internal::RegisterWebUIControllerInterfaceBinder< + brave_rewards::mojom::PanelHandlerFactory, RewardsPanelUI>(map); if (base::FeatureList::IsEnabled( brave_federated::features::kFederatedLearning)) { chrome::internal::RegisterWebUIControllerInterfaceBinder< diff --git a/browser/brave_rewards/rewards_extension_panel_handler.cc b/browser/brave_rewards/rewards_extension_panel_handler.cc new file mode 100644 index 000000000000..c18abbdf781d --- /dev/null +++ b/browser/brave_rewards/rewards_extension_panel_handler.cc @@ -0,0 +1,85 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * 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/. */ + +#include "brave/browser/brave_rewards/rewards_extension_panel_handler.h" + +#include +#include + +#include "base/strings/strcat.h" +#include "brave/browser/brave_rewards/rewards_service_factory.h" +#include "brave/browser/extensions/brave_component_loader.h" +#include "brave/browser/ui/views/brave_actions/brave_actions_container.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_window.h" +#include "extensions/browser/extension_system.h" + +namespace brave_rewards { + +namespace { + +constexpr char kRewardsPanelUrl[] = "/brave_rewards_panel.html"; +constexpr char kAdsEnableRelativeUrl[] = "/request_ads_enabled_panel.html"; + +std::string GetExtensionPath(const mojom::RewardsPanelArgs& args) { + switch (args.view) { + case mojom::RewardsPanelView::kDefault: + return kRewardsPanelUrl; + case mojom::RewardsPanelView::kRewardsTour: + return base::StrCat({kRewardsPanelUrl, "#tour"}); + case mojom::RewardsPanelView::kGrantCaptcha: + return base::StrCat({kRewardsPanelUrl, "#grant_", args.data}); + case mojom::RewardsPanelView::kAdaptiveCaptcha: + return base::StrCat({kRewardsPanelUrl, "#load_adaptive_captcha"}); + case mojom::RewardsPanelView::kBraveTalkOptIn: + return kAdsEnableRelativeUrl; + } +} + +} // namespace + +RewardsExtensionPanelHandler::~RewardsExtensionPanelHandler() = default; + +bool RewardsExtensionPanelHandler::IsRewardsExtensionPanelURL(const GURL& url) { + return url.SchemeIs("chrome-extension") && + url.host() == brave_rewards_extension_id && + (url.path() == kRewardsPanelUrl || + url.path() == kAdsEnableRelativeUrl); +} + +void RewardsExtensionPanelHandler::OnRewardsPanelRequested( + Browser* browser, + const mojom::RewardsPanelArgs& args) { + DCHECK(browser); + auto* profile = browser->profile(); + + // Start the rewards ledger process if it is not already started + auto* rewards_service = RewardsServiceFactory::GetForProfile(profile); + if (!rewards_service) { + return; + } + + rewards_service->StartProcess(base::DoNothing()); + + // Load the rewards extension if it is not already loaded. + auto* extension_service = + extensions::ExtensionSystem::Get(profile)->extension_service(); + if (!extension_service) { + return; + } + + static_cast( + extension_service->component_loader()) + ->AddRewardsExtension(); + + std::string error; + extensions::BraveActionAPI::ShowActionUI( + browser, brave_rewards_extension_id, + std::make_unique(GetExtensionPath(args)), &error); +} + +} // namespace brave_rewards diff --git a/browser/brave_rewards/rewards_extension_panel_handler.h b/browser/brave_rewards/rewards_extension_panel_handler.h new file mode 100644 index 000000000000..2491b79e4702 --- /dev/null +++ b/browser/brave_rewards/rewards_extension_panel_handler.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * 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/. */ + +#ifndef BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_EXTENSION_PANEL_HANDLER_H_ +#define BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_EXTENSION_PANEL_HANDLER_H_ + +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "url/gurl.h" + +namespace brave_rewards { + +// A `RewardsPanelService` observer that loads the Rewards extension if required +// and dispatches panel requests to the extension. +class RewardsExtensionPanelHandler : public RewardsPanelService::Observer { + public: + ~RewardsExtensionPanelHandler() override; + + static bool IsRewardsExtensionPanelURL(const GURL& url); + + void OnRewardsPanelRequested(Browser* browser, + const mojom::RewardsPanelArgs& args) override; +}; + +} // namespace brave_rewards + +#endif // BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_EXTENSION_PANEL_HANDLER_H_ diff --git a/browser/brave_rewards/rewards_panel_helper.cc b/browser/brave_rewards/rewards_panel_helper.cc deleted file mode 100644 index 830eac492760..000000000000 --- a/browser/brave_rewards/rewards_panel_helper.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright (c) 2021 The Brave Authors. All rights reserved. - * 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/. */ - -#include "brave/browser/brave_rewards/rewards_panel_helper.h" - -#include -#include -#include - -#include "brave/browser/brave_rewards/rewards_service_factory.h" -#include "brave/browser/extensions/brave_component_loader.h" -#include "brave/browser/ui/views/brave_actions/brave_actions_container.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_finder.h" -#include "chrome/browser/ui/browser_window.h" -#include "extensions/browser/extension_system.h" - -namespace { -constexpr char kRewardsPanelUrl[] = "brave_rewards_panel.html"; - -bool ShowPanel(content::BrowserContext* context, - const std::string& relative_path) { - Profile* profile = Profile::FromBrowserContext(context); - Browser* browser = chrome::FindTabbedBrowser(profile, false); - if (!browser) { - return false; - } - - // Start the rewards ledger process if it is not already started - auto* rewards_service = - brave_rewards::RewardsServiceFactory::GetForProfile(profile); - if (!rewards_service) { - return false; - } - - rewards_service->StartProcess(base::DoNothing()); - - // Load the rewards extension if it is not already loaded - auto* extension_service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - if (!extension_service) { - return false; - } - - static_cast( - extension_service->component_loader()) - ->AddRewardsExtension(); - - if (browser->window()->IsMinimized()) { - browser->window()->Restore(); - } - - std::string error; - bool popup_shown = extensions::BraveActionAPI::ShowActionUI( - browser, brave_rewards_extension_id, - std::make_unique(relative_path), &error); - if (!popup_shown) { - return false; - } - - return true; -} - -} // namespace - -namespace brave_rewards { - -bool ShowRewardsPanel(content::BrowserContext* context, - bool load_adaptive_captcha) { - std::string url = kRewardsPanelUrl; - if (load_adaptive_captcha) { - url += "#load_adaptive_captcha"; - } - - return ShowPanel(context, url); -} - -} // namespace brave_rewards diff --git a/browser/brave_rewards/rewards_panel_helper.h b/browser/brave_rewards/rewards_panel_helper.h deleted file mode 100644 index 944b4abd1af0..000000000000 --- a/browser/brave_rewards/rewards_panel_helper.h +++ /dev/null @@ -1,20 +0,0 @@ -/* Copyright (c) 2021 The Brave Authors. All rights reserved. - * 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/. */ - -#ifndef BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_HELPER_H_ -#define BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_HELPER_H_ - -namespace content { -class BrowserContext; -} // namespace content - -namespace brave_rewards { - -bool ShowRewardsPanel(content::BrowserContext* profile, - bool load_adaptive_captcha = false); - -} // namespace brave_rewards - -#endif // BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_HELPER_H_ diff --git a/browser/brave_rewards/rewards_panel_service.cc b/browser/brave_rewards/rewards_panel_service.cc new file mode 100644 index 000000000000..a7cfe21b93ee --- /dev/null +++ b/browser/brave_rewards/rewards_panel_service.cc @@ -0,0 +1,100 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * 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/. */ + +#include "brave/browser/brave_rewards/rewards_panel_service.h" + +#include +#include + +#include "base/feature_list.h" +#include "brave/browser/brave_rewards/rewards_extension_panel_handler.h" +#include "brave/components/brave_rewards/common/features.h" +#include "brave/components/constants/webui_url_constants.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_window.h" + +namespace brave_rewards { + +RewardsPanelService::RewardsPanelService(Profile* profile) : profile_(profile) { + // If we are using the Rewards extension to display the Rewards panel, then + // create an instance of `RewardsExtensionPanelHandler` to load the extension + // and dispatch panel requests to the extension. + if (!base::FeatureList::IsEnabled(features::kWebUIPanelFeature)) { + extension_handler_ = std::make_unique(); + AddObserver(extension_handler_.get()); + } +} + +RewardsPanelService::~RewardsPanelService() = default; + +bool RewardsPanelService::IsRewardsPanelURLForTesting(const GURL& url) { + if (base::FeatureList::IsEnabled(features::kWebUIPanelFeature)) { + return url.host() == kBraveRewardsPanelHost; + } else { + return RewardsExtensionPanelHandler::IsRewardsExtensionPanelURL(url); + } +} + +bool RewardsPanelService::OpenRewardsPanel() { + return OpenPanelWithArgs( + mojom::RewardsPanelArgs(mojom::RewardsPanelView::kDefault, "")); +} + +bool RewardsPanelService::ShowRewardsTour() { + return OpenPanelWithArgs( + mojom::RewardsPanelArgs(mojom::RewardsPanelView::kRewardsTour, "")); +} + +bool RewardsPanelService::ShowGrantCaptcha(const std::string& grant_id) { + return OpenPanelWithArgs(mojom::RewardsPanelArgs( + mojom::RewardsPanelView::kGrantCaptcha, grant_id)); +} + +bool RewardsPanelService::ShowAdaptiveCaptcha() { + return OpenPanelWithArgs( + mojom::RewardsPanelArgs(mojom::RewardsPanelView::kAdaptiveCaptcha, "")); +} + +bool RewardsPanelService::ShowBraveTalkOptIn() { + return OpenPanelWithArgs( + mojom::RewardsPanelArgs(mojom::RewardsPanelView::kBraveTalkOptIn, "")); +} + +void RewardsPanelService::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void RewardsPanelService::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void RewardsPanelService::NotifyPanelClosed(Browser* browser) { + DCHECK(browser); + for (auto& observer : observers_) { + observer.OnRewardsPanelClosed(browser); + } +} + +bool RewardsPanelService::OpenPanelWithArgs(mojom::RewardsPanelArgs&& args) { + auto* browser = chrome::FindTabbedBrowser(profile_, false); + if (!browser) { + return false; + } + + if (browser->window()->IsMinimized()) { + browser->window()->Restore(); + } + + panel_args_ = std::move(args); + + for (auto& observer : observers_) { + observer.OnRewardsPanelRequested(browser, panel_args_); + } + + return !observers_.empty(); +} + +} // namespace brave_rewards diff --git a/browser/brave_rewards/rewards_panel_service.h b/browser/brave_rewards/rewards_panel_service.h new file mode 100644 index 000000000000..ad1238bdab00 --- /dev/null +++ b/browser/brave_rewards/rewards_panel_service.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * 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/. */ + +#ifndef BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_SERVICE_H_ +#define BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_SERVICE_H_ + +#include +#include + +#include "base/memory/raw_ptr.h" +#include "base/observer_list.h" +#include "base/scoped_observation.h" +#include "brave/components/brave_rewards/common/brave_rewards_panel.mojom.h" +#include "components/keyed_service/core/keyed_service.h" +#include "url/gurl.h" + +class Browser; +class Profile; + +namespace brave_rewards { + +// Provides a communication channel for arbitrary browser components that need +// to open the Rewards panel and application views that control the state of the +// Rewards panel. +class RewardsPanelService : public KeyedService { + public: + explicit RewardsPanelService(Profile* profile); + ~RewardsPanelService() override; + + static bool IsRewardsPanelURLForTesting(const GURL& url); + + // Opens the Rewards panel with the default view. + bool OpenRewardsPanel(); + + // Displays the Rewards onboarding tour in the Rewards panel. + bool ShowRewardsTour(); + + // Displays a grant captcha for the specified grant in the Rewards panel. + bool ShowGrantCaptcha(const std::string& grant_id); + + // Opens the Rewards panel in order to display the currently scheduled + // adaptive captcha for the user. + bool ShowAdaptiveCaptcha(); + + // Opens the Rewards panel in order to display the Brave Talk Rewards opt-in. + bool ShowBraveTalkOptIn(); + + class Observer : public base::CheckedObserver { + public: + // Called when an application component requests that the Rewards panel be + // opened. The arguments provided by |OpenRewardsPanel| must be retrieved + // using |TakePanelArguments|. + virtual void OnRewardsPanelRequested(Browser* browser, + const mojom::RewardsPanelArgs& args) {} + + // Called when the Rewards panel has been closed. + virtual void OnRewardsPanelClosed(Browser* browser) {} + }; + + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + using Observation = base::ScopedObservation; + + // Notifies observers that the Rewards panel has been closed. This should + // only be called by UI objects. + void NotifyPanelClosed(Browser* browser); + + // Retrieves the `mojom::RewardsPanelArgs` associated with the most recent + // Rewards panel request. + const mojom::RewardsPanelArgs& panel_args() { return panel_args_; } + + private: + // Opens the Rewards panel using the specified arguments. + bool OpenPanelWithArgs(mojom::RewardsPanelArgs&& args); + + raw_ptr profile_ = nullptr; + base::ObserverList observers_; + mojom::RewardsPanelArgs panel_args_; + std::unique_ptr extension_handler_; +}; + +} // namespace brave_rewards + +#endif // BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_SERVICE_H_ diff --git a/browser/brave_rewards/rewards_panel_service_factory.cc b/browser/brave_rewards/rewards_panel_service_factory.cc new file mode 100644 index 000000000000..4bed2007f435 --- /dev/null +++ b/browser/brave_rewards/rewards_panel_service_factory.cc @@ -0,0 +1,47 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * 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/. */ + +#include "brave/browser/brave_rewards/rewards_panel_service_factory.h" + +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "brave/browser/brave_rewards/rewards_service_factory.h" +#include "brave/browser/profiles/profile_util.h" +#include "chrome/browser/profiles/profile.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace brave_rewards { + +// static +RewardsPanelService* RewardsPanelServiceFactory::GetForProfile( + Profile* profile) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(profile, true)); +} + +// static +RewardsPanelServiceFactory* RewardsPanelServiceFactory::GetInstance() { + static base::NoDestructor factory; + return factory.get(); +} + +RewardsPanelServiceFactory::RewardsPanelServiceFactory() + : BrowserContextKeyedServiceFactory( + "RewardsPanelService", + BrowserContextDependencyManager::GetInstance()) {} + +KeyedService* RewardsPanelServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new RewardsPanelService(Profile::FromBrowserContext(context)); +} + +content::BrowserContext* RewardsPanelServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return RewardsServiceFactory::IsServiceAllowedForContext(context) ? context + : nullptr; +} + +RewardsPanelServiceFactory::~RewardsPanelServiceFactory() = default; + +} // namespace brave_rewards diff --git a/browser/brave_rewards/rewards_panel_service_factory.h b/browser/brave_rewards/rewards_panel_service_factory.h new file mode 100644 index 000000000000..2e9202cfe093 --- /dev/null +++ b/browser/brave_rewards/rewards_panel_service_factory.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * 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/. */ + +#ifndef BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_SERVICE_FACTORY_H_ +#define BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_SERVICE_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +class Profile; + +namespace brave_rewards { + +class RewardsPanelService; + +// A service factory for |RewardsPanelService|. +class RewardsPanelServiceFactory : public BrowserContextKeyedServiceFactory { + public: + static RewardsPanelService* GetForProfile(Profile* profile); + + static RewardsPanelServiceFactory* GetInstance(); + + RewardsPanelServiceFactory(const RewardsPanelServiceFactory&) = delete; + RewardsPanelServiceFactory& operator=(const RewardsPanelServiceFactory&) = + delete; + + private: + friend base::NoDestructor; + + RewardsPanelServiceFactory(); + ~RewardsPanelServiceFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace brave_rewards + +#endif // BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_PANEL_SERVICE_FACTORY_H_ diff --git a/browser/brave_rewards/rewards_service_factory.cc b/browser/brave_rewards/rewards_service_factory.cc index aeabc66338db..6180a17dcb70 100644 --- a/browser/brave_rewards/rewards_service_factory.cc +++ b/browser/brave_rewards/rewards_service_factory.cc @@ -50,10 +50,6 @@ RewardsService* RewardsServiceFactory::GetForProfile( return testing_service_; } - if (!brave::IsRegularProfile(profile)) { - return nullptr; - } - return static_cast( GetInstance()->GetServiceForBrowserContext(profile, true)); } @@ -63,6 +59,12 @@ RewardsServiceFactory* RewardsServiceFactory::GetInstance() { return base::Singleton::get(); } +// static +bool RewardsServiceFactory::IsServiceAllowedForContext( + content::BrowserContext* context) { + return context && brave::IsRegularProfile(context); +} + RewardsServiceFactory::RewardsServiceFactory() : BrowserContextKeyedServiceFactory( "RewardsService", @@ -105,6 +107,11 @@ KeyedService* RewardsServiceFactory::BuildServiceInstanceFor( return rewards_service.release(); } +content::BrowserContext* RewardsServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return IsServiceAllowedForContext(context) ? context : nullptr; +} + // static void RewardsServiceFactory::SetServiceForTesting(RewardsService* service) { testing_service_ = service; diff --git a/browser/brave_rewards/rewards_service_factory.h b/browser/brave_rewards/rewards_service_factory.h index bc77ac4f61a3..bf30da122731 100644 --- a/browser/brave_rewards/rewards_service_factory.h +++ b/browser/brave_rewards/rewards_service_factory.h @@ -14,6 +14,7 @@ class Profile; namespace brave_rewards { + class RewardsService; // Singleton that owns all RewardsService and associates them with Profiles. @@ -22,10 +23,12 @@ class RewardsServiceFactory : public BrowserContextKeyedServiceFactory { RewardsServiceFactory(const RewardsServiceFactory&) = delete; RewardsServiceFactory& operator=(const RewardsServiceFactory&) = delete; - static brave_rewards::RewardsService* GetForProfile(Profile* profile); + static RewardsService* GetForProfile(Profile* profile); static RewardsServiceFactory* GetInstance(); + static bool IsServiceAllowedForContext(content::BrowserContext* context); + static void SetServiceForTesting(RewardsService* service); private: @@ -37,6 +40,10 @@ class RewardsServiceFactory : public BrowserContextKeyedServiceFactory { // BrowserContextKeyedServiceFactory: KeyedService* BuildServiceInstanceFor( content::BrowserContext* context) const override; + + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsNULLWhileTesting() const override; }; diff --git a/browser/brave_rewards/rewards_tab_helper.cc b/browser/brave_rewards/rewards_tab_helper.cc index 05cd1137eb17..23bb1510e1e0 100644 --- a/browser/brave_rewards/rewards_tab_helper.cc +++ b/browser/brave_rewards/rewards_tab_helper.cc @@ -5,15 +5,25 @@ #include "brave/browser/brave_rewards/rewards_tab_helper.h" +#include +#include + #include "brave/browser/brave_rewards/rewards_service_factory.h" #include "brave/components/brave_rewards/browser/rewards_service.h" #include "brave/components/ipfs/buildflags/buildflags.h" + +#if BUILDFLAG(ENABLE_IPFS) +#include "brave/components/ipfs/ipfs_constants.h" +#endif + #include "chrome/browser/profiles/profile.h" + #if !BUILDFLAG(IS_ANDROID) #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #endif + #include "components/sessions/content/session_tab_helper.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" @@ -22,85 +32,103 @@ #include "content/public/browser/web_contents_user_data.h" #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h" -#if BUILDFLAG(ENABLE_IPFS) -#include "brave/components/ipfs/ipfs_constants.h" -#endif - -using blink::mojom::ResourceType; - -// DEFINE_WEB_CONTENTS_USER_DATA_KEY(brave_rewards::RewardsTabHelper); - namespace brave_rewards { RewardsTabHelper::RewardsTabHelper(content::WebContents* web_contents) - : WebContentsObserver(web_contents), - content::WebContentsUserData(*web_contents), + : content::WebContentsUserData(*web_contents), + WebContentsObserver(web_contents), tab_id_(sessions::SessionTabHelper::IdForTab(web_contents)) { - if (!tab_id_.is_valid()) - return; + if (tab_id_.is_valid()) { + rewards_service_ = RewardsServiceFactory::GetForProfile( + Profile::FromBrowserContext(web_contents->GetBrowserContext())); + } + + if (rewards_service_) { + rewards_service_->AddObserver(this); + } #if !BUILDFLAG(IS_ANDROID) BrowserList::AddObserver(this); #endif - Profile* profile = - Profile::FromBrowserContext(web_contents->GetBrowserContext()); - rewards_service_ = RewardsServiceFactory::GetForProfile(profile); - if (rewards_service_) - rewards_service_->AddObserver(this); } RewardsTabHelper::~RewardsTabHelper() { - if (rewards_service_) + if (rewards_service_) { rewards_service_->RemoveObserver(this); + } #if !BUILDFLAG(IS_ANDROID) BrowserList::RemoveObserver(this); #endif } +void RewardsTabHelper::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void RewardsTabHelper::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +void RewardsTabHelper::SetPublisherIdForTab(const std::string& publisher_id) { + if (publisher_id != publisher_id_) { + publisher_id_ = publisher_id; + for (auto& observer : observer_list_) { + observer.OnPublisherForTabUpdated(publisher_id_); + } + } +} + void RewardsTabHelper::DidFinishLoad( content::RenderFrameHost* render_frame_host, const GURL& validated_url) { - if (!rewards_service_ || render_frame_host->GetParent()) + if (!rewards_service_ || render_frame_host->GetParent()) { return; + } #if BUILDFLAG(ENABLE_IPFS) - auto ipns_url = web_contents()->GetURL(); + auto ipns_url = web_contents()->GetLastCommittedURL(); if (ipns_url.SchemeIs(ipfs::kIPNSScheme)) { rewards_service_->OnLoad(tab_id_, ipns_url); return; } #endif + rewards_service_->OnLoad(tab_id_, validated_url); } void RewardsTabHelper::DidFinishNavigation(content::NavigationHandle* handle) { - if (!rewards_service_ || !handle->IsInMainFrame() || - !handle->HasCommitted() || handle->IsDownload()) + if (!handle->IsInMainFrame() || !handle->HasCommitted() || + handle->IsDownload()) { return; + } - rewards_service_->OnUnload(tab_id_); + SetPublisherIdForTab(RewardsService::GetPublisherIdFromURL( + web_contents()->GetLastCommittedURL())); + + MaybeSavePublisherInfo(); + + if (rewards_service_) { + rewards_service_->OnUnload(tab_id_); + } } void RewardsTabHelper::ResourceLoadComplete( content::RenderFrameHost* render_frame_host, const content::GlobalRequestID& request_id, const blink::mojom::ResourceLoadInfo& resource_load_info) { - if (!rewards_service_ || !render_frame_host) + if (!rewards_service_ || !render_frame_host) { return; + } - // TODO(nejczdovc): do we need to get anyother type then XHR?? switch (resource_load_info.request_destination) { - // Formerly ResourceType::kMedia case network::mojom::RequestDestination::kAudio: case network::mojom::RequestDestination::kTrack: case network::mojom::RequestDestination::kVideo: - // Best match for ResourceType::kXhr (though, not limited to kXhr) case network::mojom::RequestDestination::kEmpty: - // Formerly ResourceType::kImage case network::mojom::RequestDestination::kImage: case network::mojom::RequestDestination::kScript: rewards_service_->OnXHRLoad(tab_id_, GURL(resource_load_info.final_url), - web_contents()->GetURL(), + web_contents()->GetLastCommittedURL(), resource_load_info.referrer); break; default: @@ -109,8 +137,9 @@ void RewardsTabHelper::ResourceLoadComplete( } void RewardsTabHelper::OnVisibilityChanged(content::Visibility visibility) { - if (!rewards_service_) + if (!rewards_service_) { return; + } if (visibility == content::Visibility::HIDDEN) { rewards_service_->OnHide(tab_id_); @@ -122,34 +151,51 @@ void RewardsTabHelper::OnVisibilityChanged(content::Visibility visibility) { } void RewardsTabHelper::WebContentsDestroyed() { - if (rewards_service_) + if (rewards_service_) { rewards_service_->OnUnload(tab_id_); + } } #if !BUILDFLAG(IS_ANDROID) void RewardsTabHelper::OnBrowserSetLastActive(Browser* browser) { - if (!rewards_service_) - return; - - if (browser->tab_strip_model()->GetIndexOfWebContents(web_contents()) != - TabStripModel::kNoTab) { + if (rewards_service_ && BrowserHasWebContents(browser)) { rewards_service_->OnForeground(tab_id_); } } -#endif -#if !BUILDFLAG(IS_ANDROID) void RewardsTabHelper::OnBrowserNoLongerActive(Browser* browser) { - if (!rewards_service_) - return; - - if (browser->tab_strip_model()->GetIndexOfWebContents(web_contents()) != - TabStripModel::kNoTab) { + if (rewards_service_ && BrowserHasWebContents(browser)) { rewards_service_->OnBackground(tab_id_); } } + +bool RewardsTabHelper::BrowserHasWebContents(Browser* browser) { + int index = browser->tab_strip_model()->GetIndexOfWebContents(web_contents()); + return index != TabStripModel::kNoTab; +} #endif +void RewardsTabHelper::OnRewardsInitialized(RewardsService* rewards_service) { + MaybeSavePublisherInfo(); + if (rewards_service_) { + rewards_service_->OnLoad(tab_id_, web_contents()->GetLastCommittedURL()); + } +} + +void RewardsTabHelper::MaybeSavePublisherInfo() { + if (!rewards_service_) { + return; + } + + // The Rewards system assumes that the |publisher_info| table is populated as + // the user nativates the web. Previously, this was accomplished within the + // background script of the Rewards extension. + // TODO(zenparsing): Consider changing the name of this method to be more + // self-documenting. + rewards_service_->GetPublisherActivityFromUrl( + tab_id_.id(), web_contents()->GetLastCommittedURL().spec(), "", ""); +} + WEB_CONTENTS_USER_DATA_KEY_IMPL(RewardsTabHelper); } // namespace brave_rewards diff --git a/browser/brave_rewards/rewards_tab_helper.h b/browser/brave_rewards/rewards_tab_helper.h index 0ebb3ee2b246..3a3112f25954 100644 --- a/browser/brave_rewards/rewards_tab_helper.h +++ b/browser/brave_rewards/rewards_tab_helper.h @@ -9,6 +9,9 @@ #include #include "base/memory/raw_ptr.h" +#include "base/observer_list.h" +#include "base/observer_list_types.h" +#include "base/scoped_observation.h" #include "brave/components/brave_rewards/browser/rewards_service_observer.h" #include "build/build_config.h" #include "components/sessions/core/session_id.h" @@ -25,22 +28,44 @@ namespace brave_rewards { class RewardsService; -class RewardsTabHelper : public RewardsServiceObserver, +// A tab helper responsible for sending user-activity events to the Rewards +// engine in order to support the Auto Contribute feature, and for storing the +// publisher ID corresponding to a given tab. +class RewardsTabHelper : public content::WebContentsUserData, public content::WebContentsObserver, #if !BUILDFLAG(IS_ANDROID) public BrowserListObserver, #endif - public content::WebContentsUserData { + public RewardsServiceObserver { public: - explicit RewardsTabHelper(content::WebContents*); RewardsTabHelper(const RewardsTabHelper&) = delete; RewardsTabHelper& operator=(const RewardsTabHelper&) = delete; ~RewardsTabHelper() override; + class Observer : public base::CheckedObserver { + public: + virtual void OnPublisherForTabUpdated(const std::string& publisher_id) = 0; + }; + + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + using Observation = base::ScopedObservation; + + // Returns the publisher ID associated with the web content loaded into this + // tab. The publisher ID does not necessarily refer to a registered publisher. + std::string GetPublisherIdForTab() { return publisher_id_; } + + // Sets the publisher ID associated with the web content loaded into this tab. + // This method can be used to override the default publisher ID as determined + // by the current domain. + void SetPublisherIdForTab(const std::string& publisher_id); + private: friend class content::WebContentsUserData; - // content::WebContentsObserver overrides. + explicit RewardsTabHelper(content::WebContents* web_contents); + + // content::WebContentsObserver: void DidFinishLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url) override; void DidFinishNavigation( @@ -53,13 +78,22 @@ class RewardsTabHelper : public RewardsServiceObserver, void WebContentsDestroyed() override; #if !BUILDFLAG(IS_ANDROID) - // BrowserListObserver overrides + // BrowserListObserver: void OnBrowserSetLastActive(Browser* browser) override; void OnBrowserNoLongerActive(Browser* browser) override; + + bool BrowserHasWebContents(Browser* browser); #endif + // RewardsServiceObserver: + void OnRewardsInitialized(RewardsService* rewards_service) override; + + void MaybeSavePublisherInfo(); + SessionID tab_id_; raw_ptr rewards_service_ = nullptr; // NOT OWNED + base::ObserverList observer_list_; + std::string publisher_id_; WEB_CONTENTS_USER_DATA_KEY_DECL(); }; diff --git a/browser/brave_rewards/sources.gni b/browser/brave_rewards/sources.gni index 14c1a1c6b366..eabe1b87a23b 100644 --- a/browser/brave_rewards/sources.gni +++ b/browser/brave_rewards/sources.gni @@ -22,6 +22,7 @@ brave_browser_brave_rewards_deps = [ "//brave/browser/profiles:util", "//brave/components/brave_rewards/browser", "//brave/components/brave_rewards/common", + "//brave/components/brave_rewards/common:mojom", "//brave/components/ipfs/buildflags", "//brave/vendor/bat-native-ledger:headers", "//chrome/browser:browser_process", @@ -44,8 +45,12 @@ if (enable_extensions) { "//brave/browser/brave_rewards/extension_rewards_notification_service_observer.h", "//brave/browser/brave_rewards/extension_rewards_service_observer.cc", "//brave/browser/brave_rewards/extension_rewards_service_observer.h", - "//brave/browser/brave_rewards/rewards_panel_helper.cc", - "//brave/browser/brave_rewards/rewards_panel_helper.h", + "//brave/browser/brave_rewards/rewards_extension_panel_handler.cc", + "//brave/browser/brave_rewards/rewards_extension_panel_handler.h", + "//brave/browser/brave_rewards/rewards_panel_service.cc", + "//brave/browser/brave_rewards/rewards_panel_service.h", + "//brave/browser/brave_rewards/rewards_panel_service_factory.cc", + "//brave/browser/brave_rewards/rewards_panel_service_factory.h", ] brave_browser_brave_rewards_deps += [ diff --git a/browser/extensions/api/brave_rewards_api.cc b/browser/extensions/api/brave_rewards_api.cc index 0dc70b6fdfa6..bb382f0e168a 100644 --- a/browser/extensions/api/brave_rewards_api.cc +++ b/browser/extensions/api/brave_rewards_api.cc @@ -13,7 +13,10 @@ #include "base/bind.h" #include "base/strings/string_number_conversions.h" #include "brave/browser/brave_ads/ads_service_factory.h" +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "brave/browser/brave_rewards/rewards_panel_service_factory.h" #include "brave/browser/brave_rewards/rewards_service_factory.h" +#include "brave/browser/brave_rewards/rewards_tab_helper.h" #include "brave/browser/brave_rewards/tip_dialog.h" #include "brave/browser/extensions/api/brave_action_api.h" #include "brave/browser/extensions/brave_component_loader.h" @@ -29,6 +32,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/profiles/profile.h" +#include "components/prefs/pref_service.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/extension_system.h" #include "extensions/common/constants.h" @@ -40,6 +44,7 @@ using brave_ads::AdsService; using brave_ads::AdsServiceFactory; +using brave_rewards::RewardsPanelServiceFactory; using brave_rewards::RewardsService; using brave_rewards::RewardsServiceFactory; @@ -51,6 +56,27 @@ const char kAdsSubdivisionTargeting[] = "adsSubdivisionTargeting"; const char kAutoDetectedAdsSubdivisionTargeting[] = "automaticallyDetectedAdsSubdivisionTargeting"; +content::WebContents* GetWebContentsForTabId( + int tab_id, + content::BrowserContext* browser_context) { + DCHECK(browser_context); + content::WebContents* contents = nullptr; + bool found = extensions::ExtensionTabUtil::GetTabById( + tab_id, browser_context, false, nullptr, nullptr, &contents, nullptr); + return found ? contents : nullptr; +} + +brave_rewards::RewardsTabHelper* GetRewardsTabHelperForTabId( + int tab_id, + content::BrowserContext* browser_context) { + DCHECK(browser_context); + auto* web_contents = GetWebContentsForTabId(tab_id, browser_context); + if (!web_contents) { + return nullptr; + } + return ::brave_rewards::RewardsTabHelper::FromWebContents(web_contents); +} + } // namespace namespace extensions { @@ -63,41 +89,40 @@ ExtensionFunction::ResponseAction BraveRewardsGetLocaleFunction::Run() { return RespondNow(OneArgument(base::Value(std::move(locale)))); } -BraveRewardsOpenBrowserActionUIFunction:: -~BraveRewardsOpenBrowserActionUIFunction() { -} - -ExtensionFunction::ResponseAction -BraveRewardsOpenBrowserActionUIFunction::Run() { - std::unique_ptr params( - brave_rewards::OpenBrowserActionUI::Params::Create(args())); - EXTENSION_FUNCTION_VALIDATE(params.get()); +BraveRewardsOpenRewardsPanelFunction::~BraveRewardsOpenRewardsPanelFunction() = + default; +ExtensionFunction::ResponseAction BraveRewardsOpenRewardsPanelFunction::Run() { auto* profile = Profile::FromBrowserContext(browser_context()); + if (auto* service = RewardsPanelServiceFactory::GetForProfile(profile)) { + service->OpenRewardsPanel(); + } + return RespondNow(NoArguments()); +} - // Start the rewards ledger process if it is not already started - auto* rewards_service = RewardsServiceFactory::GetForProfile(profile); - if (!rewards_service) - return RespondNow(Error("Rewards service is not initialized")); +BraveRewardsShowRewardsTourFunction::~BraveRewardsShowRewardsTourFunction() = + default; - rewards_service->StartProcess(base::DoNothing()); +ExtensionFunction::ResponseAction BraveRewardsShowRewardsTourFunction::Run() { + auto* profile = Profile::FromBrowserContext(browser_context()); + if (auto* service = RewardsPanelServiceFactory::GetForProfile(profile)) { + service->ShowRewardsTour(); + } + return RespondNow(NoArguments()); +} - // Load the rewards extension if it is not already loaded - auto* extension_service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - if (!extension_service) - return RespondNow(Error("Extension service is not initialized")); +BraveRewardsShowGrantCaptchaFunction::~BraveRewardsShowGrantCaptchaFunction() = + default; - static_cast(extension_service->component_loader()) - ->AddRewardsExtension(); +ExtensionFunction::ResponseAction BraveRewardsShowGrantCaptchaFunction::Run() { + auto params = brave_rewards::ShowGrantCaptcha::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params.get()); - std::string error; - if (!BraveActionAPI::ShowActionUI(this, - brave_rewards_extension_id, - std::move(params->window_id), - std::move(params->relative_path), &error)) { - return RespondNow(Error(error)); + auto* profile = Profile::FromBrowserContext(browser_context()); + if (auto* service = RewardsPanelServiceFactory::GetForProfile(profile)) { + service->ShowGrantCaptcha(params->grant_id); } + return RespondNow(NoArguments()); } @@ -175,6 +200,80 @@ void BraveRewardsGetPublisherInfoFunction::OnGetPublisherInfo( Respond(TwoArguments(base::Value(static_cast(result)), std::move(dict))); } +BraveRewardsSetPublisherIdForTabFunction:: + ~BraveRewardsSetPublisherIdForTabFunction() {} + +ExtensionFunction::ResponseAction +BraveRewardsSetPublisherIdForTabFunction::Run() { + auto params = brave_rewards::SetPublisherIdForTab::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + auto* tab_helper = + GetRewardsTabHelperForTabId(params->tab_id, browser_context()); + + if (tab_helper) { + tab_helper->SetPublisherIdForTab(params->publisher_id); + } + + return RespondNow(NoArguments()); +} + +BraveRewardsGetPublisherInfoForTabFunction:: + ~BraveRewardsGetPublisherInfoForTabFunction() {} + +ExtensionFunction::ResponseAction +BraveRewardsGetPublisherInfoForTabFunction::Run() { + auto params = brave_rewards::GetPublisherInfoForTab::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + auto* profile = Profile::FromBrowserContext(browser_context()); + + auto* rewards_service = RewardsServiceFactory::GetForProfile(profile); + if (!rewards_service) { + return RespondNow(NoArguments()); + } + + auto* tab_helper = GetRewardsTabHelperForTabId(params->tab_id, profile); + if (!tab_helper) { + return RespondNow(NoArguments()); + } + + std::string publisher_id = tab_helper->GetPublisherIdForTab(); + if (publisher_id.empty()) { + return RespondNow(NoArguments()); + } + + rewards_service->GetPublisherPanelInfo( + publisher_id, + base::BindOnce( + &BraveRewardsGetPublisherInfoForTabFunction::OnGetPublisherPanelInfo, + this)); + + return RespondLater(); +} + +void BraveRewardsGetPublisherInfoForTabFunction::OnGetPublisherPanelInfo( + ledger::type::Result result, + ledger::type::PublisherInfoPtr info) { + if (!info) { + Respond(NoArguments()); + return; + } + + base::Value dict(base::Value::Type::DICTIONARY); + dict.SetStringKey("publisherKey", info->id); + dict.SetStringKey("name", info->name); + dict.SetIntKey("percentage", info->percent); + dict.SetIntKey("status", static_cast(info->status)); + dict.SetBoolKey("excluded", + info->excluded == ledger::type::PublisherExclude::EXCLUDED); + dict.SetStringKey("url", info->url); + dict.SetStringKey("provider", info->provider); + dict.SetStringKey("favIconUrl", info->favicon_url); + + Respond(OneArgument(std::move(dict))); +} + BraveRewardsGetPublisherPanelInfoFunction:: ~BraveRewardsGetPublisherPanelInfoFunction() {} @@ -318,19 +417,8 @@ ExtensionFunction::ResponseAction BraveRewardsTipUserFunction::Run() { return RespondNow(Error("Rewards service is not initialized")); } - extensions::ExtensionService* extension_service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - if (!extension_service) { - return RespondNow(Error("Extension service is not initialized")); - } - AddRef(); - extensions::ComponentLoader* component_loader = - extension_service->component_loader(); - static_cast(component_loader) - ->AddRewardsExtension(); - rewards_service->StartProcess( base::BindOnce(&BraveRewardsTipUserFunction::OnProcessStarted, this, params->publisher_key)); diff --git a/browser/extensions/api/brave_rewards_api.h b/browser/extensions/api/brave_rewards_api.h index bd3628bbe88c..46d0e7d0ac1b 100644 --- a/browser/extensions/api/brave_rewards_api.h +++ b/browser/extensions/api/brave_rewards_api.h @@ -26,13 +26,32 @@ class BraveRewardsGetLocaleFunction : public ExtensionFunction { ResponseAction Run() override; }; -class BraveRewardsOpenBrowserActionUIFunction : - public ExtensionFunction { +class BraveRewardsOpenRewardsPanelFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.openRewardsPanel", UNKNOWN) + + protected: + ~BraveRewardsOpenRewardsPanelFunction() override; + + ResponseAction Run() override; +}; + +class BraveRewardsShowRewardsTourFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.showRewardsTour", UNKNOWN) + + protected: + ~BraveRewardsShowRewardsTourFunction() override; + + ResponseAction Run() override; +}; + +class BraveRewardsShowGrantCaptchaFunction : public ExtensionFunction { public: - DECLARE_EXTENSION_FUNCTION("braveRewards.openBrowserActionUI", UNKNOWN) + DECLARE_EXTENSION_FUNCTION("braveRewards.showGrantCaptcha", UNKNOWN) protected: - ~BraveRewardsOpenBrowserActionUIFunction() override; + ~BraveRewardsShowGrantCaptchaFunction() override; ResponseAction Run() override; }; @@ -62,6 +81,29 @@ class BraveRewardsGetPublisherInfoFunction : public ExtensionFunction { ledger::type::PublisherInfoPtr info); }; +class BraveRewardsSetPublisherIdForTabFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.setPublisherIdForTab", UNKNOWN) + + protected: + ~BraveRewardsSetPublisherIdForTabFunction() override; + ResponseAction Run() override; +}; + +class BraveRewardsGetPublisherInfoForTabFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.getPublisherInfoForTab", UNKNOWN) + + protected: + ~BraveRewardsGetPublisherInfoForTabFunction() override; + + ResponseAction Run() override; + + private: + void OnGetPublisherPanelInfo(ledger::type::Result result, + ledger::type::PublisherInfoPtr info); +}; + class BraveRewardsGetPublisherPanelInfoFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("braveRewards.getPublisherPanelInfo", UNKNOWN) diff --git a/browser/extensions/brave_component_loader.cc b/browser/extensions/brave_component_loader.cc index 611bbdcb3fc3..9d2626bb717d 100644 --- a/browser/extensions/brave_component_loader.cc +++ b/browser/extensions/brave_component_loader.cc @@ -9,11 +9,13 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/feature_list.h" #include "bat/ads/pref_names.h" #include "brave/browser/component_updater/brave_component_installer.h" #include "brave/components/brave_ads/common/pref_names.h" #include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" #include "brave/components/brave_extension/grit/brave_extension.h" +#include "brave/components/brave_rewards/common/features.h" #include "brave/components/brave_rewards/common/pref_names.h" #include "brave/components/brave_rewards/resources/extension/grit/brave_rewards_extension_resources.h" #include "brave/components/brave_webtorrent/grit/brave_webtorrent_resources.h" @@ -139,7 +141,9 @@ void BraveComponentLoader::AddRewardsExtension() { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); if (!command_line.HasSwitch(switches::kDisableBraveRewardsExtension) && - !Exists(brave_rewards_extension_id)) { + !Exists(brave_rewards_extension_id) && + !base::FeatureList::IsEnabled( + brave_rewards::features::kWebUIPanelFeature)) { base::FilePath brave_rewards_path(FILE_PATH_LITERAL("")); brave_rewards_path = brave_rewards_path.Append(FILE_PATH_LITERAL("brave_rewards")); diff --git a/browser/resources/settings/brave_rewards_page/brave_rewards_page.js b/browser/resources/settings/brave_rewards_page/brave_rewards_page.js index 21584bf9ac65..d56d1d6ad011 100644 --- a/browser/resources/settings/brave_rewards_page/brave_rewards_page.js +++ b/browser/resources/settings/brave_rewards_page/brave_rewards_page.js @@ -164,7 +164,7 @@ class SettingsBraveRewardsPage extends SettingsBraveRewardsPageBase { ready() { super.ready() this.openRewardsPanel_ = () => { - chrome.braveRewards.openBrowserActionUI('brave_rewards_panel.html') + chrome.braveRewards.openRewardsPanel() this.isAutoContributeSupported_() } this.browserProxy_.getRewardsEnabled().then((enabled) => { diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index 2392e804a06b..954f2e363375 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -99,6 +99,8 @@ source_set("ui") { "toolbar/brave_app_menu_model.cc", "toolbar/brave_app_menu_model.h", "toolbar/brave_recent_tabs_sub_menu_model.h", + "webui/brave_rewards/rewards_panel_ui.cc", + "webui/brave_rewards/rewards_panel_ui.h", "webui/brave_settings_ui.cc", "webui/brave_settings_ui.h", "webui/brave_shields/shields_panel_data_handler.cc", @@ -285,7 +287,6 @@ source_set("ui") { "//brave/components/brave_rewards/resources", "//brave/components/brave_rewards/resources:internals_generated_resources", "//brave/components/brave_rewards/resources:page_generated_resources", - "//brave/components/brave_rewards/resources:tip_generated_resources", "//brave/components/brave_search/common", "//brave/components/brave_shields/browser", "//brave/components/brave_vpn/buildflags", @@ -441,6 +442,8 @@ source_set("ui") { "//brave/browser/ui/bookmark", "//brave/components/brave_new_tab_ui:generated_resources", "//brave/components/brave_new_tab_ui:mojom", + "//brave/components/brave_rewards/resources:tip_generated_resources", + "//brave/components/brave_rewards/resources/rewards_panel:brave_rewards_panel_generated", "//brave/components/brave_shields/common:mojom", "//brave/components/brave_shields/resources/panel:brave_shields_panel_generated", "//brave/components/brave_sync", @@ -478,6 +481,8 @@ source_set("ui") { "views/brave_actions/brave_actions_container.h", "views/brave_actions/brave_rewards_action_stub_view.cc", "views/brave_actions/brave_rewards_action_stub_view.h", + "views/brave_actions/brave_rewards_action_view.cc", + "views/brave_actions/brave_rewards_action_view.h", "views/brave_actions/brave_shields_action_view.cc", "views/brave_actions/brave_shields_action_view.h", "views/location_bar/brave_location_bar_view.cc", diff --git a/browser/ui/views/brave_actions/brave_actions_container.cc b/browser/ui/views/brave_actions/brave_actions_container.cc index 043a94b88fc8..ca24d39cf2bf 100644 --- a/browser/ui/views/brave_actions/brave_actions_container.cc +++ b/browser/ui/views/brave_actions/brave_actions_container.cc @@ -18,8 +18,10 @@ #include "brave/browser/ui/brave_actions/brave_action_view_controller_factory.h" #include "brave/browser/ui/views/brave_actions/brave_action_view.h" #include "brave/browser/ui/views/brave_actions/brave_rewards_action_stub_view.h" +#include "brave/browser/ui/views/brave_actions/brave_rewards_action_view.h" #include "brave/browser/ui/views/brave_actions/brave_shields_action_view.h" #include "brave/browser/ui/views/rounded_separator.h" +#include "brave/components/brave_rewards/common/features.h" #include "brave/components/brave_rewards/common/pref_names.h" #include "brave/components/brave_shields/common/features.h" #include "brave/components/constants/brave_switches.h" @@ -42,6 +44,15 @@ #include "ui/views/layout/grid_layout.h" #include "ui/views/view.h" +namespace { + +bool UsingRewardsExtension() { + return !base::FeatureList::IsEnabled( + brave_rewards::features::kWebUIPanelFeature); +} + +} // namespace + BraveActionsContainer::BraveActionInfo::BraveActionInfo() : position_(ACTION_ANY_POSITION) {} @@ -110,7 +121,11 @@ void BraveActionsContainer::Init() { brave_shields::features::kBraveShieldsPanelV1)) { actions_[brave_extension_id].position_ = 1; } - actions_[brave_rewards_extension_id].position_ = ACTION_ANY_POSITION; + if (UsingRewardsExtension()) { + actions_[brave_rewards_extension_id].position_ = ACTION_ANY_POSITION; + } else { + AddActionViewForRewards(); + } // React to Brave Rewards preferences changes. show_brave_rewards_button_.Init( @@ -141,7 +156,7 @@ bool BraveActionsContainer::ShouldShowBraveRewardsAction() const { return false; } - if (!brave::IsRegularProfile(browser_->profile())) { + if (!rewards_service_) { return false; } @@ -276,26 +291,53 @@ void BraveActionsContainer::AddActionViewForShields() { } } +void BraveActionsContainer::AddActionViewForRewards() { + auto button = std::make_unique(browser_); + rewards_action_btn_ = AddChildViewAt(std::move(button), 2); + rewards_action_btn_->SetPreferredSize(GetToolbarActionSize()); + rewards_action_btn_->SetVisible(ShouldShowBraveRewardsAction()); + rewards_action_btn_->Update(); +} + void BraveActionsContainer::Update() { - // Update state of each action and also determine if there are any buttons to - // show + if (shields_action_btn_) { + shields_action_btn_->Update(); + } + + if (rewards_action_btn_) { + rewards_action_btn_->Update(); + } + + for (auto& [_, value] : actions_) { + if (value.view_controller_) { + value.view_controller_->UpdateState(); + } + } + + UpdateVisibility(); + Layout(); +} + +void BraveActionsContainer::UpdateVisibility() { bool can_show = false; if (shields_action_btn_) { - shields_action_btn_->Update(); can_show = shields_action_btn_->GetVisible(); } - for (auto const& pair : actions_) { - if (pair.second.view_controller_) - pair.second.view_controller_->UpdateState(); - if (!can_show && pair.second.view_ && pair.second.view_->GetVisible()) - can_show = true; + if (rewards_action_btn_) { + can_show = can_show || rewards_action_btn_->GetVisible(); } - // only show separator if we're showing any buttons - const bool visible = !should_hide_ && can_show; - SetVisible(visible); - Layout(); + + for (auto& [_, value] : actions_) { + if (value.view_) { + can_show = can_show || value.view_->GetVisible(); + } + } + + // If no buttons are visible, then we want to hide this view so that the + // separator is not displayed. + SetVisible(!should_hide_ && can_show); } void BraveActionsContainer::SetShouldHide(bool should_hide) { @@ -367,7 +409,9 @@ void BraveActionsContainer::OnExtensionSystemReady() { brave_action_observer_.Observe(brave_action_api_); // Check if extensions already loaded AddAction(brave_extension_id); - AddAction(brave_rewards_extension_id); + if (UsingRewardsExtension()) { + AddAction(brave_rewards_extension_id); + } } // ExtensionRegistry::Observer @@ -420,7 +464,11 @@ void BraveActionsContainer::ChildPreferredSizeChanged(views::View* child) { // Brave Rewards preferences change observers callback void BraveActionsContainer::OnBraveRewardsPreferencesChanged() { - UpdateActionVisibility(brave_rewards_extension_id); + if (rewards_action_btn_) { + rewards_action_btn_->SetVisible(ShouldShowBraveRewardsAction()); + } else { + UpdateActionVisibility(brave_rewards_extension_id); + } } ToolbarActionViewController* BraveActionsContainer::GetActionForId( diff --git a/browser/ui/views/brave_actions/brave_actions_container.h b/browser/ui/views/brave_actions/brave_actions_container.h index 446c67d3efb0..b09d0f56d65e 100644 --- a/browser/ui/views/brave_actions/brave_actions_container.h +++ b/browser/ui/views/brave_actions/brave_actions_container.h @@ -29,6 +29,7 @@ class BraveActionViewController; class BraveActionsContainerTest; +class BraveRewardsActionView; class BraveShieldsActionView; class RewardsBrowserTest; @@ -161,6 +162,7 @@ class BraveActionsContainer : public views::View, void AddAction(const std::string& id); bool ShouldShowBraveRewardsAction() const; void AddActionStubForRewards(); + void AddActionViewForRewards(); void AddActionViewForShields(); void RemoveAction(const std::string& id); void UpdateActionVisibility(const std::string& id); @@ -169,6 +171,8 @@ class BraveActionsContainer : public views::View, void UpdateActionState(const std::string& id); void AttachAction(const std::string& id); + void UpdateVisibility(); + // BraveActionAPI::Observer void OnBraveActionShouldTrigger(const std::string& extension_id, std::unique_ptr ui_relative_path) override; @@ -208,7 +212,8 @@ class BraveActionsContainer : public views::View, extensions::BraveActionAPI::Observer> brave_action_observer_{this}; - BraveShieldsActionView* shields_action_btn_ = nullptr; + raw_ptr shields_action_btn_ = nullptr; + raw_ptr rewards_action_btn_ = nullptr; // Listen for Brave Rewards preferences changes. BooleanPrefMember brave_rewards_enabled_; diff --git a/browser/ui/views/brave_actions/brave_actions_container_browsertest.cc b/browser/ui/views/brave_actions/brave_actions_container_browsertest.cc index 9ec52a59ff47..d2935bc28577 100644 --- a/browser/ui/views/brave_actions/brave_actions_container_browsertest.cc +++ b/browser/ui/views/brave_actions/brave_actions_container_browsertest.cc @@ -5,7 +5,10 @@ #include "base/callback_helpers.h" #include "base/memory/raw_ptr.h" +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "brave/browser/brave_rewards/rewards_panel_service_factory.h" #include "brave/browser/ui/views/brave_actions/brave_actions_container.h" +#include "brave/browser/ui/views/brave_actions/brave_rewards_action_view.h" #include "brave/browser/ui/views/location_bar/brave_location_bar_view.h" #include "brave/components/brave_rewards/common/pref_names.h" #include "brave/components/constants/pref_names.h" @@ -49,12 +52,25 @@ class BraveActionsContainerTest : public InProcessBrowserTest { prefs_ = browser->profile()->GetPrefs(); } + bool UsingRewardsExtension() { + return !brave_actions_->rewards_action_btn_; + } + void CheckBraveRewardsActionShown(bool expected_shown) { - const bool shown = - brave_actions_->IsActionShown(brave_rewards_extension_id); + const bool shown = UsingRewardsExtension() + ? brave_actions_->IsActionShown(brave_rewards_extension_id) + : brave_actions_->rewards_action_btn_->GetVisible(); ASSERT_EQ(shown, expected_shown); } + void CloseRewardsPanel() { + if (UsingRewardsExtension()) { + static_cast(brave_actions_)->HideActivePopup(); + } else { + brave_actions_->rewards_action_btn_->ClosePanelForTesting(); + } + } + protected: raw_ptr brave_actions_ = nullptr; raw_ptr prefs_ = nullptr; @@ -108,23 +124,20 @@ IN_PROC_BROWSER_TEST_F(BraveActionsContainerTest, ShowRewardsIconForPanel) { prefs_->SetBoolean(brave_rewards::prefs::kShowButton, false); CheckBraveRewardsActionShown(false); - // Simulate pressing the "stub" button to ensure that the extension is loaded. - brave_actions_->OnRewardsStubButtonClicked(); - base::RunLoop().RunUntilIdle(); - - // Simulate an action from the brave actions API to open the rewards panel. - extensions::BraveActionAPI::Observer* action_observer = brave_actions_; - action_observer->OnBraveActionShouldTrigger(brave_rewards_extension_id, - nullptr); + // Send a request to open the Rewards panel. + auto* service = brave_rewards::RewardsPanelServiceFactory::GetForProfile( + browser()->profile()); + ASSERT_TRUE(service); + service->OpenRewardsPanel(); base::RunLoop().RunUntilIdle(); - // Rewards action should be shown while popup is open. - CheckBraveRewardsActionShown(true); + // If the Rewards extension is used to display the Rewards panel, then the + // Rewards action should be shown while the popup is open. + CheckBraveRewardsActionShown(UsingRewardsExtension()); // Close the rewards popup. - static_cast(brave_actions_)->HideActivePopup(); - + CloseRewardsPanel(); base::RunLoop().RunUntilIdle(); // Rewards action should be hidden after popup is closed. diff --git a/browser/ui/views/brave_actions/brave_rewards_action_view.cc b/browser/ui/views/brave_actions/brave_rewards_action_view.cc new file mode 100644 index 000000000000..685fbba13850 --- /dev/null +++ b/browser/ui/views/brave_actions/brave_rewards_action_view.cc @@ -0,0 +1,435 @@ +// Copyright (c) 2022 The Brave Authors. All rights reserved. +// 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/. + +#include "brave/browser/ui/views/brave_actions/brave_rewards_action_view.h" + +#include +#include +#include + +#include "base/strings/string_number_conversions.h" +#include "brave/app/vector_icons/vector_icons.h" +#include "brave/browser/brave_rewards/rewards_panel_service_factory.h" +#include "brave/browser/brave_rewards/rewards_service_factory.h" +#include "brave/browser/ui/brave_actions/brave_action_icon_with_badge_image_source.h" +#include "brave/components/brave_rewards/browser/rewards_service.h" +#include "brave/components/brave_rewards/common/pref_names.h" +#include "brave/components/constants/webui_url_constants.h" +#include "brave/components/l10n/common/locale_util.h" +#include "brave/grit/brave_generated_resources.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/chrome_layout_provider.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/location_bar/location_bar_view.h" +#include "chrome/browser/ui/views/toolbar/toolbar_action_view.h" +#include "components/grit/brave_components_strings.h" +#include "components/prefs/pref_service.h" +#include "extensions/common/constants.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/animation/ink_drop_impl.h" +#include "ui/views/controls/button/label_button_border.h" +#include "ui/views/controls/button/menu_button_controller.h" +#include "ui/views/controls/highlight_path_generator.h" +#include "ui/views/view_class_properties.h" + +namespace { + +using brave_rewards::RewardsNotificationService; +using brave_rewards::RewardsPanelServiceFactory; +using brave_rewards::RewardsServiceFactory; +using brave_rewards::RewardsTabHelper; + +constexpr SkColor kIconColor = SK_ColorBLACK; +constexpr SkColor kBadgeTextColor = SK_ColorWHITE; +constexpr SkColor kBadgeNotificationBG = SkColorSetRGB(0xfb, 0x54, 0x2b); +constexpr SkColor kBadgeVerifiedBG = SkColorSetRGB(0x4c, 0x54, 0xd2); +const char kVerifiedCheck[] = "\u2713"; + +// TODO(zenparsing): Should this be shared for all action buttons? +class ButtonHighlightPathGenerator : public views::HighlightPathGenerator { + public: + // views::HighlightPathGenerator: + SkPath GetHighlightPath(const views::View* view) override { + // Set the highlight path for the toolbar button, making it inset so that + // the badge can show outside it in the fake margin on the right that we are + // creating. + DCHECK(view); + gfx::Rect rect(view->GetPreferredSize()); + rect.Inset(gfx::Insets::TLBR(0, 0, 0, kBraveActionRightMargin)); + + auto* layout_provider = ChromeLayoutProvider::Get(); + DCHECK(layout_provider); + + int radius = layout_provider->GetCornerRadiusMetric( + views::Emphasis::kMaximum, rect.size()); + + SkPath path; + path.addRoundRect(gfx::RectToSkRect(rect), radius, radius); + return path; + } +}; + +const ui::ColorProvider* GetColorProviderForWebContents( + base::WeakPtr web_contents) { + if (web_contents) { + return &web_contents->GetColorProvider(); + } + + return ui::ColorProviderManager::Get().GetColorProviderFor( + ui::NativeTheme::GetInstanceForNativeUi()->GetColorProviderKey(nullptr)); +} + +// Provides the context menu for the Rewards button. +class RewardsActionMenuModel : public ui::SimpleMenuModel, + public ui::SimpleMenuModel::Delegate { + public: + explicit RewardsActionMenuModel(PrefService* prefs) + : SimpleMenuModel(this), prefs_(prefs) { + Build(); + } + + ~RewardsActionMenuModel() override = default; + RewardsActionMenuModel(const RewardsActionMenuModel&) = delete; + RewardsActionMenuModel& operator=(const RewardsActionMenuModel&) = delete; + + private: + enum ContextMenuCommand { kHideBraveRewardsIcon }; + + // ui::SimpleMenuModel::Delegate override: + void ExecuteCommand(int command_id, int event_flags) override { + if (command_id == kHideBraveRewardsIcon) { + prefs_->SetBoolean(brave_rewards::prefs::kShowButton, false); + } + } + + void Build() { + AddItemWithStringId(kHideBraveRewardsIcon, + IDS_HIDE_BRAVE_REWARDS_ACTION_ICON); + } + + raw_ptr prefs_ = nullptr; +}; + +} // namespace + +BraveRewardsActionView::BraveRewardsActionView(Browser* browser) + : ToolbarButton( + base::BindRepeating(&BraveRewardsActionView::OnButtonPressed, + base::Unretained(this)), + std::make_unique( + browser->profile()->GetPrefs()), + nullptr, + false), + browser_(browser), + bubble_manager_(this, + browser_->profile(), + GURL(kBraveRewardsPanelURL), + IDS_BRAVE_UI_BRAVE_REWARDS) { + DCHECK(browser_); + + SetButtonController(std::make_unique( + this, + base::BindRepeating(&BraveRewardsActionView::OnButtonPressed, + base::Unretained(this)), + std::make_unique(this))); + + views::HighlightPathGenerator::Install( + this, std::make_unique()); + + // The highlight opacity set by |ToolbarButton| is different that the default + // highlight opacity used by the other buttons in the actions container. Unset + // the highlight opacity to match. + views::InkDrop::Get(this)->SetHighlightOpacity({}); + + SetHorizontalAlignment(gfx::ALIGN_CENTER); + SetLayoutInsets(gfx::Insets(0)); + SetAccessibleName( + brave_l10n::GetLocalizedResourceUTF16String(IDS_BRAVE_UI_BRAVE_REWARDS)); + + auto* profile = browser_->profile(); + + pref_change_registrar_.Init(profile->GetPrefs()); + pref_change_registrar_.Add( + brave_rewards::prefs::kBadgeText, + base::BindRepeating(&BraveRewardsActionView::OnPreferencesChanged, + base::Unretained(this))); + + browser_->tab_strip_model()->AddObserver(this); + + if (auto* rewards_service = GetRewardsService()) { + rewards_service_observation_.Observe(rewards_service); + } + + if (auto* notification_service = GetNotificationService()) { + notification_service_observation_.Observe(notification_service); + } + + if (auto* service = RewardsPanelServiceFactory::GetForProfile(profile)) { + panel_service_ = service; + panel_observation_.Observe(service); + } + + UpdateTabHelper(GetActiveWebContents()); +} + +BraveRewardsActionView::~BraveRewardsActionView() = default; + +void BraveRewardsActionView::Update() { + gfx::Size preferred_size = GetPreferredSize(); + auto* web_contents = GetActiveWebContents(); + auto weak_contents = web_contents ? web_contents->GetWeakPtr() + : base::WeakPtr(); + + auto image_source = std::make_unique( + preferred_size, + base::BindRepeating(GetColorProviderForWebContents, weak_contents)); + + image_source->SetIcon(gfx::Image(GetRewardsIcon())); + + auto [text, background_color] = GetBadgeTextAndBackground(); + image_source->SetBadge(std::make_unique( + text, kBadgeTextColor, background_color)); + + SetImage(views::Button::STATE_NORMAL, + gfx::ImageSkia(std::move(image_source), preferred_size)); +} + +void BraveRewardsActionView::ClosePanelForTesting() { + if (IsPanelOpen()) { + ToggleRewardsPanel(); + } +} + +gfx::Rect BraveRewardsActionView::GetAnchorBoundsInScreen() const { + if (!GetVisible()) { + // If the button is currently hidden, then anchor the bubble to the + // location bar instead. + // TODO(zenparsing): Should we anchor it to the action view instead? + auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser_); + DCHECK(browser_view); + return browser_view->GetLocationBarView()->GetAnchorBoundsInScreen(); + } + return ToolbarButton::GetAnchorBoundsInScreen(); +} + +std::unique_ptr +BraveRewardsActionView::CreateDefaultBorder() const { + auto border = ToolbarButton::CreateDefaultBorder(); + border->set_insets(gfx::Insets::TLBR(0, 0, 0, 0)); + return border; +} + +void BraveRewardsActionView::OnWidgetDestroying(views::Widget* widget) { + DCHECK(bubble_observation_.IsObservingSource(widget)); + bubble_observation_.Reset(); + if (panel_service_) { + panel_service_->NotifyPanelClosed(browser_); + } +} + +void BraveRewardsActionView::OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) { + if (selection.active_tab_changed()) { + UpdateTabHelper(selection.new_contents); + } +} + +void BraveRewardsActionView::OnPublisherForTabUpdated( + const std::string& publisher_id) { + // TODO(zenparsing): Consider an LRUCache for this initialization. + publisher_registered_ = {publisher_id, false}; + bool status_updating = UpdatePublisherStatus(); + if (!status_updating) { + Update(); + } +} + +void BraveRewardsActionView::OnRewardsPanelRequested( + Browser* browser, + const brave_rewards::mojom::RewardsPanelArgs& args) { + // If the panel is already open, then assume that the corresponding WebUI + // handler will be listening for this event and take the panel arguments. + if (browser == browser_ && !IsPanelOpen()) { + ToggleRewardsPanel(); + } +} + +void BraveRewardsActionView::OnPublisherRegistryUpdated() { + UpdatePublisherStatus(); +} + +void BraveRewardsActionView::OnPublisherUpdated( + const std::string& publisher_id) { + if (publisher_id == std::get(publisher_registered_)) { + UpdatePublisherStatus(); + } +} + +void BraveRewardsActionView::OnNotificationAdded( + RewardsNotificationService* service, + const RewardsNotificationService::RewardsNotification& notification) { + Update(); +} + +void BraveRewardsActionView::OnNotificationDeleted( + RewardsNotificationService* service, + const RewardsNotificationService::RewardsNotification& notification) { + Update(); +} + +void BraveRewardsActionView::OnButtonPressed() { + if (IsPanelOpen()) { + ToggleRewardsPanel(); + return; + } + + // Even though the bubble is hidden, the Rewards panel may be running in the + // background for cached access and listening to the panel service. In order + // to notify the panel, request that the panel be opened through the panel + // service, rather than opening the bubble directly. + if (panel_service_) { + panel_service_->OpenRewardsPanel(); + } +} + +void BraveRewardsActionView::OnPreferencesChanged(const std::string& key) { + Update(); +} + +content::WebContents* BraveRewardsActionView::GetActiveWebContents() { + return browser_->tab_strip_model()->GetActiveWebContents(); +} + +brave_rewards::RewardsService* BraveRewardsActionView::GetRewardsService() { + return RewardsServiceFactory::GetForProfile(browser_->profile()); +} + +brave_rewards::RewardsNotificationService* +BraveRewardsActionView::GetNotificationService() { + if (auto* rewards_service = GetRewardsService()) { + return rewards_service->GetNotificationService(); + } + return nullptr; +} + +bool BraveRewardsActionView::IsPanelOpen() { + return bubble_observation_.IsObserving(); +} + +void BraveRewardsActionView::ToggleRewardsPanel() { + if (IsPanelOpen()) { + bubble_manager_.CloseBubble(); + return; + } + + // Clear the default-on-start badge text when the user opens the panel. + auto* prefs = browser_->profile()->GetPrefs(); + prefs->SetString(brave_rewards::prefs::kBadgeText, ""); + + bubble_manager_.ShowBubble(); + + DCHECK(!bubble_observation_.IsObserving()); + bubble_observation_.Observe(bubble_manager_.GetBubbleWidget()); +} + +gfx::ImageSkia BraveRewardsActionView::GetRewardsIcon() { + // Since the BAT icon has color the actual color value here is not relevant, + // but |CreateVectorIcon| requires one. + return gfx::CreateVectorIcon(kBatIcon, kBraveActionGraphicSize, kIconColor); +} + +std::pair +BraveRewardsActionView::GetBadgeTextAndBackground() { + // 1. Display the default-on-start Rewards badge text, if specified. + std::string text_pref = browser_->profile()->GetPrefs()->GetString( + brave_rewards::prefs::kBadgeText); + if (!text_pref.empty()) { + return {text_pref, kBadgeNotificationBG}; + } + + // 2. Display the number of current notifications, if non-zero. + size_t notifications = GetRewardsNotificationCount(); + if (notifications > 0) { + std::string text = + notifications > 99 ? "99+" : base::NumberToString(notifications); + + return {text, kBadgeNotificationBG}; + } + + // 3. Display a verified checkmark for verified publishers. + if (std::get(publisher_registered_)) { + return {kVerifiedCheck, kBadgeVerifiedBG}; + } + + return {"", kBadgeNotificationBG}; +} + +size_t BraveRewardsActionView::GetRewardsNotificationCount() { + auto* service = GetNotificationService(); + return service ? service->GetAllNotifications().size() : 0; +} + +bool BraveRewardsActionView::UpdatePublisherStatus() { + std::string& publisher_id = std::get(publisher_registered_); + if (publisher_id.empty()) { + return false; + } + + auto* rewards_service = GetRewardsService(); + if (!rewards_service || !rewards_service->IsRewardsEnabled()) { + return false; + } + + // TODO(zenparsing): When rewards is enabled, should we automatically check + // this again? We can use |OnRewardsInitialized|, but it doesn't really work + // because the publisher index has not been fully populated yet. We may need + // to introduce an event that is triggered when the publisher index has been + // updated. + // TODO(zenparsing): We also need to update the badge when the user manually + // refreshes the publisher status from the Rewards panel UI. It looks like + // we'll need another event for that. + rewards_service->IsPublisherRegistered( + publisher_id, + base::BindOnce(&BraveRewardsActionView::IsPublisherRegisteredCallback, + weak_factory_.GetWeakPtr(), publisher_id)); + + return true; +} + +void BraveRewardsActionView::IsPublisherRegisteredCallback( + const std::string& publisher_id, + bool is_registered) { + if (publisher_id == std::get(publisher_registered_)) { + publisher_registered_.second = is_registered; + Update(); + } +} + +void BraveRewardsActionView::UpdateTabHelper( + content::WebContents* web_contents) { + tab_helper_ = nullptr; + if (tab_helper_observation_.IsObserving()) { + tab_helper_observation_.Reset(); + } + + if (web_contents) { + if (auto* helper = RewardsTabHelper::FromWebContents(web_contents)) { + tab_helper_ = helper; + tab_helper_observation_.Observe(helper); + } + } + + OnPublisherForTabUpdated(tab_helper_ ? tab_helper_->GetPublisherIdForTab() + : ""); +} diff --git a/browser/ui/views/brave_actions/brave_rewards_action_view.h b/browser/ui/views/brave_actions/brave_rewards_action_view.h new file mode 100644 index 000000000000..b3b35af64034 --- /dev/null +++ b/browser/ui/views/brave_actions/brave_rewards_action_view.h @@ -0,0 +1,135 @@ +// Copyright (c) 2022 The Brave Authors. All rights reserved. +// 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/. + +#ifndef BRAVE_BROWSER_UI_VIEWS_BRAVE_ACTIONS_BRAVE_REWARDS_ACTION_VIEW_H_ +#define BRAVE_BROWSER_UI_VIEWS_BRAVE_ACTIONS_BRAVE_REWARDS_ACTION_VIEW_H_ + +#include +#include +#include + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "brave/browser/brave_rewards/rewards_tab_helper.h" +#include "brave/browser/ui/webui/brave_rewards/rewards_panel_ui.h" +#include "brave/components/brave_rewards/browser/rewards_notification_service.h" +#include "brave/components/brave_rewards/browser/rewards_notification_service_observer.h" +#include "brave/components/brave_rewards/browser/rewards_service.h" +#include "brave/components/brave_rewards/browser/rewards_service_observer.h" +#include "chrome/browser/ui/tabs/tab_strip_model_observer.h" +#include "chrome/browser/ui/views/bubble/webui_bubble_manager.h" +#include "chrome/browser/ui/views/toolbar/toolbar_button.h" +#include "components/prefs/pref_change_registrar.h" +#include "ui/gfx/geometry/skia_conversions.h" +#include "ui/views/widget/widget_observer.h" + +class Profile; +class TabStripModel; + +// A button that lives in the actions container and opens the Rewards panel. The +// button has an associated context menu and can be hidden by user settings. +class BraveRewardsActionView + : public ToolbarButton, + public views::WidgetObserver, + public TabStripModelObserver, + public brave_rewards::RewardsTabHelper::Observer, + public brave_rewards::RewardsPanelService::Observer, + public brave_rewards::RewardsServiceObserver, + public brave_rewards::RewardsNotificationServiceObserver { + public: + explicit BraveRewardsActionView(Browser* browser); + + ~BraveRewardsActionView() override; + + BraveRewardsActionView(const BraveRewardsActionView&) = delete; + BraveRewardsActionView& operator=(const BraveRewardsActionView&) = delete; + + void Update(); + + void ClosePanelForTesting(); + + // views::View: + gfx::Rect GetAnchorBoundsInScreen() const override; + + // views::LabelButton: + std::unique_ptr CreateDefaultBorder() + const override; + + // views::WidgetObserver: + void OnWidgetDestroying(views::Widget* widget) override; + + // TabStripModelObserver: + void OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) override; + + // brave_rewards::RewardsTabHelper::Observer: + void OnPublisherForTabUpdated(const std::string& publisher_id) override; + + // brave_rewards::RewardsPanelService::Observer: + void OnRewardsPanelRequested( + Browser* browser, + const brave_rewards::mojom::RewardsPanelArgs& args) override; + + // brave_rewards::RewardsServiceObserver: + void OnPublisherRegistryUpdated() override; + + void OnPublisherUpdated(const std::string& publisher_id) override; + + // brave_rewards::RewardsNotificationServiceObserver: + void OnNotificationAdded( + brave_rewards::RewardsNotificationService* service, + const brave_rewards::RewardsNotificationService::RewardsNotification& + notification) override; + + void OnNotificationDeleted( + brave_rewards::RewardsNotificationService* service, + const brave_rewards::RewardsNotificationService::RewardsNotification& + notification) override; + + private: + void OnButtonPressed(); + void OnPreferencesChanged(const std::string& key); + content::WebContents* GetActiveWebContents(); + brave_rewards::RewardsService* GetRewardsService(); + brave_rewards::RewardsNotificationService* GetNotificationService(); + bool IsPanelOpen(); + void ToggleRewardsPanel(); + gfx::ImageSkia GetRewardsIcon(); + std::pair GetBadgeTextAndBackground(); + size_t GetRewardsNotificationCount(); + bool UpdatePublisherStatus(); + void IsPublisherRegisteredCallback(const std::string& publisher_id, + bool is_registered); + void UpdateTabHelper(content::WebContents* web_contents); + + using WidgetObservation = + base::ScopedObservation; + + using RewardsObservation = + base::ScopedObservation; + + using NotificationServiceObservation = base::ScopedObservation< + brave_rewards::RewardsNotificationService, + brave_rewards::RewardsNotificationServiceObserver>; + + raw_ptr browser_ = nullptr; + raw_ptr panel_service_ = nullptr; + raw_ptr tab_helper_ = nullptr; + WebUIBubbleManagerT bubble_manager_; + PrefChangeRegistrar pref_change_registrar_; + std::pair publisher_registered_; + brave_rewards::RewardsTabHelper::Observation tab_helper_observation_{this}; + brave_rewards::RewardsPanelService::Observation panel_observation_{this}; + WidgetObservation bubble_observation_{this}; + RewardsObservation rewards_service_observation_{this}; + NotificationServiceObservation notification_service_observation_{this}; + base::WeakPtrFactory weak_factory_{this}; +}; + +#endif // BRAVE_BROWSER_UI_VIEWS_BRAVE_ACTIONS_BRAVE_REWARDS_ACTION_VIEW_H_ diff --git a/browser/ui/webui/brave_rewards/rewards_panel_ui.cc b/browser/ui/webui/brave_rewards/rewards_panel_ui.cc new file mode 100644 index 000000000000..5c48f575388b --- /dev/null +++ b/browser/ui/webui/brave_rewards/rewards_panel_ui.cc @@ -0,0 +1,367 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * 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/. */ + +#include "brave/browser/ui/webui/brave_rewards/rewards_panel_ui.h" + +#include +#include +#include + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "brave/browser/brave_rewards/rewards_panel_service_factory.h" +#include "brave/browser/brave_rewards/rewards_service_factory.h" +#include "brave/browser/brave_rewards/rewards_tab_helper.h" +#include "brave/components/brave_adaptive_captcha/server_util.h" +#include "brave/components/brave_rewards/browser/rewards_service.h" +#include "brave/components/brave_rewards/resources/grit/brave_rewards_panel_generated_map.h" +#include "brave/components/brave_rewards/resources/grit/brave_rewards_resources.h" +#include "brave/components/constants/webui_url_constants.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/webui/favicon_source.h" +#include "chrome/browser/ui/webui/webui_util.h" +#include "components/favicon_base/favicon_url_parser.h" +#include "components/grit/brave_components_resources.h" +#include "components/grit/brave_components_strings.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_ui.h" +#include "services/network/public/mojom/content_security_policy.mojom.h" + +namespace { + +/* + +Functional issues: + +- The button was unresponsive for a while during testing for an unkown reason, + perhaps because the front-end was not calling "showUI". We need to know why + that happened. + +Other issues: + +- Tests for new code. +- We are no longer showing the button while the panel is open, due to anchoring + difficulties. Is that OK? +- Better method name for GetPublisherActivityFromUrl? +- Import locale strings from messages.json files? +- Verify that using RewardsInitialized in tab helper is correct. Do we even need + to listen for that? Can we do that on tab reloads instead? +- Better (decent) debugging for panel "spinner" stalls and the Rewards panel in + general. +- Rewards button context menu has different padding and corner radius + +*/ + +static constexpr webui::LocalizedString kStrings[] = { + {"attention", IDS_REWARDS_PANEL_ATTENTION}, + {"braveTalkBraveRewardsDescription", + IDS_REWARDS_BRAVE_TALK_BRAVE_REWARDS_DESCRIPTION}, + {"braveTalkCanStartFreeCall", IDS_REWARDS_BRAVE_TALK_CAN_START_FREE_CALL}, + {"braveTalkClickAnywhereToBraveTalk", + IDS_REWARDS_BRAVE_TALK_CLICK_ANYWHERE_TO_BRAVE_TALK}, + {"braveTalkOptInRewardsTerms", IDS_REWARDS_BRAVE_TALK_OPT_IN_REWARDS_TERMS}, + {"braveTalkPrivateAdsDescription", + IDS_REWARDS_BRAVE_TALK_PRIVATE_ADS_DESCRIPTION}, + {"braveTalkTurnOnPrivateAds", IDS_REWARDS_BRAVE_TALK_TURN_ON_PRIVATE_ADS}, + {"braveTalkTurnOnPrivateAdsToStartCall", + IDS_REWARDS_BRAVE_TALK_TURN_ON_PRIVATE_ADS_TO_START_CALL}, + {"braveTalkTurnOnRewards", IDS_REWARDS_BRAVE_TALK_TURN_ON_REWARDS}, + {"braveTalkTurnOnRewardsToStartCall", + IDS_REWARDS_BRAVE_TALK_TURN_ON_REWARDS_TO_START_CALL}, + {"braveTalkWantLearnMore", IDS_REWARDS_BRAVE_TALK_WANT_LEARN_MORE}, + {"cancel", IDS_REWARDS_PANEL_CANCEL}, + {"captchaContactSupport", IDS_REWARDS_CAPTCHA_CONTACT_SUPPORT}, + {"captchaDismiss", IDS_REWARDS_CAPTCHA_DISMISS}, + {"captchaMaxAttemptsExceededText", + IDS_REWARDS_CAPTCHA_MAX_ATTEMPTS_EXCEEDED_TEXT}, + {"captchaMaxAttemptsExceededTitle", + IDS_REWARDS_CAPTCHA_MAX_ATTEMPTS_EXCEEDED_TITLE}, + {"captchaSolvedText", IDS_REWARDS_CAPTCHA_SOLVED_TEXT}, + {"captchaSolvedTitle", IDS_REWARDS_CAPTCHA_SOLVED_TITLE}, + {"changeAmount", IDS_REWARDS_PANEL_CHANGE_AMOUNT}, + {"grantCaptchaAmountAds", IDS_REWARDS_GRANT_CAPTCHA_AMOUNT_ADS}, + {"grantCaptchaAmountUGP", IDS_REWARDS_GRANT_CAPTCHA_AMOUNT_UGP}, + {"grantCaptchaErrorText", IDS_REWARDS_GRANT_CAPTCHA_ERROR_TEXT}, + {"grantCaptchaErrorTitle", IDS_REWARDS_GRANT_CAPTCHA_ERROR_TITLE}, + {"grantCaptchaExpiration", IDS_REWARDS_GRANT_CAPTCHA_EXPIRATION}, + {"grantCaptchaFailedTitle", IDS_REWARDS_GRANT_CAPTCHA_FAILED_TITLE}, + {"grantCaptchaHint", IDS_REWARDS_GRANT_CAPTCHA_HINT}, + {"grantCaptchaPassedTextAds", IDS_REWARDS_GRANT_CAPTCHA_PASSED_TEXT_ADS}, + {"grantCaptchaPassedTextUGP", IDS_REWARDS_GRANT_CAPTCHA_PASSED_TEXT_UGP}, + {"grantCaptchaPassedTitleAds", IDS_REWARDS_GRANT_CAPTCHA_PASSED_TITLE_ADS}, + {"grantCaptchaPassedTitleUGP", IDS_REWARDS_GRANT_CAPTCHA_PASSED_TITLE_UGP}, + {"grantCaptchaTitle", IDS_REWARDS_GRANT_CAPTCHA_TITLE}, + {"includeInAutoContribute", IDS_REWARDS_PANEL_INCLUDE_IN_AUTO_CONTRIBUTE}, + {"monthlyTip", IDS_REWARDS_PANEL_MONTHLY_TIP}, + {"notificationAddFunds", IDS_REWARDS_NOTIFICATION_ADD_FUNDS}, + {"notificationAddFundsText", IDS_REWARDS_NOTIFICATION_ADD_FUNDS_TEXT}, + {"notificationAddFundsTitle", IDS_REWARDS_NOTIFICATION_ADD_FUNDS_TITLE}, + {"notificationAdGrantAmount", IDS_REWARDS_NOTIFICATION_AD_GRANT_AMOUNT}, + {"notificationAdGrantTitle", IDS_REWARDS_NOTIFICATION_AD_GRANT_TITLE}, + {"notificationAutoContributeCompletedText", + IDS_REWARDS_NOTIFICATION_AUTO_CONTRIBUTE_COMPLETED_TEXT}, + {"notificationAutoContributeCompletedTitle", + IDS_REWARDS_NOTIFICATION_AUTO_CONTRIBUTE_COMPLETED_TITLE}, + {"notificationBackupWalletAction", + IDS_REWARDS_NOTIFICATION_BACKUP_WALLET_ACTION}, + {"notificationBackupWalletText", + IDS_REWARDS_NOTIFICATION_BACKUP_WALLET_TEXT}, + {"notificationBackupWalletTitle", + IDS_REWARDS_NOTIFICATION_BACKUP_WALLET_TITLE}, + {"notificationClaimRewards", IDS_REWARDS_NOTIFICATION_CLAIM_REWARDS}, + {"notificationClaimTokens", IDS_REWARDS_NOTIFICATION_CLAIM_TOKENS}, + {"notificationGrantDaysRemaining", + IDS_REWARDS_NOTIFICATION_GRANT_DAYS_REMAINING}, + {"notificationInsufficientFundsText", + IDS_REWARDS_NOTIFICATION_INSUFFICIENT_FUNDS_TEXT}, + {"notificationMonthlyContributionFailedText", + IDS_REWARDS_NOTIFICATION_MONTHLY_CONTRIBUTION_FAILED_TEXT}, + {"notificationMonthlyContributionFailedTitle", + IDS_REWARDS_NOTIFICATION_MONTHLY_CONTRIBUTION_FAILED_TITLE}, + {"notificationMonthlyTipCompletedText", + IDS_REWARDS_NOTIFICATION_MONTHLY_TIP_COMPLETED_TEXT}, + {"notificationMonthlyTipCompletedTitle", + IDS_REWARDS_NOTIFICATION_MONTHLY_TIP_COMPLETED_TITLE}, + {"notificationPendingTipFailedText", + IDS_REWARDS_NOTIFICATION_PENDING_TIP_FAILED_TEXT}, + {"notificationPendingTipFailedTitle", + IDS_REWARDS_NOTIFICATION_PENDING_TIP_FAILED_TITLE}, + {"notificationPublisherVerifiedText", + IDS_REWARDS_NOTIFICATION_PUBLISHER_VERIFIED_TEXT}, + {"notificationPublisherVerifiedTitle", + IDS_REWARDS_NOTIFICATION_PUBLISHER_VERIFIED_TITLE}, + {"notificationReconnect", IDS_REWARDS_NOTIFICATION_RECONNECT}, + {"notificationWalletDisconnectedAction", + IDS_REWARDS_NOTIFICATION_WALLET_DISCONNECTED_ACTION}, + {"notificationWalletDisconnectedText", + IDS_REWARDS_NOTIFICATION_WALLET_DISCONNECTED_TEXT}, + {"notificationWalletDisconnectedTitle", + IDS_REWARDS_NOTIFICATION_WALLET_DISCONNECTED_TITLE}, + {"notificationTokenGrantTitle", IDS_REWARDS_NOTIFICATION_TOKEN_GRANT_TITLE}, + {"notificationWalletVerifiedText", + IDS_REWARDS_NOTIFICATION_WALLET_VERIFIED_TEXT}, + {"notificationWalletVerifiedTitle", + IDS_REWARDS_NOTIFICATION_WALLET_VERIFIED_TITLE}, + {"ok", IDS_REWARDS_PANEL_OK}, + {"onboardingEarnHeader", IDS_BRAVE_REWARDS_ONBOARDING_EARN_HEADER}, + {"onboardingEarnText", IDS_BRAVE_REWARDS_ONBOARDING_EARN_TEXT}, + {"onboardingPanelAcHeader", IDS_BRAVE_REWARDS_ONBOARDING_PANEL_AC_HEADER}, + {"onboardingPanelAcText", IDS_BRAVE_REWARDS_ONBOARDING_PANEL_AC_TEXT}, + {"onboardingPanelAdsHeader", IDS_BRAVE_REWARDS_ONBOARDING_PANEL_ADS_HEADER}, + {"onboardingPanelAdsText", IDS_BRAVE_REWARDS_ONBOARDING_PANEL_ADS_TEXT}, + {"onboardingPanelBitflyerLearnMore", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_BITFLYER_LEARN_MORE}, + {"onboardingPanelBitflyerNote", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_BITFLYER_NOTE}, + {"onboardingPanelBitflyerText", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_BITFLYER_TEXT}, + {"onboardingPanelCompleteHeader", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_COMPLETE_HEADER}, + {"onboardingPanelCompleteText", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_COMPLETE_TEXT}, + {"onboardingPanelRedeemHeader", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_REDEEM_HEADER}, + {"onboardingPanelRedeemText", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_REDEEM_TEXT}, + {"onboardingPanelScheduleHeader", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_SCHEDULE_HEADER}, + {"onboardingPanelScheduleText", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_SCHEDULE_TEXT}, + {"onboardingPanelSetupHeader", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_SETUP_HEADER}, + {"onboardingPanelSetupText", IDS_BRAVE_REWARDS_ONBOARDING_PANEL_SETUP_TEXT}, + {"onboardingPanelTippingHeader", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_TIPPING_HEADER}, + {"onboardingPanelTippingText", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_TIPPING_TEXT}, + {"onboardingPanelVerifyHeader", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_VERIFY_HEADER}, + {"onboardingPanelVerifyLater", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_VERIFY_LATER}, + {"onboardingPanelVerifyNow", IDS_BRAVE_REWARDS_ONBOARDING_PANEL_VERIFY_NOW}, + {"onboardingPanelVerifySubtext", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_VERIFY_SUBTEXT}, + {"onboardingPanelWelcomeHeader", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_WELCOME_HEADER}, + {"onboardingPanelWelcomeText", + IDS_BRAVE_REWARDS_ONBOARDING_PANEL_WELCOME_TEXT}, + {"onboardingSetupAdsHeader", IDS_BRAVE_REWARDS_ONBOARDING_SETUP_ADS_HEADER}, + {"onboardingSetupAdsSubheader", + IDS_BRAVE_REWARDS_ONBOARDING_SETUP_ADS_SUBHEADER}, + {"onboardingSetupContributeHeader", + IDS_BRAVE_REWARDS_ONBOARDING_SETUP_CONTRIBUTE_HEADER}, + {"onboardingSetupContributeSubheader", + IDS_BRAVE_REWARDS_ONBOARDING_SETUP_CONTRIBUTE_SUBHEADER}, + {"onboardingStartUsingRewards", + IDS_BRAVE_REWARDS_ONBOARDING_START_USING_REWARDS}, + {"onboardingTakeTour", IDS_BRAVE_REWARDS_ONBOARDING_TAKE_TOUR}, + {"onboardingTerms", IDS_BRAVE_REWARDS_ONBOARDING_TERMS}, + {"onboardingTourBack", IDS_BRAVE_REWARDS_ONBOARDING_TOUR_BACK}, + {"onboardingTourBegin", IDS_BRAVE_REWARDS_ONBOARDING_TOUR_BEGIN}, + {"onboardingTourContinue", IDS_BRAVE_REWARDS_ONBOARDING_TOUR_CONTINUE}, + {"onboardingTourDone", IDS_BRAVE_REWARDS_ONBOARDING_TOUR_DONE}, + {"onboardingTourSkip", IDS_BRAVE_REWARDS_ONBOARDING_TOUR_SKIP}, + {"onboardingTourSkipForNow", + IDS_BRAVE_REWARDS_ONBOARDING_TOUR_SKIP_FOR_NOW}, + {"pendingTipText", IDS_REWARDS_PANEL_PENDING_TIP_TEXT}, + {"pendingTipTitle", IDS_REWARDS_PANEL_PENDING_TIP_TITLE}, + {"pendingTipTitleRegistered", + IDS_REWARDS_PANEL_PENDING_TIP_TITLE_REGISTERED}, + {"platformPublisherTitle", IDS_REWARDS_PANEL_PLATFORM_PUBLISHER_TITLE}, + {"refreshStatus", IDS_REWARDS_PANEL_REFRESH_STATUS}, + {"rewardsLogInToSeeBalance", IDS_REWARDS_LOG_IN_TO_SEE_BALANCE}, + {"rewardsPaymentCheckStatus", IDS_REWARDS_PAYMENT_CHECK_STATUS}, + {"rewardsPaymentCompleted", IDS_REWARDS_PAYMENT_COMPLETED}, + {"rewardsPaymentPending", IDS_REWARDS_PAYMENT_PENDING}, + {"rewardsPaymentProcessing", IDS_REWARDS_PAYMENT_PROCESSING}, + {"sendTip", IDS_REWARDS_PANEL_SEND_TIP}, + {"set", IDS_REWARDS_PANEL_SET}, + {"summary", IDS_REWARDS_PANEL_SUMMARY}, + {"tip", IDS_REWARDS_PANEL_TIP}, + {"unverifiedCreator", IDS_REWARDS_PANEL_UNVERIFIED_CREATOR}, + {"verifiedCreator", IDS_REWARDS_PANEL_VERIFIED_CREATOR}, + {"walletAccountLink", IDS_REWARDS_WALLET_ACCOUNT_LINK}, + {"walletAddFunds", IDS_REWARDS_WALLET_ADD_FUNDS}, + {"walletAutoContribute", IDS_REWARDS_WALLET_AUTO_CONTRIBUTE}, + {"walletDisconnected", IDS_REWARDS_WALLET_DISCONNECTED}, + {"walletDisconnectLink", IDS_REWARDS_WALLET_DISCONNECT_LINK}, + {"walletEstimatedEarnings", IDS_REWARDS_WALLET_ESTIMATED_EARNINGS}, + {"walletLogIntoYourAccount", IDS_REWARDS_WALLET_LOG_INTO_YOUR_ACCOUNT}, + {"walletMonthlyTips", IDS_REWARDS_WALLET_MONTHLY_TIPS}, + {"walletOneTimeTips", IDS_REWARDS_WALLET_ONE_TIME_TIPS}, + {"walletPending", IDS_REWARDS_WALLET_PENDING}, + {"walletPendingText", IDS_REWARDS_WALLET_PENDING_TEXT}, + {"walletRewardsFromAds", IDS_REWARDS_WALLET_REWARDS_FROM_ADS}, + {"walletRewardsSummary", IDS_REWARDS_WALLET_REWARDS_SUMMARY}, + {"walletUnverified", IDS_REWARDS_WALLET_UNVERIFIED}, + {"walletVerified", IDS_REWARDS_WALLET_VERIFIED}, + {"walletYourBalance", IDS_REWARDS_WALLET_YOUR_BALANCE}}; + +using brave_rewards::RewardsPanelService; +using brave_rewards::RewardsPanelServiceFactory; +using brave_rewards::RewardsServiceFactory; +using brave_rewards::RewardsTabHelper; + +class PanelHandlerImpl : public brave_rewards::mojom::PanelHandler, + public RewardsPanelService::Observer { + public: + PanelHandlerImpl( + mojo::PendingReceiver receiver, + mojo::PendingRemote ui_handler, + base::WeakPtr embedder, + Profile* profile) + : receiver_(this, std::move(receiver)), + ui_handler_(std::move(ui_handler)), + embedder_(embedder), + profile_(profile) { + DCHECK(embedder_); + DCHECK(profile_); + + if (auto* service = RewardsPanelServiceFactory::GetForProfile(profile_)) { + panel_service_observation_.Observe(service); + } + } + + PanelHandlerImpl(const PanelHandlerImpl&) = delete; + PanelHandlerImpl& operator=(const PanelHandlerImpl&) = delete; + + ~PanelHandlerImpl() override = default; + + // brave_rewards::mojom::PanelHandler: + void ShowUI() override { + if (embedder_) { + embedder_->ShowUI(); + } + } + + void CloseUI() override { + if (embedder_) { + embedder_->CloseUI(); + } + } + + void StartRewards(StartRewardsCallback callback) override { + auto* rewards_service = RewardsServiceFactory::GetForProfile(profile_); + if (!rewards_service) { + NOTREACHED(); + std::move(callback).Run(); + return; + } + + rewards_service->StartProcess(std::move(callback)); + } + + void GetRewardsPanelArgs(GetRewardsPanelArgsCallback callback) override { + auto* panel_service = RewardsPanelServiceFactory::GetForProfile(profile_); + std::move(callback).Run( + panel_service ? panel_service->panel_args().Clone() + : brave_rewards::mojom::RewardsPanelArgs::New()); + } + + // brave_rewards::RewardsPanelService::Observer: + void OnRewardsPanelRequested( + Browser* browser, + const brave_rewards::mojom::RewardsPanelArgs& args) override { + if (ui_handler_) { + ui_handler_->OnRewardsPanelRequested(args.Clone()); + } + } + + private: + mojo::Receiver receiver_; + mojo::Remote ui_handler_; + base::WeakPtr embedder_; + raw_ptr profile_ = nullptr; + RewardsPanelService::Observation panel_service_observation_{this}; +}; + +} // namespace + +RewardsPanelUI::RewardsPanelUI(content::WebUI* web_ui) + : MojoBubbleWebUIController(web_ui, true) { + auto* profile = Profile::FromWebUI(web_ui); + + auto* source = content::WebUIDataSource::Create(kBraveRewardsPanelHost); + source->AddLocalizedStrings(kStrings); + + webui::SetupWebUIDataSource(source, + base::make_span(kBraveRewardsPanelGenerated, + kBraveRewardsPanelGeneratedSize), + IDR_BRAVE_REWARDS_PANEL_HTML); + + // Adaptive captcha challenges are displayed in an iframe on the Rewards + // panel. In order to display these challenges we need to specify in CSP that + // frames can be loaded from the adaptive captcha server URL. + source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::ChildSrc, + "frame-src 'self' " + brave_adaptive_captcha::GetServerUrl("/") + ";"); + + content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(), + source); + + content::URLDataSource::Add( + profile, std::make_unique( + profile, chrome::FaviconUrlFormat::kFavicon2)); +} + +RewardsPanelUI::~RewardsPanelUI() = default; + +WEB_UI_CONTROLLER_TYPE_IMPL(RewardsPanelUI) + +void RewardsPanelUI::BindInterface( + mojo::PendingReceiver receiver) { + panel_factory_receiver_.reset(); + panel_factory_receiver_.Bind(std::move(receiver)); +} + +void RewardsPanelUI::CreatePanelHandler( + mojo::PendingReceiver panel_handler, + mojo::PendingRemote ui_handler) { + panel_handler_ = std::make_unique( + std::move(panel_handler), std::move(ui_handler), embedder(), + Profile::FromWebUI(web_ui())); +} diff --git a/browser/ui/webui/brave_rewards/rewards_panel_ui.h b/browser/ui/webui/brave_rewards/rewards_panel_ui.h new file mode 100644 index 000000000000..a4d8bccd9a35 --- /dev/null +++ b/browser/ui/webui/brave_rewards/rewards_panel_ui.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * 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/. */ + +#ifndef BRAVE_BROWSER_UI_WEBUI_BRAVE_REWARDS_REWARDS_PANEL_UI_H_ +#define BRAVE_BROWSER_UI_WEBUI_BRAVE_REWARDS_REWARDS_PANEL_UI_H_ + +#include + +#include "brave/components/brave_rewards/common/brave_rewards_panel.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "ui/webui/mojo_bubble_web_ui_controller.h" + +class RewardsPanelUI : public ui::MojoBubbleWebUIController, + public brave_rewards::mojom::PanelHandlerFactory { + public: + explicit RewardsPanelUI(content::WebUI* web_ui); + ~RewardsPanelUI() override; + + RewardsPanelUI(const RewardsPanelUI&) = delete; + RewardsPanelUI& operator=(const RewardsPanelUI&) = delete; + + void BindInterface(mojo::PendingReceiver receiver); + + private: + // brave_rewards::mojom::PanelHandlerFactory: + void CreatePanelHandler( + mojo::PendingReceiver panel_handler, + mojo::PendingRemote ui_handler) + override; + + std::unique_ptr panel_handler_; + mojo::Receiver panel_factory_receiver_{this}; + + WEB_UI_CONTROLLER_TYPE_DECL(); +}; + +#endif // BRAVE_BROWSER_UI_WEBUI_BRAVE_REWARDS_REWARDS_PANEL_UI_H_ diff --git a/browser/ui/webui/brave_web_ui_controller_factory.cc b/browser/ui/webui/brave_web_ui_controller_factory.cc index 0303588039dc..257e66efb073 100644 --- a/browser/ui/webui/brave_web_ui_controller_factory.cc +++ b/browser/ui/webui/brave_web_ui_controller_factory.cc @@ -12,6 +12,7 @@ #include "brave/browser/ethereum_remote_client/buildflags/buildflags.h" #include "brave/browser/ui/webui/brave_adblock_ui.h" #include "brave/browser/ui/webui/brave_federated/federated_internals_ui.h" +#include "brave/browser/ui/webui/brave_rewards/rewards_panel_ui.h" #include "brave/browser/ui/webui/brave_rewards_internals_ui.h" #include "brave/browser/ui/webui/brave_rewards_page_ui.h" #include "brave/browser/ui/webui/brave_tip_ui.h" @@ -107,6 +108,8 @@ WebUIController* NewWebUI(WebUI* web_ui, const GURL& url) { #if !BUILDFLAG(IS_ANDROID) } else if (host == kTipHost) { return new BraveTipUI(web_ui, url.host()); + } else if (host == kBraveRewardsPanelHost) { + return new RewardsPanelUI(web_ui); #endif // !BUILDFLAG(IS_ANDROID) #if !BUILDFLAG(IS_ANDROID) } else if (host == kWelcomeHost) { @@ -163,7 +166,10 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, const GURL& url) { url.host_piece() == kRewardsPageHost || url.host_piece() == kFederatedInternalsHost || url.host_piece() == kRewardsInternalsHost || +#if !BUILDFLAG(IS_ANDROID) url.host_piece() == kTipHost || + url.host_piece() == kBraveRewardsPanelHost || +#endif #if BUILDFLAG(ENABLE_TOR) url.host_piece() == kTorInternalsHost || #endif diff --git a/chromium_src/chrome/browser/extensions/chrome_component_extension_resource_manager.cc b/chromium_src/chrome/browser/extensions/chrome_component_extension_resource_manager.cc index 5dc85a9bdb1f..181362b32566 100644 --- a/chromium_src/chrome/browser/extensions/chrome_component_extension_resource_manager.cc +++ b/chromium_src/chrome/browser/extensions/chrome_component_extension_resource_manager.cc @@ -7,8 +7,8 @@ #include "brave/components/brave_extension/grit/brave_extension_generated_map.h" #include "brave/components/brave_extension/grit/brave_extension_resources_map.h" +#include "brave/components/brave_rewards/resources/extension/grit/brave_rewards_extension_panel_generated_map.h" #include "brave/components/brave_rewards/resources/extension/grit/brave_rewards_extension_resources_map.h" -#include "brave/components/brave_rewards/resources/extension/grit/brave_rewards_panel_generated_map.h" #include "brave/components/brave_webtorrent/browser/buildflags/buildflags.h" #if BUILDFLAG(ENABLE_BRAVE_WEBTORRENT) @@ -26,14 +26,14 @@ #define BRAVE_WEBTORRENT_RESOURCES #endif -#define BRAVE_CHROME_COMPONENT_EXTENSION_RESOURCE_MANAGER_DATA_DATA \ - AddComponentResourceEntries(kBraveExtension, kBraveExtensionSize); \ - AddComponentResourceEntries(kBraveExtensionGenerated, \ - kBraveExtensionGeneratedSize); \ - AddComponentResourceEntries(kBraveRewardsExtensionResources, \ - kBraveRewardsExtensionResourcesSize); \ - AddComponentResourceEntries(kBraveRewardsPanelGenerated, \ - kBraveRewardsPanelGeneratedSize); \ +#define BRAVE_CHROME_COMPONENT_EXTENSION_RESOURCE_MANAGER_DATA_DATA \ + AddComponentResourceEntries(kBraveExtension, kBraveExtensionSize); \ + AddComponentResourceEntries(kBraveExtensionGenerated, \ + kBraveExtensionGeneratedSize); \ + AddComponentResourceEntries(kBraveRewardsExtensionResources, \ + kBraveRewardsExtensionResourcesSize); \ + AddComponentResourceEntries(kBraveRewardsExtensionPanelGenerated, \ + kBraveRewardsExtensionPanelGeneratedSize); \ BRAVE_WEBTORRENT_RESOURCES #include "src/chrome/browser/extensions/chrome_component_extension_resource_manager.cc" diff --git a/common/extensions/api/_api_features.json b/common/extensions/api/_api_features.json index d084f35e3d8c..b327c0448c47 100644 --- a/common/extensions/api/_api_features.json +++ b/common/extensions/api/_api_features.json @@ -73,7 +73,8 @@ "chrome://tab-strip/*", "chrome://wallet-panel.top-chrome/*", "chrome://wallet/*", - "chrome://brave-shields.top-chrome/*" + "chrome://brave-shields.top-chrome/*", + "chrome://rewards-panel.top-chrome/*" ] }, { "channel": "stable", @@ -123,18 +124,25 @@ "matches": [ "chrome://newtab/*", "chrome://rewards/*", + "chrome://rewards-panel.top-chrome/*", "chrome://settings/*" ] } ], - "rewardsNotifications": { + "rewardsNotifications": [{ "channel": "stable", "dependencies": [], "contexts": ["blessed_extension"], "allowlist": [ "46E9817CBF915C0D1F6BCCF916C42CC666FF1D64" ] - }, + }, { + "channel": "stable", + "contexts": ["webui"], + "matches": [ + "chrome://rewards-panel.top-chrome/*" + ] + }], "braveTheme": [{ "channel": "stable", "contexts": ["blessed_extension"], @@ -153,7 +161,8 @@ "chrome://webcompat/*", "chrome://wallet/*", "chrome://wallet-panel.top-chrome/*", - "chrome://brave-shields.top-chrome/*" + "chrome://brave-shields.top-chrome/*", + "chrome://rewards-panel.top-chrome/*" ] }], "greaselion": [{ diff --git a/common/extensions/api/brave_rewards.json b/common/extensions/api/brave_rewards.json index 776a4bb021ef..401ee3ebe077 100644 --- a/common/extensions/api/brave_rewards.json +++ b/common/extensions/api/brave_rewards.json @@ -344,20 +344,25 @@ ] }, { - "name": "openBrowserActionUI", + "name": "openRewardsPanel", "type": "function", - "description": "Prompts the user with a popup UI", + "description": "Opens the Rewards panel in the active window.", + "parameters": [] + }, + { + "name": "showRewardsTour", + "type": "function", + "description": "Displays the Rewards tour in the Rewards panel.", + "parameters": [] + }, + { + "name": "showGrantCaptcha", + "type": "function", + "description": "Displays a grant captcha in the Rewards panel.", "parameters": [ { - "name": "windowId", - "type": "integer", - "minimum": 0, - "optional": true - }, - { - "name": "relativePath", - "type": "string", - "optional": true + "name": "grantId", + "type": "string" } ] }, @@ -436,6 +441,77 @@ } ] }, + { + "name": "setPublisherIdForTab", + "type": "function", + "description": "Sets the current publisher ID for the specified tab", + "parameters": [ + { + "name": "tabId", + "type": "integer" + }, + { + "name": "publisherId", + "type": "string" + } + ] + }, + { + "name": "getPublisherInfoForTab", + "type": "function", + "description": "Returns publisher information for the specified tab", + "parameters": [ + { + "name": "tabId", + "type": "integer" + }, + { + "name": "callback", + "type": "function", + "parameters": [ + { + "name": "publisher", + "type": "object", + "optional": true, + "properties": { + "percentage": { + "type": "integer", + "description": "publisher attention score" + }, + "excluded": { + "type": "boolean", + "description": "is site excluded from auto contribute" + }, + "provider": { + "type": "string", + "description": "provider (if media publisher) for this publisher" + }, + "favIconUrl": { + "type": "string", + "description": "publisher image url" + }, + "publisherKey": { + "type": "string", + "description": "publisher key, unique identifier" + }, + "name": { + "type": "string", + "description": "publisher name" + }, + "url": { + "type": "string", + "description": "url of the current tab" + }, + "status": { + "type": "integer", + "description": "publisher status" + } + } + } + ] + } + ] + }, { "name": "getPublisherPanelInfo", "type": "function", diff --git a/components/brave_extension/extension/brave_extension/background/greaselion.ts b/components/brave_extension/extension/brave_extension/background/greaselion.ts index f2ce146ab84a..d3c6ab8a4dee 100644 --- a/components/brave_extension/extension/brave_extension/background/greaselion.ts +++ b/components/brave_extension/extension/brave_extension/background/greaselion.ts @@ -283,6 +283,8 @@ const handleSavePublisherVisit = (tabId: number, mediaType: string, data: SavePu publisherKeysByTabId.set(tabId, data.publisherKey) + chrome.braveRewards.setPublisherIdForTab(tabId, data.publisherKey) + if (data.mediaKey && !publisherKeysByMediaKey.has(data.mediaKey)) { publisherKeysByMediaKey.set(data.mediaKey, data.publisherKey) } @@ -470,6 +472,7 @@ chrome.runtime.onMessageExternal.addListener( } switch (msg.type) { case 'GetPublisherPanelInfo': + // TODO(zenparsing): Unused? getPublisherPanelInfoByTabId(msg.tabId) break case 'SupportsGreaselion': diff --git a/components/brave_new_tab_ui/components/default/rewards/index.tsx b/components/brave_new_tab_ui/components/default/rewards/index.tsx index e06af9d36d7b..64d6a478a80c 100644 --- a/components/brave_new_tab_ui/components/default/rewards/index.tsx +++ b/components/brave_new_tab_ui/components/default/rewards/index.tsx @@ -28,7 +28,7 @@ const onboardingCompleted = new OnboardingCompletedStore() export function showRewardsOnboarding () { if (!onboardingCompleted.load()) { onboardingCompleted.save() - chrome.braveRewards.openBrowserActionUI('brave_rewards_panel.html#tour') + chrome.braveRewards.showRewardsTour() } } @@ -94,8 +94,7 @@ export const RewardsWidget = createWidget((props: RewardsProps) => { const onClaimGrant = () => { if (grantInfo) { - chrome.braveRewards.openBrowserActionUI( - `brave_rewards_panel.html#grant_${grantInfo.id}`) + chrome.braveRewards.showGrantCaptcha(grantInfo.id) } } diff --git a/components/brave_rewards/browser/rewards_service.cc b/components/brave_rewards/browser/rewards_service.cc index 6fbab4929609..a46783e3526a 100644 --- a/components/brave_rewards/browser/rewards_service.cc +++ b/components/brave_rewards/browser/rewards_service.cc @@ -5,23 +5,49 @@ #include "brave/components/brave_rewards/browser/rewards_service.h" +#include +#include +#include + #include "base/logging.h" #include "base/time/time.h" #include "brave/components/brave_rewards/browser/rewards_notification_service_impl.h" #include "brave/components/brave_rewards/browser/rewards_service_observer.h" #include "brave/components/brave_rewards/common/pref_names.h" +#include "brave/components/ipfs/buildflags/buildflags.h" #include "build/build_config.h" #include "components/prefs/pref_registry_simple.h" -#include "content/public/common/referrer.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" -namespace brave_rewards { +#if BUILDFLAG(ENABLE_IPFS) +#include "brave/components/ipfs/ipfs_constants.h" +#include "brave/components/ipfs/ipfs_utils.h" +#endif -RewardsService::RewardsService() { -} +namespace { -RewardsService::~RewardsService() { +const std::array kGreaselionDomains = { + "twitter.com", "github.com", "reddit.com", + "twitch.tv", "vimeo.com", "youtube.com"}; + +bool IsGreaselionURL(const GURL& url) { + return std::any_of( + kGreaselionDomains.begin(), kGreaselionDomains.end(), + [&url](auto& domain) { + return net::registry_controlled_domains::SameDomainOrHost( + url, GURL("https://" + domain), + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); + }); } +} // namespace + +namespace brave_rewards { + +RewardsService::RewardsService() = default; + +RewardsService::~RewardsService() = default; + void RewardsService::AddObserver(RewardsServiceObserver* observer) { observers_.AddObserver(observer); } @@ -30,6 +56,25 @@ void RewardsService::RemoveObserver(RewardsServiceObserver* observer) { observers_.RemoveObserver(observer); } +std::string RewardsService::GetPublisherIdFromURL(const GURL& url) { + if (IsGreaselionURL(url)) { + return ""; + } + +#if BUILDFLAG(ENABLE_IPFS) + if (url.SchemeIs(ipfs::kIPNSScheme)) { + return ipfs::GetRegistryDomainFromIPNS(url); + } +#endif + + if (!url.SchemeIsHTTPOrHTTPS()) { + return ""; + } + + return net::registry_controlled_domains::GetDomainAndRegistry( + url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); +} + // static void RewardsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { registry->RegisterStringPref(prefs::kNotifications, ""); diff --git a/components/brave_rewards/browser/rewards_service.h b/components/brave_rewards/browser/rewards_service.h index 572d1251bb8c..ad3469978f8b 100644 --- a/components/brave_rewards/browser/rewards_service.h +++ b/components/brave_rewards/browser/rewards_service.h @@ -140,6 +140,8 @@ class RewardsService : public KeyedService { RewardsService& operator=(const RewardsService&) = delete; ~RewardsService() override; + static std::string GetPublisherIdFromURL(const GURL& url); + virtual bool IsInitialized() = 0; virtual void CreateWallet(CreateWalletCallback callback) = 0; @@ -282,6 +284,10 @@ class RewardsService : public KeyedService { const uint64_t duration, const bool firstVisit) = 0; + virtual void IsPublisherRegistered( + const std::string& publisher_id, + base::OnceCallback callback) = 0; + virtual void GetPublisherInfo( const std::string& publisher_key, GetPublisherInfoCallback callback) = 0; diff --git a/components/brave_rewards/browser/rewards_service_impl.cc b/components/brave_rewards/browser/rewards_service_impl.cc index 5cf8c64cdb7f..2a81ac350d87 100644 --- a/components/brave_rewards/browser/rewards_service_impl.cc +++ b/components/brave_rewards/browser/rewards_service_impl.cc @@ -1977,6 +1977,21 @@ void RewardsServiceImpl::UpdateMediaDuration( first_visit); } +void RewardsServiceImpl::IsPublisherRegistered( + const std::string& publisher_id, + base::OnceCallback callback) { + if (!Connected()) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce( + [](decltype(callback) callback) { std::move(callback).Run(false); }, + std::move(callback))); + return; + } + + bat_ledger_->IsPublisherRegistered(publisher_id, std::move(callback)); +} + void RewardsServiceImpl::GetPublisherInfo( const std::string& publisher_key, GetPublisherInfoCallback callback) { @@ -2540,6 +2555,18 @@ void RewardsServiceImpl::PublisherListNormalized( } } +void RewardsServiceImpl::OnPublisherRegistryUpdated() { + for (auto& observer : observers_) { + observer.OnPublisherRegistryUpdated(); + } +} + +void RewardsServiceImpl::OnPublisherUpdated(const std::string& publisher_id) { + for (auto& observer : observers_) { + observer.OnPublisherUpdated(publisher_id); + } +} + void RewardsServiceImpl::RefreshPublisher( const std::string& publisher_key, RefreshPublisherCallback callback) { diff --git a/components/brave_rewards/browser/rewards_service_impl.h b/components/brave_rewards/browser/rewards_service_impl.h index d091ccf4c58a..fefa8dab3d91 100644 --- a/components/brave_rewards/browser/rewards_service_impl.h +++ b/components/brave_rewards/browser/rewards_service_impl.h @@ -250,6 +250,9 @@ class RewardsServiceImpl : public RewardsService, const uint64_t duration, const bool first_visit) override; + void IsPublisherRegistered(const std::string& publisher_id, + base::OnceCallback callback) override; + void GetPublisherInfo( const std::string& publisher_key, GetPublisherInfoCallback callback) override; @@ -649,6 +652,9 @@ class RewardsServiceImpl : public RewardsService, void PublisherListNormalized(ledger::type::PublisherInfoList list) override; + void OnPublisherRegistryUpdated() override; + void OnPublisherUpdated(const std::string& publisher_id) override; + void ShowNotification( const std::string& type, const std::vector& args, diff --git a/components/brave_rewards/browser/rewards_service_observer.h b/components/brave_rewards/browser/rewards_service_observer.h index 1af8e7989bed..9380fccc9aee 100644 --- a/components/brave_rewards/browser/rewards_service_observer.h +++ b/components/brave_rewards/browser/rewards_service_observer.h @@ -65,6 +65,10 @@ class RewardsServiceObserver : public base::CheckedObserver { RewardsService* rewards_service, ledger::type::PublisherInfoList list) {} + virtual void OnPublisherRegistryUpdated() {} + + virtual void OnPublisherUpdated(const std::string& publisher_id) {} + virtual void OnStatementChanged( brave_rewards::RewardsService* rewards_service) {} diff --git a/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.cc b/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.cc index 809aa509e74a..6c0bb27e88b6 100644 --- a/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.cc +++ b/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.cc @@ -6,23 +6,23 @@ #include #include "base/test/bind.h" -#include "brave/browser/extensions/api/brave_action_api.h" -#include "brave/browser/ui/views/brave_actions/brave_actions_container.h" -#include "brave/browser/ui/views/location_bar/brave_location_bar_view.h" +#include "brave/browser/brave_rewards/rewards_panel_service.h" +#include "brave/browser/brave_rewards/rewards_panel_service_factory.h" #include "brave/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.h" #include "brave/components/brave_rewards/browser/test/common/rewards_browsertest_context_util.h" #include "brave/components/brave_rewards/browser/test/common/rewards_browsertest_util.h" -#include "brave/components/brave_rewards/common/pref_names.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/test/browser_test_utils.h" -#include "extensions/common/constants.h" #include "testing/gtest/include/gtest/gtest.h" namespace rewards_browsertest { +using brave_rewards::RewardsPanelService; +using brave_rewards::RewardsPanelServiceFactory; + RewardsBrowserTestContextHelper::RewardsBrowserTestContextHelper( Browser* browser) { browser_ = browser; @@ -32,68 +32,54 @@ RewardsBrowserTestContextHelper::~RewardsBrowserTestContextHelper() = default; void RewardsBrowserTestContextHelper::OpenPopup() { // Ask the popup to open - std::string error; - bool popup_shown = extensions::BraveActionAPI::ShowActionUI( - browser_, - brave_rewards_extension_id, - nullptr, - &error); + auto* service = + RewardsPanelServiceFactory::GetForProfile(browser_->profile()); + + bool popup_shown = service && service->OpenRewardsPanel(); if (!popup_shown) { - LOG(ERROR) << "Could not open rewards popup: " << error; + LOG(ERROR) << "Could not open rewards popup"; } - EXPECT_TRUE(popup_shown); -} -void RewardsBrowserTestContextHelper::OpenPopupFirstTime() { - BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_); - BraveLocationBarView* brave_location_bar_view = - static_cast(browser_view->GetLocationBarView()); - ASSERT_NE(brave_location_bar_view, nullptr); - auto* brave_actions = brave_location_bar_view->GetBraveActionsContainer(); - ASSERT_NE(brave_actions, nullptr); - - brave_actions->OnRewardsStubButtonClicked(); - loaded_ = true; + EXPECT_TRUE(popup_shown); } base::WeakPtr RewardsBrowserTestContextHelper::OpenRewardsPopup() { + if (popup_web_contents_) { + OpenPopup(); + return popup_web_contents_; + } + // Construct an observer to wait for the popup to load - base::WeakPtr popup_contents; auto check_load_is_rewards_panel = [&](const content::NotificationSource& source, - const content::NotificationDetails&) -> bool { + const content::NotificationDetails&) { auto web_contents_source = static_cast&>(source); - popup_contents = web_contents_source.ptr()->GetWeakPtr(); - - // Check that this notification is for the Rewards panel and not, say, - // the extension background page. - std::string url = popup_contents->GetLastCommittedURL().spec(); - std::string rewards_panel_url = std::string("chrome-extension://") + - brave_rewards_extension_id + "/brave_rewards_panel.html"; - return url == rewards_panel_url; + auto* web_contents = web_contents_source.ptr(); + GURL url = web_contents->GetLastCommittedURL(); + + if (RewardsPanelService::IsRewardsPanelURLForTesting(url)) { + popup_web_contents_ = web_contents->GetWeakPtr(); + return true; + } + + return false; }; content::WindowedNotificationObserver popup_observer( content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, base::BindLambdaForTesting(check_load_is_rewards_panel)); - bool ac_enabled = browser_->profile()->GetPrefs()-> - GetBoolean(brave_rewards::prefs::kAutoContributeEnabled); - - if (loaded_ || ac_enabled) { - OpenPopup(); - } else { - OpenPopupFirstTime(); - } + OpenPopup(); // Wait for the popup to load popup_observer.Wait(); + rewards_browsertest_util::WaitForElementToAppear( - popup_contents.get(), "[data-test-id=rewards-panel]"); + popup_web_contents_.get(), "[data-test-id=rewards-panel]"); - return popup_contents; + return popup_web_contents_; } base::WeakPtr diff --git a/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.h b/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.h index 72b0bd65e1bf..8a0cf1cbb42b 100644 --- a/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.h +++ b/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.h @@ -45,10 +45,8 @@ class RewardsBrowserTestContextHelper { private: void OpenPopup(); - void OpenPopupFirstTime(); - raw_ptr browser_ = nullptr; // NOT OWNED - bool loaded_ = false; + base::WeakPtr popup_web_contents_; }; } // namespace rewards_browsertest diff --git a/components/brave_rewards/common/BUILD.gn b/components/brave_rewards/common/BUILD.gn index 6edcf376aa21..989d4f72aa37 100644 --- a/components/brave_rewards/common/BUILD.gn +++ b/components/brave_rewards/common/BUILD.gn @@ -1,3 +1,5 @@ +import("//mojo/public/tools/bindings/mojom.gni") + source_set("features") { sources = [ "features.cc", @@ -26,3 +28,9 @@ static_library("common") { "//third_party/abseil-cpp:absl", ] } + +mojom("mojom") { + sources = [ "brave_rewards_panel.mojom" ] + + public_deps = [ "//mojo/public/mojom/base" ] +} diff --git a/components/brave_rewards/common/brave_rewards_panel.mojom b/components/brave_rewards/common/brave_rewards_panel.mojom new file mode 100644 index 000000000000..38b2ac3dbabb --- /dev/null +++ b/components/brave_rewards/common/brave_rewards_panel.mojom @@ -0,0 +1,48 @@ +// Copyright (c) 2022 The Brave Authors. All rights reserved. +// 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/. + +module brave_rewards.mojom; + +enum RewardsPanelView { + kDefault, + kRewardsTour, + kGrantCaptcha, + kAdaptiveCaptcha, + kBraveTalkOptIn +}; + +struct RewardsPanelArgs { + RewardsPanelView view = RewardsPanelView.kDefault; + string data; +}; + +// Used by the WebUI page to bootstrap bidirectional communication. +interface PanelHandlerFactory { + // The WebUI calls this method when the page is first initialized. + CreatePanelHandler(pending_receiver panel_handler, + pending_remote ui_handler); +}; + +// Browser-side handler for requests from WebUI page. +interface PanelHandler { + // Notify the browser that the UI is ready to be shown. + ShowUI(); + + // Notify the browser that the panel should be closed. + CloseUI(); + + // Starts the Rewards services if necessary. + StartRewards() => (); + + // Returns the `RewardsPanelArgs` for the most recent panel request. + GetRewardsPanelArgs() => (RewardsPanelArgs args); +}; + +// WebUI-side handler for requests from the browser. +interface PanelUIHandler { + // Called when a browser component has requested that the Rewards panel be + // displayed to the user. + OnRewardsPanelRequested(RewardsPanelArgs panel_args); +}; diff --git a/components/brave_rewards/common/features.cc b/components/brave_rewards/common/features.cc index f82b63143d9a..b1179adcbd39 100644 --- a/components/brave_rewards/common/features.cc +++ b/components/brave_rewards/common/features.cc @@ -29,5 +29,8 @@ const base::Feature kGeminiFeature{"BraveRewardsGemini", const base::Feature kVerboseLoggingFeature{"BraveRewardsVerboseLogging", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kWebUIPanelFeature{"BraveRewardsWebUIPanel", + base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features } // namespace brave_rewards diff --git a/components/brave_rewards/common/features.h b/components/brave_rewards/common/features.h index 8b645b1a7139..ae58724cfba0 100644 --- a/components/brave_rewards/common/features.h +++ b/components/brave_rewards/common/features.h @@ -26,6 +26,8 @@ extern const base::Feature kGeminiFeature; extern const base::Feature kVerboseLoggingFeature; +extern const base::Feature kWebUIPanelFeature; + } // namespace features } // namespace brave_rewards diff --git a/components/brave_rewards/resources/BUILD.gn b/components/brave_rewards/resources/BUILD.gn index e9bd6fee0b37..8e07e133aab5 100644 --- a/components/brave_rewards/resources/BUILD.gn +++ b/components/brave_rewards/resources/BUILD.gn @@ -54,8 +54,14 @@ repack("resources") { ] if (!is_android) { - sources += [ "$root_gen_dir/brave/components/brave_rewards/resources/brave_rewards_tip_generated.pak" ] - public_deps += [ ":tip_generated_resources" ] + sources += [ + "$root_gen_dir/brave/components/brave_rewards/resources/brave_rewards_panel_generated.pak", + "$root_gen_dir/brave/components/brave_rewards/resources/brave_rewards_tip_generated.pak", + ] + public_deps += [ + ":tip_generated_resources", + "rewards_panel:brave_rewards_panel_generated", + ] } output = "$root_gen_dir/brave/components/brave_rewards/resources/brave_rewards_resources.pak" diff --git a/components/brave_rewards/resources/brave_rewards_static_resources.grd b/components/brave_rewards/resources/brave_rewards_static_resources.grd index 82a75fd4f8a4..c919fd0c65a8 100644 --- a/components/brave_rewards/resources/brave_rewards_static_resources.grd +++ b/components/brave_rewards/resources/brave_rewards_static_resources.grd @@ -25,6 +25,7 @@ + diff --git a/components/brave_rewards/resources/extension/BUILD.gn b/components/brave_rewards/resources/extension/BUILD.gn index b63bfba5e1d4..eef8d058a19e 100644 --- a/components/brave_rewards/resources/extension/BUILD.gn +++ b/components/brave_rewards/resources/extension/BUILD.gn @@ -7,10 +7,10 @@ import("//tools/grit/repack.gni") assert(enable_extensions) pack_web_resources("extension_generated_resources") { - resource_name = "brave_rewards_panel" + resource_name = "brave_rewards_extension_panel" output_dir = "$root_gen_dir/brave/components/brave_rewards/resources/extension" - deps = [ "brave_rewards:brave_rewards_panel" ] + deps = [ "brave_rewards:brave_rewards_extension_panel" ] } brave_grit("static_resources") { @@ -30,8 +30,8 @@ repack("resources") { ] sources = [ + "$root_gen_dir/brave/components/brave_rewards/resources/extension/brave_rewards_extension_panel_generated.pak", "$root_gen_dir/brave/components/brave_rewards/resources/extension/brave_rewards_extension_static.pak", - "$root_gen_dir/brave/components/brave_rewards/resources/extension/brave_rewards_panel_generated.pak", ] output = "$root_gen_dir/brave/components/brave_rewards/resources/extension/brave_rewards_extension_resources.pak" diff --git a/components/brave_rewards/resources/extension/brave_rewards/BUILD.gn b/components/brave_rewards/resources/extension/brave_rewards/BUILD.gn index cbbbd3f02f2c..5421de7c0b9d 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/BUILD.gn +++ b/components/brave_rewards/resources/extension/brave_rewards/BUILD.gn @@ -1,10 +1,10 @@ import("//brave/components/common/typescript.gni") group("brave_rewards") { - deps = [ ":brave_rewards_panel" ] + deps = [ ":brave_rewards_extension_panel" ] } -transpile_web_ui("brave_rewards_panel") { +transpile_web_ui("brave_rewards_extension_panel") { entry_points = [ [ "brave_rewards_panel", @@ -20,11 +20,13 @@ transpile_web_ui("brave_rewards_panel") { ], ] - resource_name = "brave_rewards_panel" + resource_name = "brave_rewards_extension_panel" # Must match the relative path from the static GRD to the manifest.json # plus any other relative path we want these files to live in the extension extra_relative_path = "/brave_rewards/out" public_asset_path = "/out/" + + deps = [ "//brave/components/brave_rewards/common:mojom_js" ] } 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 6f696037e3cd..cc0defd55788 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 @@ -42,25 +42,6 @@ const updateBadgeTextAllWindows = (windows: chrome.windows.Window[], state?: Rew }) } -const handledByGreaselion = (url: URL) => { - if (!url) { - return false - } - - return url.hostname.endsWith('.github.com') || - url.hostname === 'github.com' || - url.hostname.endsWith('.reddit.com') || - url.hostname === 'reddit.com' || - url.hostname.endsWith('.twitch.tv') || - url.hostname === 'twitch.tv' || - url.hostname.endsWith('.twitter.com') || - url.hostname === 'twitter.com' || - url.hostname.endsWith('.vimeo.com') || - url.hostname === 'vimeo.com' || - url.hostname.endsWith('.youtube.com') || - url.hostname === 'youtube.com' -} - export const rewardsPanelReducer: Reducer = (state: RewardsExtension.State, action: any) => { if (!state) { return @@ -85,15 +66,6 @@ export const rewardsPanelReducer: Reducer = const validKey = publisher && publisher.publisherKey && publisher.publisherKey.length > 0 if (!publisher || (publisher.tabUrl !== tab.url || !validKey)) { - // Invalid publisher for tab, re-fetch publisher. - if (!handledByGreaselion(new URL(tab.url))) { - chrome.braveRewards.getPublisherData( - tab.id, - tab.url, - tab.favIconUrl || '', - payload.publisherBlob || '') - } - if (publisher) { delete publishers[tabKey] } diff --git a/components/brave_rewards/resources/page/reducers/promotion_reducer.ts b/components/brave_rewards/resources/page/reducers/promotion_reducer.ts index 93f35fca5a74..bc227c2e632c 100644 --- a/components/brave_rewards/resources/page/reducers/promotion_reducer.ts +++ b/components/brave_rewards/resources/page/reducers/promotion_reducer.ts @@ -89,8 +89,7 @@ const promotionReducer: Reducer = (state: Rewards.Sta // The grant captcha "lives" in the Rewards panel. Open the Rewards panel // with the grant ID specified in the URL. - chrome.braveRewards.openBrowserActionUI( - `brave_rewards_panel.html#grant_${promotionId}`) + chrome.braveRewards.showGrantCaptcha(promotionId) break } case types.ON_CLAIM_PROMOTION: { diff --git a/components/brave_rewards/resources/rewards_panel/BUILD.gn b/components/brave_rewards/resources/rewards_panel/BUILD.gn new file mode 100644 index 000000000000..dcd68118f802 --- /dev/null +++ b/components/brave_rewards/resources/rewards_panel/BUILD.gn @@ -0,0 +1,17 @@ +import("//brave/components/common/typescript.gni") +import("//mojo/public/tools/bindings/mojom.gni") + +transpile_web_ui("brave_rewards_panel") { + entry_points = [ [ + "brave_rewards_panel", + rebase_path("main.tsx"), + ] ] + resource_name = "brave_rewards_panel" + deps = [ "//brave/components/brave_rewards/common:mojom_js" ] +} + +pack_web_resources("brave_rewards_panel_generated") { + resource_name = "brave_rewards_panel" + output_dir = "$root_gen_dir/brave/components/brave_rewards/resources" + deps = [ ":brave_rewards_panel" ] +} diff --git a/components/brave_rewards/resources/rewards_panel/components/adaptive_captcha_view.style.ts b/components/brave_rewards/resources/rewards_panel/components/adaptive_captcha_view.style.ts index b2e303fa78ff..26aca9c333ab 100644 --- a/components/brave_rewards/resources/rewards_panel/components/adaptive_captcha_view.style.ts +++ b/components/brave_rewards/resources/rewards_panel/components/adaptive_captcha_view.style.ts @@ -92,11 +92,10 @@ export const closeAction = styled.div` export const helpAction = styled.div` margin-top: 36px; + text-align: center; - button { - display: block; - height: auto; - margin: 0 auto; + a { + display: inline-block; padding: 10px 22px; font-weight: 600; font-size: 13px; @@ -104,7 +103,6 @@ export const helpAction = styled.div` background: var(--brave-palette-white); color: #212529; border: 1px solid var(--brave-palette-grey500); - box-sizing: border-box; border-radius: 48px; cursor: pointer; diff --git a/components/brave_rewards/resources/rewards_panel/components/adaptive_captcha_view.tsx b/components/brave_rewards/resources/rewards_panel/components/adaptive_captcha_view.tsx index 8fc9af984a19..b1507727a392 100644 --- a/components/brave_rewards/resources/rewards_panel/components/adaptive_captcha_view.tsx +++ b/components/brave_rewards/resources/rewards_panel/components/adaptive_captcha_view.tsx @@ -5,6 +5,7 @@ import * as React from 'react' import { LocaleContext } from '../../shared/lib/locale_context' +import { NewTabLink } from '../../shared/components/new_tab_link' import { AdaptiveCaptchaInfo, AdaptiveCaptchaResult } from '../../rewards_panel/lib/interfaces' import * as styles from './adaptive_captcha_view.style' @@ -74,11 +75,6 @@ export function AdaptiveCaptchaView (props: Props) { return () => { window.removeEventListener('message', listener) } }, [props.onCaptchaResult]) - function onContactSupport () { - window.open('https://support.brave.com/', '_blank') - props.onClose() - } - function renderCaptcha () { return ( @@ -127,9 +123,12 @@ export function AdaptiveCaptchaView (props: Props) { {getString('captchaMaxAttemptsExceededText')} - + diff --git a/components/brave_rewards/resources/rewards_panel/components/app.style.ts b/components/brave_rewards/resources/rewards_panel/components/app.style.ts index 0c79196d74d6..e1784902ddf4 100644 --- a/components/brave_rewards/resources/rewards_panel/components/app.style.ts +++ b/components/brave_rewards/resources/rewards_panel/components/app.style.ts @@ -24,6 +24,17 @@ export const root = styled.div` .panel-overlay-grant-captcha & { min-height: 576px; } + + /* The Brave Talk opt-in is a special-case: for historical reasons it's not + presented as an overlay. Instead it should replace the panel content. */ + + .panel-overlay-brave-talk-opt-in & { + padding: 0; + + .rewards-panel { + display: none; + } + } ` export const loading = styled.div` diff --git a/components/brave_rewards/resources/rewards_panel/components/app.tsx b/components/brave_rewards/resources/rewards_panel/components/app.tsx index 4591c4245c0c..3cb5a498dc92 100644 --- a/components/brave_rewards/resources/rewards_panel/components/app.tsx +++ b/components/brave_rewards/resources/rewards_panel/components/app.tsx @@ -27,17 +27,25 @@ function Loading () { export function App (props: Props) { const [loading, setLoading] = React.useState(props.host.state.loading) + const [openTime, setOpenTime] = React.useState(props.host.state.openTime) useHostListener(props.host, (state) => { setLoading(state.loading) + setOpenTime(state.openTime) }) + React.useEffect(() => { props.host.onAppRendered() }, [props.host, openTime]) + + // This component key is used to reset the internal view state of the + // component tree when a cached panel is reopened. + const panelKey = `panel-${openTime}` + return ( - {loading ? : } + {loading ? : } diff --git a/components/brave_rewards/resources/rewards_panel/components/monthly_tip_view.tsx b/components/brave_rewards/resources/rewards_panel/components/monthly_tip_view.tsx index ecf455622128..df72b325781f 100644 --- a/components/brave_rewards/resources/rewards_panel/components/monthly_tip_view.tsx +++ b/components/brave_rewards/resources/rewards_panel/components/monthly_tip_view.tsx @@ -42,6 +42,16 @@ export function MonthlyTipView (props: Props) { setShowActions(!showActions) } + function onUpdateClick () { + setShowActions(false) + props.onUpdateClick() + } + + function onCancelClick () { + setShowActions(false) + props.onCancelClick() + } + return ( @@ -62,7 +72,7 @@ export function MonthlyTipView (props: Props) {