From a54ee96722f4de7f55e385e9da89e62a984a0fb3 Mon Sep 17 00:00:00 2001 From: ryanml Date: Mon, 7 Oct 2019 21:49:56 -0700 Subject: [PATCH 1/2] Feature brave/brave-browser#6370 - Adding Rewards widget to NTP Checking onlyAnonWallet for currency display Fixes brave/brave-browser#6598 --- browser/brave_profile_prefs.cc | 1 + browser/extensions/api/brave_rewards_api.cc | 116 ++++++++ browser/extensions/api/brave_rewards_api.h | 59 ++++ .../ui/webui/brave_new_tab_message_handler.cc | 8 + browser/ui/webui/brave_rewards_page_ui.cc | 15 + browser/ui/webui/brave_webui_source.cc | 27 +- common/extensions/api/_api_features.json | 25 +- common/extensions/api/brave_rewards.json | 79 +++++ common/pref_names.cc | 1 + common/pref_names.h | 1 + .../brave_ads/browser/ads_service_impl.cc | 1 + .../browser/ads_service_impl_unittest.cc | 1 + .../actions/new_tab_actions.ts | 49 +++- .../brave_new_tab_ui/api/initialData.ts | 72 ++++- .../brave_new_tab_ui/api/preferences.ts | 5 + .../brave_new_tab_ui/apiEventsToStore.ts | 60 +++- components/brave_new_tab_ui/brave_new_tab.tsx | 4 +- .../components/default/grid/index.ts | 16 +- .../components/default/index.ts | 2 + .../components/default/rewards/index.tsx | 269 ++++++++++++++++++ .../default/rewards/notification.tsx | 58 ++++ .../components/default/rewards/style.ts | 151 ++++++++++ .../constants/new_tab_types.ts | 16 +- .../brave_new_tab_ui/containers/app.tsx | 1 + .../containers/newTab/footerInfo.tsx | 8 +- .../containers/newTab/index.tsx | 42 ++- .../containers/newTab/settings.tsx | 12 + .../reducers/new_tab_reducer.tsx | 155 +++++++++- components/brave_new_tab_ui/rewards-utils.ts | 17 ++ components/brave_new_tab_ui/storage.ts | 22 +- .../stories/default/footerInfo.tsx | 8 +- .../stories/default/index.tsx | 92 +++++- .../stories/default/settings.tsx | 12 + .../brave_new_tab_ui/stories/fakeLocale.ts | 3 +- .../extension_rewards_service_observer.cc | 18 ++ .../extension_rewards_service_observer.h | 3 + .../brave_rewards/browser/rewards_service.h | 1 + .../browser/rewards_service_browsertest.cc | 16 +- .../browser/rewards_service_impl.cc | 6 + .../browser/rewards_service_impl.h | 1 + .../browser/rewards_service_impl_unittest.cc | 1 + .../browser/rewards_service_observer.h | 3 + .../brave_rewards/components/app.tsx | 16 ++ components/definitions/chromel.d.ts | 9 + components/definitions/newTab.d.ts | 64 +++++ .../resources/brave_components_strings.grd | 19 +- 46 files changed, 1535 insertions(+), 30 deletions(-) create mode 100644 components/brave_new_tab_ui/components/default/rewards/index.tsx create mode 100644 components/brave_new_tab_ui/components/default/rewards/notification.tsx create mode 100644 components/brave_new_tab_ui/components/default/rewards/style.ts create mode 100644 components/brave_new_tab_ui/rewards-utils.ts diff --git a/browser/brave_profile_prefs.cc b/browser/brave_profile_prefs.cc index 7430fd73f05c..3adcc95f7e9f 100644 --- a/browser/brave_profile_prefs.cc +++ b/browser/brave_profile_prefs.cc @@ -160,6 +160,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref(kNewTabPageShowClock, true); registry->RegisterBooleanPref(kNewTabPageShowTopSites, true); registry->RegisterBooleanPref(kNewTabPageShowStats, true); + registry->RegisterBooleanPref(kNewTabPageShowRewards, true); // Brave Wallet registry->RegisterStringPref(kBraveWalletAES256GCMSivNonce, ""); diff --git a/browser/extensions/api/brave_rewards_api.cc b/browser/extensions/api/brave_rewards_api.cc index d5c30fb534cf..7b946da4e0c7 100644 --- a/browser/extensions/api/brave_rewards_api.cc +++ b/browser/extensions/api/brave_rewards_api.cc @@ -968,5 +968,121 @@ BraveRewardsOnlyAnonWalletFunction::Run() { return RespondNow(OneArgument(std::make_unique(only))); } +BraveRewardsGetAdsEnabledFunction:: +~BraveRewardsGetAdsEnabledFunction() { +} + +ExtensionFunction::ResponseAction +BraveRewardsGetAdsEnabledFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + AdsService* ads_service_ = + AdsServiceFactory::GetForProfile(profile); + + if (!ads_service_) { + return RespondNow(Error("Ads service is not initialized")); + } + + const bool enabled = ads_service_->IsEnabled(); + return RespondNow( + OneArgument(std::make_unique(enabled))); +} + +BraveRewardsGetAdsEstimatedEarningsFunction:: +~BraveRewardsGetAdsEstimatedEarningsFunction() { +} + +ExtensionFunction::ResponseAction +BraveRewardsGetAdsEstimatedEarningsFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + RewardsService* rewards_service = + RewardsServiceFactory::GetForProfile(profile); + + if (!rewards_service) { + return RespondNow(Error("Rewards service is not initialized")); + } + + rewards_service->GetTransactionHistory(base::Bind( + &BraveRewardsGetAdsEstimatedEarningsFunction::OnAdsEstimatedEarnings, + this)); + return RespondLater(); +} + +void BraveRewardsGetAdsEstimatedEarningsFunction::OnAdsEstimatedEarnings( + const double estimated_pending_rewards, + const uint64_t next_payment_date_in_seconds, + const uint64_t ad_notifications_received_this_month) { + Respond(OneArgument( + std::make_unique(estimated_pending_rewards))); +} + +BraveRewardsGetBalanceReportsFunction:: +~BraveRewardsGetBalanceReportsFunction() { +} + +ExtensionFunction::ResponseAction +BraveRewardsGetBalanceReportsFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + auto* rewards_service_ = RewardsServiceFactory::GetForProfile(profile); + + if (!rewards_service_) { + return RespondNow(Error("Rewards service is not not initialized")); + } + + rewards_service_->GetAllBalanceReports(base::Bind( + &BraveRewardsGetBalanceReportsFunction::OnGetBalanceReports, + this)); + + return RespondLater(); +} + +void BraveRewardsGetBalanceReportsFunction::OnGetBalanceReports( + const std::map& reports) { + std::unique_ptr data( + new base::DictionaryValue()); + + if (!reports.empty()) { + for (auto const& report : reports) { + const ::brave_rewards::BalanceReport old_report = report.second; + auto new_report = std::make_unique(); + new_report->SetString("grant", old_report.grants); + new_report->SetString("deposit", old_report.deposits); + new_report->SetString("ads", old_report.earning_from_ads); + new_report->SetString("contribute", old_report.auto_contribute); + new_report->SetString("donation", old_report.recurring_donation); + new_report->SetString("tips", old_report.one_time_donation); + new_report->SetString("total", old_report.total); + data->SetDictionary(report.first, std::move(new_report)); + } + } + + Respond(OneArgument(std::move(data))); +} + +BraveRewardsGetWalletExistsFunction:: +~BraveRewardsGetWalletExistsFunction() { +} + +ExtensionFunction::ResponseAction +BraveRewardsGetWalletExistsFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + RewardsService* rewards_service = + RewardsServiceFactory::GetForProfile(profile); + + if (!rewards_service) { + return RespondNow(Error("Rewards service is not initialized")); + } + + rewards_service->IsWalletCreated(base::Bind( + &BraveRewardsGetWalletExistsFunction::OnGetWalletExists, + this)); + return RespondLater(); +} + +void BraveRewardsGetWalletExistsFunction::OnGetWalletExists( + const bool exists) { + Respond(OneArgument(std::make_unique(exists))); +} + } // namespace api } // namespace extensions diff --git a/browser/extensions/api/brave_rewards_api.h b/browser/extensions/api/brave_rewards_api.h index bd215c146d6b..783d6f9d67b9 100644 --- a/browser/extensions/api/brave_rewards_api.h +++ b/browser/extensions/api/brave_rewards_api.h @@ -6,6 +6,7 @@ #ifndef BRAVE_BROWSER_EXTENSIONS_API_BRAVE_REWARDS_API_H_ #define BRAVE_BROWSER_EXTENSIONS_API_BRAVE_REWARDS_API_H_ +#include #include #include @@ -14,6 +15,7 @@ #include "brave/components/brave_rewards/browser/content_site.h" #include "brave/components/brave_rewards/browser/external_wallet.h" #include "brave/components/brave_rewards/browser/publisher_banner.h" +#include "brave/components/brave_rewards/browser/balance_report.h" #include "extensions/browser/extension_function.h" namespace extensions { @@ -371,6 +373,63 @@ class BraveRewardsOnlyAnonWalletFunction : public ExtensionFunction { ResponseAction Run() override; }; +class BraveRewardsGetAdsEnabledFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.getAdsEnabled", UNKNOWN) + + protected: + ~BraveRewardsGetAdsEnabledFunction() override; + + ResponseAction Run() override; +}; + +class BraveRewardsGetAdsEstimatedEarningsFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.getAdsEstimatedEarnings", UNKNOWN) + + protected: + ~BraveRewardsGetAdsEstimatedEarningsFunction() override; + + ResponseAction Run() override; + + private: + void OnAdsEstimatedEarnings( + const double estimated_pending_rewards, + const uint64_t next_payment_date_in_seconds, + const uint64_t ad_notifications_received_this_month); +}; + +class BraveRewardsGetBalanceReportsFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.getBalanceReports", UNKNOWN) + + protected: + ~BraveRewardsGetBalanceReportsFunction() override; + + ResponseAction Run() override; + + private: + void OnGetBalanceReports( + const std::map& reports); +}; + +class BraveRewardsGetWalletExistsFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveRewards.getWalletExists", UNKNOWN) + + protected: + ~BraveRewardsGetWalletExistsFunction() override; + + ResponseAction Run() override; + + private: + void OnGetWalletExists(const bool exists); +}; + } // namespace api } // namespace extensions diff --git a/browser/ui/webui/brave_new_tab_message_handler.cc b/browser/ui/webui/brave_new_tab_message_handler.cc index 544fa6ad74a5..48a69db91e4a 100644 --- a/browser/ui/webui/brave_new_tab_message_handler.cc +++ b/browser/ui/webui/brave_new_tab_message_handler.cc @@ -55,6 +55,9 @@ base::DictionaryValue GetPreferencesDictionary(PrefService* prefs) { pref_data.SetBoolean( "showStats", prefs->GetBoolean(kNewTabPageShowStats)); + pref_data.SetBoolean( + "showRewards", + prefs->GetBoolean(kNewTabPageShowRewards)); return pref_data; } @@ -168,6 +171,9 @@ void BraveNewTabMessageHandler::OnJavascriptAllowed() { pref_change_registrar_.Add(kNewTabPageShowTopSites, base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, base::Unretained(this))); + pref_change_registrar_.Add(kNewTabPageShowRewards, + base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, + base::Unretained(this))); } void BraveNewTabMessageHandler::OnJavascriptDisallowed() { @@ -229,6 +235,8 @@ void BraveNewTabMessageHandler::HandleSaveNewTabPagePref( settingsKey = kNewTabPageShowTopSites; } else if (settingsKeyInput == "showStats") { settingsKey = kNewTabPageShowStats; + } else if (settingsKeyInput == "showRewards") { + settingsKey = kNewTabPageShowRewards; } else { LOG(ERROR) << "Invalid setting key"; return; diff --git a/browser/ui/webui/brave_rewards_page_ui.cc b/browser/ui/webui/brave_rewards_page_ui.cc index d4fcd313d38f..36d6823d769c 100644 --- a/browser/ui/webui/brave_rewards_page_ui.cc +++ b/browser/ui/webui/brave_rewards_page_ui.cc @@ -238,6 +238,10 @@ class RewardsDOMHandler : public WebUIMessageHandler, int32_t result, const std::string& wallet_type) override; + void OnAdsEnabled( + brave_rewards::RewardsService* rewards_service, + bool ads_enabled) override; + // RewardsNotificationsServiceObserver implementation void OnNotificationAdded( brave_rewards::RewardsNotificationService* rewards_notification_service, @@ -1646,6 +1650,17 @@ void RewardsDOMHandler::OnDisconnectWallet( data); } +void RewardsDOMHandler::OnAdsEnabled( + brave_rewards::RewardsService* rewards_service, + bool ads_enabled) { + if (!web_ui()->CanCallJavascript()) { + return; + } + + base::ListValue* emptyArgs = nullptr; + GetAdsData(emptyArgs); +} + void RewardsDOMHandler::OnlyAnonWallet(const base::ListValue* args) { if (!rewards_service_) { return; diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index 662db126ab0a..b4e130801f21 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -155,6 +155,7 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "showBraveStats", IDS_BRAVE_NEW_TAB_SHOW_BRAVE_STATS }, { "showClock", IDS_BRAVE_NEW_TAB_SHOW_CLOCK }, { "showTopSites", IDS_BRAVE_NEW_TAB_SHOW_TOP_SITES }, + { "showRewards", IDS_BRAVE_NEW_TAB_SHOW_REWARDS }, // Private Tab - General { "learnMore", IDS_BRAVE_PRIVATE_NEW_TAB_LEARN_MORE }, @@ -189,7 +190,31 @@ void CustomizeWebUIHTMLSource(const std::string &name, // Private Tab - Private Window - Tor Box { "boxTorText2", IDS_BRAVE_PRIVATE_NEW_TAB_BOX_TOR_TEXT_2 }, - { "boxTorButton", IDS_BRAVE_PRIVATE_NEW_TAB_BOX_TOR_BUTTON } + { "boxTorButton", IDS_BRAVE_PRIVATE_NEW_TAB_BOX_TOR_BUTTON }, + + // Rewards widget + { "rewardsWidgetAnd", IDS_BRAVE_UI_AND }, + { "rewardsWidgetBat", IDS_BRAVE_UI_BAT_REWARDS_TEXT }, + { "rewardsWidgetBatPoints", IDS_BRAVE_UI_BAT_POINTS_TEXT }, + { "rewardsWidgetBraveRewards", IDS_BRAVE_UI_BRAVE_REWARDS }, + { "rewardsWidgetPrivacyPolicy", IDS_BRAVE_UI_PRIVACY_POLICY }, + { "rewardsWidgetTermsOfService", IDS_BRAVE_UI_TERMS_OF_SERVICE }, + { "rewardsWidgetTurnOnAds", IDS_BRAVE_UI_TURN_ON_ADS }, + { "rewardsWidgetClaimMyRewards", IDS_REWARDS_WIDGET_CLAIM_MY_REWARDS }, + { "rewardsWidgetWalletFailedButton", IDS_BRAVE_UI_WALLET_FAILED_BUTTON }, + { "rewardsWidgetAboutRewards", IDS_REWARDS_WIDGET_ABOUT_REWARDS }, + { "rewardsWidgetServiceText", IDS_REWARDS_WIDGET_SERVICE_TEXT }, + { "rewardsWidgetEstimatedEarnings", IDS_REWARDS_WIDGET_ESTIMATED_EARNINGS }, // NOLINT + { "rewardsWidgetMonthlyTips", IDS_REWARDS_WIDGET_MONTHLY_TIPS }, + { "rewardsWidgetTurningOn", IDS_REWARDS_WIDGET_TURNING_ON }, + { "rewardsWidgetTurnOnRewards", IDS_REWARDS_WIDGET_TURN_ON_REWARDS }, // NOLINT + { "rewardsWidgetReEnableTitle", IDS_REWARDS_WIDGET_REENABLE_TITLE }, // NOLINT + { "rewardsWidgetEnableTitle", IDS_REWARDS_WIDGET_ENABLE_TITLE }, + { "rewardsWidgetReEnableSubTitle", IDS_REWARDS_WIDGET_REENABLE_SUBTITLE }, // NOLINT + { "rewardsWidgetEnableSubTitle", IDS_REWARDS_WIDGET_ENABLE_SUBTITLE }, // NOLINT + { "rewardsWidgetNotificationTitle", IDS_REWARDS_WIDGET_NOTIFICATION_TITLE }, // NOLINT + { "rewardsWidgetNotificationTextAds", IDS_REWARDS_WIDGET_NOTIFICATION_TEXT_ADS }, // NOLINT + { "rewardsWidgetNotificationTextUGP", IDS_REWARDS_WIDGET_NOTIFICATION_TEXT_UGP } // NOLINT } }, { std::string("welcome"), { diff --git a/common/extensions/api/_api_features.json b/common/extensions/api/_api_features.json index ab2e1c70fb1a..07b1028c9e33 100644 --- a/common/extensions/api/_api_features.json +++ b/common/extensions/api/_api_features.json @@ -77,14 +77,23 @@ "contexts": ["blessed_extension"], "whitelist": ["3D9518A72EB02667A773B69DBA9E72E0F4A37423", "780BF954C0F7C586EA9662D4F967771F49CC2114", "FF32507DC3DB5DFFD1D6733187C84D4B74713D63"] }, - "braveRewards": { - "channel": "stable", - "dependencies": [], - "contexts": ["blessed_extension"], - "whitelist": [ - "46E9817CBF915C0D1F6BCCF916C42CC666FF1D64" - ] - }, + "braveRewards": [ + { + "channel": "stable", + "dependencies": [], + "contexts": ["blessed_extension"], + "whitelist": [ + "46E9817CBF915C0D1F6BCCF916C42CC666FF1D64" + ] + }, + { + "channel": "stable", + "contexts": ["webui"], + "matches": [ + "chrome://newtab/*" + ] + } + ], "rewardsNotifications": { "channel": "stable", "dependencies": [], diff --git a/common/extensions/api/brave_rewards.json b/common/extensions/api/brave_rewards.json index 04b6f53c6dd0..1df69a288cb6 100644 --- a/common/extensions/api/brave_rewards.json +++ b/common/extensions/api/brave_rewards.json @@ -230,6 +230,17 @@ } ] }, + { + "name": "onAdsEnabled", + "type": "function", + "description": "", + "parameters": [ + { + "name": "enabled", + "type": "boolean" + } + ] + }, { "name": "onEnabledMain", "type": "function", @@ -628,6 +639,57 @@ } ] }, + { + "name": "getAdsEnabled", + "type": "function", + "description": "Gets whether ads is enabled", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "enabled", + "type": "boolean" + } + ] + } + ] + }, + { + "name": "getAdsEstimatedEarnings", + "type": "function", + "description": "Gets estimated ads earnings for the current month", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "amount", + "type": "number" + } + ] + } + ] + }, + { + "name": "getBalanceReports", + "type": "function", + "description": "Gets balance reports and contribution totals", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "data", + "type": "any" + } + ] + } + ] + }, { "name": "getRewardsMainEnabled", "type": "function", @@ -645,6 +707,23 @@ } ] }, + { + "name": "getWalletExists", + "type": "function", + "description": "Gets whether a Rewards wallet already exists", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "exists", + "type": "boolean" + } + ] + } + ] + }, { "name": "getACEnabled", "type": "function", diff --git a/common/pref_names.cc b/common/pref_names.cc index 758112bed927..ac01e1e73254 100644 --- a/common/pref_names.cc +++ b/common/pref_names.cc @@ -63,6 +63,7 @@ const char kNewTabPageShowBackgroundImage[] = const char kNewTabPageShowClock[] = "brave.new_tab_page.show_clock"; const char kNewTabPageShowTopSites[] = "brave.new_tab_page.show_top_sites"; const char kNewTabPageShowStats[] = "brave.new_tab_page.show_stats"; +const char kNewTabPageShowRewards[] = "brave.new_tab_page.show_rewards"; const char kBraveEnabledMediaRouter[] = "brave.enable_media_router"; const char kBraveWalletAES256GCMSivNonce[] = "brave.wallet.aes_256_gcm_siv_nonce"; diff --git a/common/pref_names.h b/common/pref_names.h index 6f709804ce79..49d5bd976951 100644 --- a/common/pref_names.h +++ b/common/pref_names.h @@ -54,6 +54,7 @@ extern const char kNewTabPageShowBackgroundImage[]; extern const char kNewTabPageShowClock[]; extern const char kNewTabPageShowTopSites[]; extern const char kNewTabPageShowStats[]; +extern const char kNewTabPageShowRewards[]; extern const char kBraveEnabledMediaRouter[]; extern const char kBraveWalletAES256GCMSivNonce[]; extern const char kBraveWalletEncryptedSeed[]; diff --git a/components/brave_ads/browser/ads_service_impl.cc b/components/brave_ads/browser/ads_service_impl.cc index 1a509a828e50..e6eebd4860b3 100644 --- a/components/brave_ads/browser/ads_service_impl.cc +++ b/components/brave_ads/browser/ads_service_impl.cc @@ -411,6 +411,7 @@ bool AdsServiceImpl::IsSupportedRegion() const { void AdsServiceImpl::SetEnabled( const bool is_enabled) { SetBooleanPref(prefs::kEnabled, is_enabled); + rewards_service_->TriggerOnAdsEnabled(is_enabled); } void AdsServiceImpl::SetAdsPerHour( diff --git a/components/brave_ads/browser/ads_service_impl_unittest.cc b/components/brave_ads/browser/ads_service_impl_unittest.cc index fea5f5ccdb8f..2fc1bdf2a8a1 100644 --- a/components/brave_ads/browser/ads_service_impl_unittest.cc +++ b/components/brave_ads/browser/ads_service_impl_unittest.cc @@ -144,6 +144,7 @@ class MockRewardsService : public RewardsService { brave_rewards::SaveRecurringTipCallback)); MOCK_METHOD2(RefreshPublisher, void(const std::string&, brave_rewards::RefreshPublisherCallback)); + MOCK_METHOD1(TriggerOnAdsEnabled, void(bool)); MOCK_METHOD0(GetAllNotifications, const brave_rewards::RewardsNotificationService::RewardsNotificationsMap&()); MOCK_METHOD3(SaveInlineMediaInfo, diff --git a/components/brave_new_tab_ui/actions/new_tab_actions.ts b/components/brave_new_tab_ui/actions/new_tab_actions.ts index b13c08d3e948..6bb64447e030 100644 --- a/components/brave_new_tab_ui/actions/new_tab_actions.ts +++ b/components/brave_new_tab_ui/actions/new_tab_actions.ts @@ -9,7 +9,7 @@ import { types } from '../constants/new_tab_types' import { Preferences } from '../api/preferences' import { Stats } from '../api/stats' import { PrivateTabData } from '../api/privateTabData' -import { InitialData } from '../api/initialData' +import { InitialData, InitialRewardsData, PreInitialRewardsData } from '../api/initialData' export const bookmarkAdded = (url: string) => action(types.BOOKMARK_ADDED, { url @@ -74,3 +74,50 @@ export const preferencesUpdated = (preferences: Preferences) => export const setInitialData = (initialData: InitialData) => action(types.NEW_TAB_SET_INITIAL_DATA, initialData) + +export const createWallet = () => action(types.CREATE_WALLET, {}) + +export const onEnabledMain = (enabledMain: boolean) => action(types.ON_ENABLED_MAIN, { + enabledMain +}) + +export const onAdsEnabled = (enabled: boolean) => action(types.ON_ADS_ENABLED, { + enabled +}) + +export const onRewardsSettingSave = (key: string, value: any) => action(types.ON_REWARDS_SETTING_SAVE, { + key, + value +}) + +export const onWalletInitialized = (result: NewTab.RewardsResult) => action(types.ON_WALLET_INITIALIZED, { + result +}) + +export const onAdsEstimatedEarnings = (amount: number) => action(types.ON_ADS_ESTIMATED_EARNINGS, { + amount +}) + +export const onBalanceReports = (reports: Record) => action(types.ON_BALANCE_REPORTS, { + reports +}) + +export const onGrant = (properties: NewTab.GrantResponse) => action(types.ON_GRANT, { + properties +}) + +export const dismissNotification = (id: string) => action(types.DISMISS_NOTIFICATION, { + id +}) + +export const onBalance = (balance: NewTab.RewardsBalance) => action(types.ON_BALANCE, { + balance +}) + +export const onWalletExists = (exists: boolean) => action(types.ON_WALLET_EXISTS, { + exists +}) + +export const setInitialRewardsData = (initialRewardsData: InitialRewardsData) => action(types.SET_INITIAL_REWARDS_DATA, initialRewardsData) + +export const setPreInitialRewardsData = (preInitialRewardsData: PreInitialRewardsData) => action(types.SET_PRE_INITIAL_REWARDS_DATA, preInitialRewardsData) diff --git a/components/brave_new_tab_ui/api/initialData.ts b/components/brave_new_tab_ui/api/initialData.ts index 4bb0bc46e168..a4e442c89d15 100644 --- a/components/brave_new_tab_ui/api/initialData.ts +++ b/components/brave_new_tab_ui/api/initialData.ts @@ -15,8 +15,20 @@ export type InitialData = { topSites: topSitesAPI.TopSitesData } +export type PreInitialRewardsData = { + enabledAds: boolean + enabledMain: boolean +} + +export type InitialRewardsData = { + onlyAnonWallet: boolean + adsEstimatedEarnings: boolean + reports: Record + balance: NewTab.RewardsBalance +} + // Gets all data required for the first render of the page -export default async function getInitialData (): Promise { +export async function getInitialData (): Promise { try { console.timeStamp('Getting initial data...') const [preferences, stats, privateTabData, topSites] = await Promise.all([ @@ -37,3 +49,61 @@ export default async function getInitialData (): Promise { throw Error('Error getting initial data') } } + +export async function getRewardsPreInitialData (): Promise { + try { + const [ + enabledAds, + enabledMain + ] = await Promise.all([ + new Promise(resolve => chrome.braveRewards.getAdsEnabled((enabledAds: boolean) => { + resolve(enabledAds) + })), + new Promise(resolve => chrome.braveRewards.getRewardsMainEnabled((enabledMain: boolean) => { + resolve(enabledMain) + })) + ]) + return { + enabledAds, + enabledMain + } as PreInitialRewardsData + } catch (err) { + throw Error(err) + } +} + +export async function getRewardsInitialData (): Promise { + try { + const [ + onlyAnonWallet, + adsEstimatedEarnings, + reports, + balance + ] = await Promise.all([ + new Promise(resolve => chrome.braveRewards.onlyAnonWallet((onlyAnonWallet: boolean) => { + resolve(!!onlyAnonWallet) + })), + new Promise(resolve => chrome.braveRewards.getAdsEstimatedEarnings((adsEstimatedEarnings: number) => { + resolve(adsEstimatedEarnings) + })), + new Promise(resolve => chrome.braveRewards.getBalanceReports((reports: Record) => { + resolve(reports) + })), + new Promise(resolve => chrome.braveRewards.fetchBalance((balance: NewTab.RewardsBalance) => { + resolve(balance) + })), + new Promise(resolve => { + chrome.braveRewards.getGrants() + resolve(true) + }) + ]) + return { + onlyAnonWallet, + adsEstimatedEarnings, + reports, + balance + } as InitialRewardsData + } catch (err) { + throw Error(err) + } +} diff --git a/components/brave_new_tab_ui/api/preferences.ts b/components/brave_new_tab_ui/api/preferences.ts index cd600a6ec153..e62b49a8cbc8 100644 --- a/components/brave_new_tab_ui/api/preferences.ts +++ b/components/brave_new_tab_ui/api/preferences.ts @@ -15,6 +15,7 @@ export type Preferences = { showStats: boolean showClock: boolean showTopSites: boolean + showRewards: boolean } type PreferencesUpdatedHandler = (prefData: Preferences) => void @@ -43,6 +44,10 @@ export function saveShowStats (value: boolean): void { sendSavePref('showStats', value) } +export function saveShowRewards (value: boolean): void { + sendSavePref('showRewards', value) +} + export function addChangeListener (listener: PreferencesUpdatedHandler): void { window.cr.addWebUIListener('preferences-changed', listener) } diff --git a/components/brave_new_tab_ui/apiEventsToStore.ts b/components/brave_new_tab_ui/apiEventsToStore.ts index 6338caf31203..88b31d1249ae 100644 --- a/components/brave_new_tab_ui/apiEventsToStore.ts +++ b/components/brave_new_tab_ui/apiEventsToStore.ts @@ -7,7 +7,7 @@ import getActions from './api/getActions' import * as preferencesAPI from './api/preferences' import * as statsAPI from './api/stats' import * as privateTabDataAPI from './api/privateTabData' -import getInitialData from './api/initialData' +import { getInitialData, getRewardsInitialData, getRewardsPreInitialData } from './api/initialData' async function updatePreferences (prefData: preferencesAPI.Preferences) { getActions().preferencesUpdated(prefData) @@ -21,19 +21,75 @@ async function updatePrivateTabData (data: privateTabDataAPI.PrivateTabData) { getActions().privateTabDataUpdated(data) } +function onRewardsToggled (prefData: preferencesAPI.Preferences): void { + if (prefData.showRewards) { + rewardsInitData() + } +} + // Not marked as async so we don't return a promise // and confuse callers -export default function wireApiEventsToStore () { +export function wireApiEventsToStore () { // Get initial data and dispatch to store getInitialData() .then((initialData) => { + if (initialData.preferences.showRewards) { + rewardsInitData() + window.setInterval(() => { + fetchCreatedWalletData() + }, 30000) + } getActions().setInitialData(initialData) // Listen for API changes and dispatch to store statsAPI.addChangeListener(updateStats) preferencesAPI.addChangeListener(updatePreferences) + preferencesAPI.addChangeListener(onRewardsToggled) privateTabDataAPI.addChangeListener(updatePrivateTabData) }) .catch(e => { console.error('New Tab Page fatal error:', e) }) } + +export function rewardsInitData () { + getRewardsPreInitialData() + .then((preInitialRewardsData) => { + getActions().setPreInitialRewardsData(preInitialRewardsData) + chrome.braveRewards.getWalletExists((exists: boolean) => { + if (!exists) { + return + } + fetchCreatedWalletData() + getActions().onWalletExists(exists) + }) + }) + .catch(e => { + console.error('Error fetching pre-initial rewards data: ', e) + }) +} + +function fetchCreatedWalletData () { + getRewardsInitialData() + .then((initialRewardsData) => { + getActions().setInitialRewardsData(initialRewardsData) + }) + .catch(e => { + console.error('Error fetching initial rewards data: ', e) + }) +} + +chrome.braveRewards.onWalletInitialized.addListener((result: any | NewTab.RewardsResult) => { + getActions().onWalletInitialized(result) +}) + +chrome.braveRewards.onEnabledMain.addListener((enabledMain: boolean) => { + getActions().onEnabledMain(enabledMain) +}) + +chrome.braveRewards.onAdsEnabled.addListener((enabled: boolean) => { + getActions().onAdsEnabled(enabled) +}) + +chrome.braveRewards.onGrant.addListener((properties: NewTab.GrantResponse) => { + getActions().onGrant(properties) +}) diff --git a/components/brave_new_tab_ui/brave_new_tab.tsx b/components/brave_new_tab_ui/brave_new_tab.tsx index a4aca28665b6..cd9cc8285992 100644 --- a/components/brave_new_tab_ui/brave_new_tab.tsx +++ b/components/brave_new_tab_ui/brave_new_tab.tsx @@ -9,7 +9,7 @@ import { Provider } from 'react-redux' import Theme from 'brave-ui/theme/brave-default' import DarkTheme from 'brave-ui/theme/brave-dark' import BraveCoreThemeProvider from '../common/BraveCoreThemeProvider' -import wireAPIEventsToStore from './apiEventsToStore' +import { wireApiEventsToStore } from './apiEventsToStore' // Components import App from './containers/app' @@ -49,7 +49,7 @@ function initialize () { console.timeStamp('JS start') // Get store data going -wireAPIEventsToStore() +wireApiEventsToStore() // Perform DOM-dependent initialization when ready document.addEventListener('DOMContentLoaded', initialize) diff --git a/components/brave_new_tab_ui/components/default/grid/index.ts b/components/brave_new_tab_ui/components/default/grid/index.ts index 12a7c3e69706..03c09b2c3eb7 100644 --- a/components/brave_new_tab_ui/components/default/grid/index.ts +++ b/components/brave_new_tab_ui/components/default/grid/index.ts @@ -13,25 +13,31 @@ export const Header = styled<{}, 'header'>('header')` grid-template-areas: "stats . clock" ". . clock" - "topsites topsites topsites" + "topsites . rewards" + ". . ." "notification notification notification"; > *:nth-child(1) { grid-area: stats; margin: 0 46px 0 46px; } + > *:nth-child(2) { grid-area: clock; margin: 0 46px 0 46px; } > *:nth-child(3) { + grid-area: rewards; + } + + > *:nth-child(4) { grid-area: topsites; margin: 0 46px 0 46px; justify-self: start; } - > *:nth-child(4) { + > *:nth-child(5) { grid-area: notification; justify-self: center; } @@ -42,6 +48,7 @@ export const Header = styled<{}, 'header'>('header')` "clock clock clock" "stats stats stats" "topsites topsites topsites" + "rewards rewards rewards" "notification notification notification"; @@ -59,6 +66,11 @@ export const Header = styled<{}, 'header'>('header')` margin: auto; justify-content: center; } + + > *:nth-child(4) { + margin: auto; + justify-content: center; + } } ` export const Main = styled<{}, 'main'>('main')` diff --git a/components/brave_new_tab_ui/components/default/index.ts b/components/brave_new_tab_ui/components/default/index.ts index a60a46772ffb..82223b352736 100644 --- a/components/brave_new_tab_ui/components/default/index.ts +++ b/components/brave_new_tab_ui/components/default/index.ts @@ -9,6 +9,7 @@ import { SettingsMenu, SettingsRow, SettingsText, SettingsTitle, SettingsWrapper import { ListWidget, Tile, TileActionsContainer, TileAction, TileFavicon } from './topSites' import { SiteRemovalNotification, SiteRemovalText, SiteRemovalAction } from './notification' import { ClockWidget } from './clock' +import { RewardsWidget } from './rewards' import createWidget from './widget' export * from './page' @@ -33,5 +34,6 @@ export { SettingsText, SettingsTitle, SettingsWrapper, + RewardsWidget, createWidget } diff --git a/components/brave_new_tab_ui/components/default/rewards/index.tsx b/components/brave_new_tab_ui/components/default/rewards/index.tsx new file mode 100644 index 000000000000..f86a07ad44e5 --- /dev/null +++ b/components/brave_new_tab_ui/components/default/rewards/index.tsx @@ -0,0 +1,269 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this file, +* You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react' +import createWidget from '../widget/index' +import { convertBalance } from '../../../../brave_rewards/resources/page/utils' +import { getLocale } from '../../../../common/locale' + +import { + WidgetWrapper, + BatIcon, + RewardsTitle, + Footer, + ServiceText, + ServiceLink, + LearnMoreText, + PreOptInInfo, + Title, + SubTitle, + PreOptInAction, + TurnOnButton, + AmountItem, + AmountInformation, + AmountDescription, + Amount, + ConvertedAmount, + LearnMoreLink, + TurnOnAdsButton +} from './style' +import Notification from './notification' +import { BatColorIcon } from 'brave-ui/components/icons' + +export interface RewardsProps { + enabledAds: boolean + enabledMain: boolean + balance: NewTab.RewardsBalance + grants: NewTab.GrantRecord[] + totalContribution: string + walletCreated: boolean + walletCreating: boolean + walletCreateFailed: boolean + walletCorrupted: boolean + adsEstimatedEarnings: number + onlyAnonWallet?: boolean + onCreateWallet: () => void + onEnableAds: () => void + onEnableRewards: () => void + onDismissNotification: (id: string) => void +} + +const enum AmountItemType { + ADS = 0, + TIPS = 1 +} + +class Rewards extends React.PureComponent { + + getButtonText = (isAds: boolean = false) => { + if (isAds) { + return getLocale('rewardsWidgetTurnOnAds') + } + + const { + walletCreating, + walletCreateFailed, + walletCorrupted + } = this.props + + if (walletCreateFailed || walletCorrupted) { + return getLocale('rewardsWidgetWalletFailedButton') + } + + if (walletCreating) { + return getLocale('rewardsWidgetTurningOn') + } + + return getLocale('rewardsWidgetTurnOnRewards') + } + + optInAction = (hasEnabled: boolean) => { + if (hasEnabled) { + this.props.onEnableRewards() + } else { + this.props.onCreateWallet() + } + } + + renderPreOptIn = () => { + const { + enabledMain, + walletCreated + } = this.props + + if (enabledMain && walletCreated) { + return null + } + + const hasEnabled = !enabledMain && walletCreated + + return ( + <> + + + { + hasEnabled + ? getLocale('rewardsWidgetReEnableTitle') + : getLocale('rewardsWidgetEnableTitle') + } + + + { + hasEnabled + ? getLocale('rewardsWidgetReEnableSubTitle') + : getLocale('rewardsWidgetEnableSubTitle') + } + + + + + {this.getButtonText()} + + + + ) + } + + renderAmountItem = (type: AmountItemType) => { + const { + balance, + enabledAds, + onEnableAds, + adsEstimatedEarnings, + onlyAnonWallet, + totalContribution + } = this.props + + const rates = balance.rates || {} + const showEnableAds = type === AmountItemType.ADS && !enabledAds + const amount = type === AmountItemType.TIPS + ? totalContribution + : adsEstimatedEarnings.toFixed(1) + const converted = convertBalance(amount, rates) + const batFormatString = onlyAnonWallet ? getLocale('rewardsWidgetBatPoints') : getLocale('rewardsWidgetBat') + + return ( + + { + showEnableAds + ? + {this.getButtonText(true)} + + : null + } + { + !showEnableAds + ? + {amount} + + {`${batFormatString} ${converted} USD`} + + + : null + } + + { + type === AmountItemType.ADS + ? getLocale('rewardsWidgetEstimatedEarnings') + : getLocale('rewardsWidgetMonthlyTips') + } + + + ) + } + + renderRewardsInfo = () => { + const { + enabledMain, + walletCreated + } = this.props + + if (!enabledMain || !walletCreated) { + return null + } + + return ( +
+ {this.renderAmountItem(AmountItemType.ADS)} + {this.renderAmountItem(AmountItemType.TIPS)} +
+ ) + } + + renderLearnMore = () => { + return ( + + + {getLocale('learnMore')} + + {getLocale('rewardsWidgetAboutRewards')} + + ) + } + + renderPrivacyPolicy = () => { + return ( + <> + + {getLocale('rewardsWidgetServiceText')} {getLocale('rewardsWidgetTermsOfService')} {getLocale('rewardsWidgetAnd')} {getLocale('rewardsWidgetPrivacyPolicy')}. + + + ) + } + + renderNotifications = () => { + const { grants, onDismissNotification } = this.props + + return ( + <> + {grants.map((grant: NewTab.GrantRecord, index) => { + return ( + + ) + })} + + ) + } + + render () { + const { + enabledMain, + walletCreated + } = this.props + + return ( + + + + + + {getLocale('rewardsWidgetBraveRewards')} + + {this.renderPreOptIn()} + {this.renderRewardsInfo()} +
+ { + enabledMain && walletCreated + ? this.renderLearnMore() + : this.renderPrivacyPolicy() + } +
+ { + enabledMain + ? this.renderNotifications() + : null + } +
+ ) + } +} + +export const RewardsWidget = createWidget(Rewards) diff --git a/components/brave_new_tab_ui/components/default/rewards/notification.tsx b/components/brave_new_tab_ui/components/default/rewards/notification.tsx new file mode 100644 index 000000000000..975afaa9699b --- /dev/null +++ b/components/brave_new_tab_ui/components/default/rewards/notification.tsx @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this file, +* You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react' + +import { + Title, + SubTitle, + CloseIcon, + Content, + NotificationWrapper, + NotificationButton +} from './style' +import { CloseStrokeIcon } from 'brave-ui/components/icons' +import { getLocale } from '../../../../common/locale' + +interface NotificationProps { + grant: NewTab.GrantRecord + onDismissNotification: (id: string) => void +} + +export default class RewardsNotification extends React.PureComponent { + + dismissNotification = () => { + this.props.onDismissNotification(this.props.grant.promotionId) + } + + onNotificationAction = () => { + this.dismissNotification() + chrome.braveRewards.openBrowserActionUI(`brave_rewards_panel.html#grant_${this.props.grant.promotionId}`) + } + + render () { + return ( + + + + + + + {getLocale('rewardsWidgetNotificationTitle')} + + + { + this.props.grant.type === 'ads' + ? getLocale('rewardsWidgetNotificationTextAds') + : getLocale('rewardsWidgetNotificationTextUGP') + } + + + {getLocale('rewardsWidgetClaimMyRewards')} + + + + ) + } +} diff --git a/components/brave_new_tab_ui/components/default/rewards/style.ts b/components/brave_new_tab_ui/components/default/rewards/style.ts new file mode 100644 index 000000000000..c96a0efa1de0 --- /dev/null +++ b/components/brave_new_tab_ui/components/default/rewards/style.ts @@ -0,0 +1,151 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import styled from 'styled-components' +import palette from 'brave-ui/theme/colors' + +export const WidgetWrapper = styled<{}, 'div'>('div')` + color: white; + padding: 10px 15px; + border-radius: 6px; + position: relative; + font-family: Muli, sans-serif; + overflow-x: hidden; + background-image: linear-gradient(140deg, #392DD1 0%, #8E2995 100%); +` + +export const Footer = styled<{}, 'div'>('div')` + max-width: 275px; + margin-top: 10px; +` + +export const BatIcon = styled<{}, 'div'>('div')` + width: 30px; + height: 30px; + display: inline-block; +` + +export const RewardsTitle = styled<{}, 'span'>('span')` + top: -9px; + left: 8px; + font-size: 14px; + font-weight: 500; + position: relative; + font-family: Poppins, sans-serif; +` + +export const ServiceText = styled<{}, 'span'>('span')` + color: #fff; + font-size: 10px; + letter-spacing: 0; + line-height: 18px; +` + +export const ServiceLink = styled<{}, 'a'>('a')` + color: ${palette.blue500}; + font-weight: 600; + text-decoration: none; +` + +export const LearnMoreLink = styled(ServiceLink)` + margin-right: 5px; +` + +export const LearnMoreText = styled<{}, 'div'>('div')` + font-size: 14px; + margin-top: 50px; +` + +export const PreOptInInfo = styled<{}, 'div'>('div')` + margin-top: 20px; +` + +export const Title = styled<{}, 'span'>('span')` + font-size: 19px; + display: block; + font-family: Poppins, sans-serif; +` + +export const SubTitle = styled<{}, 'span'>('span')` + font-size: 14px; + display: block; + margin-top: 10px; + max-width: 250px; +` + +export const PreOptInAction = styled<{}, 'div'>('div')` + margin-top: 30px; + text-align: center +` + +export const TurnOnButton = styled<{}, 'button'>('button')` + border-radius: 20px; + background: white; + color: ${palette.blurple500}; + font-weight: bold; + font-size: 14px; + padding: 7px 60px; + margin: 0 auto; + cursor: pointer; +` + +export const TurnOnAdsButton = styled(TurnOnButton)` + width: 100%; + margin-bottom: 5px; + display: block; +` + +export const AmountItem = styled<{}, 'div'>('div')` + margin-top: 10px; +` + +export const AmountInformation = styled<{}, 'div'>('div')` + margin-bottom: 5px; +` + +export const Amount = styled<{}, 'span'>('span')` + font-size: 32px; + margin-right: 10px; + font-family: Poppins, sans-serif; +` + +export const ConvertedAmount = styled<{}, 'span'>('span')` + font-size: 14px; +` + +export const AmountDescription = styled<{}, 'span'>('span')` + font-size: 14px; + color: rgb(128, 158, 255); +` + +export const NotificationWrapper = styled(WidgetWrapper)` + position: absolute; + bottom: 0; + left: 0; + width: 100%; + box-shadow: 5px 10px 8px 10px #000; +` + +export const NotificationButton = styled(TurnOnButton)` + background: ${palette.green500}; + color: #fff; + border: none; + font-weight: bold; + padding-top: 10px; + padding-bottom: 10px; + margin-top: 50px; + font-size: 12px; +` + +export const Content = styled<{}, 'div'>('div')` + margin-top: 30px; +` + +export const CloseIcon = styled<{}, 'div'>('div')` + color: #fff; + width: 20px; + height: 20px; + float: right; + cursor: pointer; +` diff --git a/components/brave_new_tab_ui/constants/new_tab_types.ts b/components/brave_new_tab_ui/constants/new_tab_types.ts index 34d73066b617..aec0519ba47b 100644 --- a/components/brave_new_tab_ui/constants/new_tab_types.ts +++ b/components/brave_new_tab_ui/constants/new_tab_types.ts @@ -19,5 +19,19 @@ export const enum types { NEW_TAB_STATS_UPDATED = '@@newtab/NEW_TAB_STATS_UPDATED', NEW_TAB_PRIVATE_TAB_DATA_UPDATED = '@@newtab/NEW_TAB_PRIVATE_TAB_DATA_UPDATED', NEW_TAB_PREFERENCES_UPDATED = '@@newtab/NEW_TAB_PREFERENCES_UPDATED', - NEW_TAB_SET_INITIAL_DATA = '@@newtab/NEW_TAB_SET_INITIAL_DATA' + NEW_TAB_SET_INITIAL_DATA = '@@newtab/NEW_TAB_SET_INITIAL_DATA', + // Rewards Widget + CREATE_WALLET = '@@newtab/CREATE_WALLET', + DISMISS_NOTIFICATION = '@@newtab/DISMISS_NOTIFICATION', + ON_ADS_ENABLED = '@@newtab/ON_ADS_ENABLED', + ON_ADS_ESTIMATED_EARNINGS = '@@newtab/ON_ADS_ESTIMATED_EARNINGS', + ON_ENABLED_MAIN = '@@newtab/ON_ENABLED_MAIN', + ON_REWARDS_SETTING_SAVE = '@@newtab_panel/ON_SETTING_SAVE', + ON_WALLET_INITIALIZED = '@@newtab/ON_WALLET_INITIALIZED', + ON_BALANCE_REPORTS = '@@newtab/ON_BALANCE_REPORTS', + ON_GRANT = '@@newtab/ON_GRANT', + ON_BALANCE = '@@newtab/ON_BALANCE', + ON_WALLET_EXISTS = '@@newtab/ON_WALLET_EXISTS', + SET_INITIAL_REWARDS_DATA = '@@newtab/SET_INITIAL_REWARDS_DATA', + SET_PRE_INITIAL_REWARDS_DATA = '@@newtab/SET_PRE_INITIAL_REWARDS_DATA' } diff --git a/components/brave_new_tab_ui/containers/app.tsx b/components/brave_new_tab_ui/containers/app.tsx index a9255855f2df..dd00465968a6 100644 --- a/components/brave_new_tab_ui/containers/app.tsx +++ b/components/brave_new_tab_ui/containers/app.tsx @@ -39,6 +39,7 @@ class DefaultPage extends React.Component { saveShowClock={PreferencesAPI.saveShowClock} saveShowStats={PreferencesAPI.saveShowStats} saveShowTopSites={PreferencesAPI.saveShowTopSites} + saveShowRewards={PreferencesAPI.saveShowRewards} /> ) } diff --git a/components/brave_new_tab_ui/containers/newTab/footerInfo.tsx b/components/brave_new_tab_ui/containers/newTab/footerInfo.tsx index a620804ae6d8..3d45d9ef6027 100644 --- a/components/brave_new_tab_ui/containers/newTab/footerInfo.tsx +++ b/components/brave_new_tab_ui/containers/newTab/footerInfo.tsx @@ -25,10 +25,12 @@ interface Props { toggleShowClock: () => void toggleShowStats: () => void toggleShowTopSites: () => void + toggleShowRewards: () => void showBackgroundImage: boolean showClock: boolean showStats: boolean showTopSites: boolean + showRewards: boolean } export default class FooterInfo extends React.PureComponent { @@ -48,7 +50,9 @@ export default class FooterInfo extends React.PureComponent { showBackgroundImage, showClock, showStats, - showTopSites + showTopSites, + toggleShowRewards, + showRewards } = this.props return ( @@ -76,6 +80,8 @@ export default class FooterInfo extends React.PureComponent { showClock={showClock} showStats={showStats} showTopSites={showTopSites} + toggleShowRewards={toggleShowRewards} + showRewards={showRewards} /> diff --git a/components/brave_new_tab_ui/containers/newTab/index.tsx b/components/brave_new_tab_ui/containers/newTab/index.tsx index 4f78d19f94e2..f8b2cd478b75 100644 --- a/components/brave_new_tab_ui/containers/newTab/index.tsx +++ b/components/brave_new_tab_ui/containers/newTab/index.tsx @@ -13,7 +13,8 @@ import { Footer, App, PosterBackground, - Gradient + Gradient, + RewardsWidget as Rewards } from '../../components/default' // Components @@ -29,15 +30,18 @@ interface Props { saveShowClock: (value: boolean) => void saveShowTopSites: (value: boolean) => void saveShowStats: (value: boolean) => void + saveShowRewards: (value: boolean) => void } interface State { + onlyAnonWallet: boolean showSettingsMenu: boolean backgroundHasLoaded: boolean } class NewTabPage extends React.Component { state = { + onlyAnonWallet: false, showSettingsMenu: false, backgroundHasLoaded: false } @@ -128,6 +132,28 @@ class NewTabPage extends React.Component { ) } + toggleShowRewards = () => { + this.props.saveShowRewards( + !this.props.newTabData.showRewards + ) + } + + enableAds = () => { + chrome.braveRewards.saveAdsSetting('adsEnabled', 'true') + } + + enableRewards = () => { + this.props.actions.onRewardsSettingSave('enabledMain', '1') + } + + createWallet = () => { + this.props.actions.createWallet() + } + + dismissNotification = (id: string) => { + this.props.actions.dismissNotification(id) + } + closeSettings = () => { this.setState({ showSettingsMenu: false }) } @@ -139,6 +165,7 @@ class NewTabPage extends React.Component { render () { const { newTabData, actions } = this.props const { showSettingsMenu } = this.state + const { rewardsState } = newTabData if (!newTabData) { return null @@ -174,6 +201,17 @@ class NewTabPage extends React.Component { hideWidget={this.toggleShowClock} menuPosition={'left'} /> + {this.props.newTabData.gridSites.length ? { showClock={newTabData.showClock} showStats={newTabData.showStats} showTopSites={newTabData.showTopSites} + showRewards={newTabData.showRewards} + toggleShowRewards={this.toggleShowRewards} /> diff --git a/components/brave_new_tab_ui/containers/newTab/settings.tsx b/components/brave_new_tab_ui/containers/newTab/settings.tsx index 812a5902e346..f5298420ded4 100644 --- a/components/brave_new_tab_ui/containers/newTab/settings.tsx +++ b/components/brave_new_tab_ui/containers/newTab/settings.tsx @@ -20,10 +20,12 @@ export interface Props { toggleShowClock: () => void toggleShowStats: () => void toggleShowTopSites: () => void + toggleShowRewards: () => void showBackgroundImage: boolean showStats: boolean showClock: boolean showTopSites: boolean + showRewards: boolean } export default class Settings extends React.PureComponent { @@ -61,10 +63,12 @@ export default class Settings extends React.PureComponent { toggleShowClock, toggleShowStats, toggleShowTopSites, + toggleShowRewards, showBackgroundImage, showStats, showClock, showTopSites, + showRewards, onClick } = this.props return ( @@ -105,6 +109,14 @@ export default class Settings extends React.PureComponent { size='small' /> + + {getLocale('showRewards')} + + } diff --git a/components/brave_new_tab_ui/reducers/new_tab_reducer.tsx b/components/brave_new_tab_ui/reducers/new_tab_reducer.tsx index f28bcdbc7fb4..0fdead5bc76c 100644 --- a/components/brave_new_tab_ui/reducers/new_tab_reducer.tsx +++ b/components/brave_new_tab_ui/reducers/new_tab_reducer.tsx @@ -13,10 +13,11 @@ import { PrivateTabData } from '../api/privateTabData' // API import * as backgroundAPI from '../api/background' import * as gridAPI from '../api/topSites/grid' -import { InitialData } from '../api/initialData' +import { InitialData, InitialRewardsData, PreInitialRewardsData } from '../api/initialData' import * as bookmarksAPI from '../api/topSites/bookmarks' import * as dndAPI from '../api/topSites/dnd' import * as storage from '../storage' +import { getTotalContributions } from '../rewards-utils' const initialState = storage.load() @@ -246,6 +247,158 @@ export const newTabReducer: Reducer = (state: NewTab.S } break + case types.CREATE_WALLET: + chrome.braveRewards.createWallet() + state = { ...state } + state.rewardsState.walletCreating = true + break + + case types.ON_ENABLED_MAIN: + state = { ...state } + state.rewardsState.enabledMain = payload.enabledMain + break + + case types.ON_WALLET_INITIALIZED: { + const result: NewTab.RewardsResult = payload.result + state = { ...state } + + switch (result) { + case NewTab.RewardsResult.WALLET_CORRUPT: + state.rewardsState.walletCorrupted = true + break + case NewTab.RewardsResult.WALLET_CREATED: + state.rewardsState.walletCreated = true + state.rewardsState.walletCreateFailed = false + state.rewardsState.walletCreating = false + state.rewardsState.walletCorrupted = false + chrome.braveRewards.saveAdsSetting('adsEnabled', 'true') + break + case NewTab.RewardsResult.LEDGER_OK: + state.rewardsState.walletCreateFailed = true + state.rewardsState.walletCreating = false + state.rewardsState.walletCreated = false + state.rewardsState.walletCorrupted = false + break + } + break + } + + case types.ON_REWARDS_SETTING_SAVE: + const key = action.payload.key + const value = action.payload.value + + if (key) { + state = { ...state } + state.rewardsState[key] = !!value + chrome.braveRewards.saveSetting(key, value) + } + break + + case types.ON_ADS_ENABLED: + state = { ...state } + state.rewardsState.enabledAds = payload.enabled + break + + case types.ON_ADS_ESTIMATED_EARNINGS: + state = { ...state } + state.rewardsState.adsEstimatedEarnings = payload.amount + break + + case types.ON_BALANCE_REPORTS: + state = { ...state } + const reports = payload.reports || {} + state.rewardsState.totalContribution = getTotalContributions(reports) + break + + case types.DISMISS_NOTIFICATION: + state = { ...state } + + const dismissedNotifications = state.rewardsState.dismissedNotifications + dismissedNotifications.push(payload.id) + state.rewardsState.dismissedNotifications = dismissedNotifications + + state.rewardsState.grants = state.rewardsState.grants.filter((grant) => { + return grant.promotionId !== payload.id + }) + break + + case types.ON_GRANT: + if (action.payload.properties.status === 1) { + break + } + + const promotionId = payload.properties.promotionId + if (!promotionId) { + break + } + + state = { ...state } + + if (!state.rewardsState.dismissedNotifications) { + state.rewardsState.dismissedNotifications = [] + } + + if (state.rewardsState.dismissedNotifications.indexOf(promotionId) > -1) { + break + } + + const hasGrant = state.rewardsState.grants.find((grant: NewTab.GrantRecord) => { + return grant.promotionId === promotionId + }) + if (hasGrant) { + break + } + + const updatedGrants = state.rewardsState.grants + updatedGrants.push({ + promotionId: promotionId, + type: payload.properties.type + }) + + state.rewardsState.grants = updatedGrants + break + + case types.ON_BALANCE: + state = { ...state } + state.rewardsState.balance = payload.balance + break + + case types.ON_WALLET_EXISTS: + if (!payload.exists || state.rewardsState.walletCreated) { + break + } + state.rewardsState.walletCreated = true + break + + case types.SET_PRE_INITIAL_REWARDS_DATA: + const preInitialRewardsDataPayload = payload as PreInitialRewardsData + state = { + ...state, + rewardsState: { + ...state.rewardsState, + enabledAds: preInitialRewardsDataPayload.enabledAds, + enabledMain: preInitialRewardsDataPayload.enabledMain + } + } + break + + case types.SET_INITIAL_REWARDS_DATA: + const initialRewardsDataPayload = payload as InitialRewardsData + const newRewardsState = { + onlyAnonWallet: initialRewardsDataPayload.onlyAnonWallet, + balance: initialRewardsDataPayload.balance, + totalContribution: getTotalContributions(initialRewardsDataPayload.reports) + } + + state = { + ...state, + rewardsState: { + ...state.rewardsState, + ...newRewardsState + } + } + break + default: break } diff --git a/components/brave_new_tab_ui/rewards-utils.ts b/components/brave_new_tab_ui/rewards-utils.ts new file mode 100644 index 000000000000..109e57323c36 --- /dev/null +++ b/components/brave_new_tab_ui/rewards-utils.ts @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { tipsTotal } from '../brave_rewards/resources/page/utils' + +export const getTotalContributions = (reports: Record) => { + const currentTime = new Date() + const year = currentTime.getFullYear() + const month = (currentTime.getMonth() + 1) + const report: NewTab.RewardsReport = reports[`${year}_${month}`] + + if (!report) { + return '0.0' + } + + return tipsTotal(report) +} diff --git a/components/brave_new_tab_ui/storage.ts b/components/brave_new_tab_ui/storage.ts index 1d1abb9a609f..c24a8002d5a1 100644 --- a/components/brave_new_tab_ui/storage.ts +++ b/components/brave_new_tab_ui/storage.ts @@ -14,6 +14,7 @@ const defaultState: NewTab.State = { showStats: false, showClock: false, showTopSites: false, + showRewards: false, topSites: [], ignoredTopSites: [], pinnedTopSites: [], @@ -29,6 +30,24 @@ const defaultState: NewTab.State = { javascriptBlockedStat: 0, httpsUpgradesStat: 0, fingerprintingBlockedStat: 0 + }, + rewardsState: { + adsEstimatedEarnings: 0, + balance: { + total: 0, + rates: {}, + wallets: {} + }, + dismissedNotifications: [], + enabledAds: false, + enabledMain: false, + grants: [], + onlyAnonWallet: false, + totalContribution: '0.0', + walletCreated: false, + walletCreating: false, + walletCreateFailed: false, + walletCorrupted: false } } @@ -46,7 +65,8 @@ const getPersistentData = (state: NewTab.State): NewTab.PersistentState => { pinnedTopSites: state.pinnedTopSites, gridSites: state.gridSites, showEmptyPage: state.showEmptyPage, - bookmarks: state.bookmarks + bookmarks: state.bookmarks, + rewardsState: state.rewardsState } return peristantState diff --git a/components/brave_new_tab_ui/stories/default/footerInfo.tsx b/components/brave_new_tab_ui/stories/default/footerInfo.tsx index 7fac03da21de..823813fd2c95 100644 --- a/components/brave_new_tab_ui/stories/default/footerInfo.tsx +++ b/components/brave_new_tab_ui/stories/default/footerInfo.tsx @@ -25,10 +25,12 @@ interface Props { toggleShowClock: () => void toggleShowStats: () => void toggleShowTopSites: () => void + toggleShowRewards: () => void showBackgroundImage: boolean showClock: boolean showStats: boolean showTopSites: boolean + showRewards: boolean } export default class FooterInfo extends React.PureComponent { @@ -45,10 +47,12 @@ export default class FooterInfo extends React.PureComponent { toggleShowClock, toggleShowStats, toggleShowTopSites, + toggleShowRewards, showBackgroundImage, showClock, showStats, - showTopSites + showTopSites, + showRewards } = this.props return ( @@ -72,10 +76,12 @@ export default class FooterInfo extends React.PureComponent { toggleShowClock={toggleShowClock} toggleShowStats={toggleShowStats} toggleShowTopSites={toggleShowTopSites} + toggleShowRewards={toggleShowRewards} showBackgroundImage={showBackgroundImage} showClock={showClock} showStats={showStats} showTopSites={showTopSites} + showRewards={showRewards} /> diff --git a/components/brave_new_tab_ui/stories/default/index.tsx b/components/brave_new_tab_ui/stories/default/index.tsx index 8443bf58be18..9aa1e1783bb6 100644 --- a/components/brave_new_tab_ui/stories/default/index.tsx +++ b/components/brave_new_tab_ui/stories/default/index.tsx @@ -5,7 +5,7 @@ import * as React from 'react' // Feature-specific components -import { Page, Header, Footer, App, PosterBackground, Gradient, ClockWidget as Clock } from '../../components/default' +import { Page, Header, Footer, App, PosterBackground, Gradient, ClockWidget as Clock, RewardsWidget as Rewards } from '../../components/default' import TopSitesList from './topSites/topSitesList' import Stats from './stats' @@ -23,6 +23,17 @@ interface State { showStats: boolean showClock: boolean showTopSites: boolean + showRewards: boolean + adsEstimatedEarnings: number + balance: NewTab.RewardsBalance + grants: NewTab.GrantRecord[] + enabledAds: boolean + enabledMain: boolean + totalContribution: string + walletCreated: boolean + walletCreating: boolean + walletCreateFailed: boolean + walletCorrupted: boolean } interface Props { @@ -37,10 +48,29 @@ export default class NewTabPage extends React.PureComponent { showBackgroundImage: true, showStats: true, showClock: true, - showTopSites: true + showTopSites: true, + showRewards: true, + adsEstimatedEarnings: 5, + enabledAds: false, + enabledMain: false, + grants: [], + balance: { + total: 0, + rates: {}, + wallets: {} + }, + totalContribution: '0.0', + walletCreated: false, + walletCreating: false, + walletCreateFailed: false, + walletCorrupted: false } } + doNothing = (s: string) => { + /* no-op */ + } + toggleShowBackgroundImage = () => { this.setState({ showBackgroundImage: !this.state.showBackgroundImage }) } @@ -57,6 +87,10 @@ export default class NewTabPage extends React.PureComponent { this.setState({ showTopSites: !this.state.showTopSites }) } + toggleShowRewards = () => { + this.setState({ showRewards: !this.state.showRewards }) + } + closeSettings = () => { this.setState({ showSettingsMenu: false }) } @@ -65,9 +99,39 @@ export default class NewTabPage extends React.PureComponent { this.setState({ showSettingsMenu: !this.state.showSettingsMenu }) } + enableAds = () => { + this.setState({ enabledAds: true }) + } + + enableRewards = () => { + this.setState({ enabledMain: true }) + } + + createWallet = () => { + this.setState({ walletCreating: true }) + setTimeout(() => { + this.setState({ walletCreated: true }) + this.enableAds() + this.enableRewards() + }, 1000) + } + render () { - const { showSettingsMenu, showBackgroundImage, showClock, showStats, showTopSites } = this.state + const { showSettingsMenu, showBackgroundImage, showClock, showStats, showTopSites, showRewards } = this.state + const { + enabledAds, + enabledMain, + adsEstimatedEarnings, + walletCorrupted, + walletCreateFailed, + walletCreated, + walletCreating, + grants, + balance, + totalContribution + } = this.state const { textDirection } = this.props + return ( @@ -94,6 +158,26 @@ export default class NewTabPage extends React.PureComponent { menuPosition={'right'} hideWidget={this.toggleShowTopSites} /> +
@@ -108,10 +192,12 @@ export default class NewTabPage extends React.PureComponent { toggleShowClock={this.toggleShowClock} toggleShowStats={this.toggleShowStats} toggleShowTopSites={this.toggleShowTopSites} + toggleShowRewards={this.toggleShowRewards} showBackgroundImage={showBackgroundImage} showClock={showClock} showStats={showStats} showTopSites={showTopSites} + showRewards={showRewards} />
diff --git a/components/brave_new_tab_ui/stories/default/settings.tsx b/components/brave_new_tab_ui/stories/default/settings.tsx index a785a902f281..71dbb404b115 100644 --- a/components/brave_new_tab_ui/stories/default/settings.tsx +++ b/components/brave_new_tab_ui/stories/default/settings.tsx @@ -20,10 +20,12 @@ interface Props { toggleShowClock: () => void toggleShowStats: () => void toggleShowTopSites: () => void + toggleShowRewards: () => void showBackgroundImage: boolean showStats: boolean showClock: boolean showTopSites: boolean + showRewards: boolean } export default class Settings extends React.PureComponent { @@ -61,10 +63,12 @@ export default class Settings extends React.PureComponent { toggleShowClock, toggleShowStats, toggleShowTopSites, + toggleShowRewards, showBackgroundImage, showStats, showClock, showTopSites, + showRewards, onClick } = this.props return ( @@ -105,6 +109,14 @@ export default class Settings extends React.PureComponent { size='small' /> + + {getLocale('showRewards')} + + } diff --git a/components/brave_new_tab_ui/stories/fakeLocale.ts b/components/brave_new_tab_ui/stories/fakeLocale.ts index d38beffeaa43..49206e2f7d5a 100644 --- a/components/brave_new_tab_ui/stories/fakeLocale.ts +++ b/components/brave_new_tab_ui/stories/fakeLocale.ts @@ -20,7 +20,8 @@ const locale: any = { showBackgroundImg: 'Show background image', showBraveStats: 'Show Brave Stats', showClock: 'Show Clock', - showTopSites: 'Show Top Sites' + showTopSites: 'Show Top Sites', + showRewards: 'Show Rewards' } export default locale diff --git a/components/brave_rewards/browser/extension_rewards_service_observer.cc b/components/brave_rewards/browser/extension_rewards_service_observer.cc index fa543e6f6aee..f62378a9e351 100644 --- a/components/brave_rewards/browser/extension_rewards_service_observer.cc +++ b/components/brave_rewards/browser/extension_rewards_service_observer.cc @@ -222,6 +222,24 @@ void ExtensionRewardsServiceObserver::OnGrantFinish( event_router->BroadcastEvent(std::move(event)); } +void ExtensionRewardsServiceObserver::OnAdsEnabled( + RewardsService* rewards_service, + bool ads_enabled) { + auto* event_router = extensions::EventRouter::Get(profile_); + if (!event_router) { + return; + } + + std::unique_ptr args( + extensions::api::brave_rewards::OnAdsEnabled::Create( + ads_enabled).release()); + std::unique_ptr event(new extensions::Event( + extensions::events::BRAVE_START, + extensions::api::brave_rewards::OnAdsEnabled::kEventName, + std::move(args))); + event_router->BroadcastEvent(std::move(event)); +} + void ExtensionRewardsServiceObserver::OnRewardsMainEnabled( RewardsService* rewards_service, bool rewards_main_enabled) { diff --git a/components/brave_rewards/browser/extension_rewards_service_observer.h b/components/brave_rewards/browser/extension_rewards_service_observer.h index 1e9e51ced7ac..70193b5f4991 100644 --- a/components/brave_rewards/browser/extension_rewards_service_observer.h +++ b/components/brave_rewards/browser/extension_rewards_service_observer.h @@ -83,6 +83,9 @@ class ExtensionRewardsServiceObserver : public RewardsServiceObserver, void OnPendingContributionSaved(RewardsService* rewards_service, int result) override; + void OnAdsEnabled(RewardsService* rewards_service, + bool ads_enabled) override; + private: Profile* profile_; diff --git a/components/brave_rewards/browser/rewards_service.h b/components/brave_rewards/browser/rewards_service.h index 87aaaa249cfa..7b3111befb4c 100644 --- a/components/brave_rewards/browser/rewards_service.h +++ b/components/brave_rewards/browser/rewards_service.h @@ -214,6 +214,7 @@ class RewardsService : public KeyedService { RewardsServicePrivateObserver* observer) = 0; virtual void GetTransactionHistory( GetTransactionHistoryCallback callback) = 0; + virtual void TriggerOnAdsEnabled(bool ads_enabled) = 0; virtual void RefreshPublisher( const std::string& publisher_key, diff --git a/components/brave_rewards/browser/rewards_service_browsertest.cc b/components/brave_rewards/browser/rewards_service_browsertest.cc index e1830d9b87e6..2bef25a4118c 100644 --- a/components/brave_rewards/browser/rewards_service_browsertest.cc +++ b/components/brave_rewards/browser/rewards_service_browsertest.cc @@ -846,6 +846,11 @@ class BraveRewardsBrowserTest return rewards_url; } + GURL new_tab_url() { + GURL new_tab_url("brave://newtab"); + return new_tab_url; + } + GURL uphold_auth_url() { GURL url("chrome://rewards/uphold/authorization?" "code=0c42b34121f624593ee3b04cbe4cc6ddcd72d&state=123456789"); @@ -856,9 +861,10 @@ class BraveRewardsBrowserTest return browser()->tab_strip_model()->GetActiveWebContents(); } - void EnableRewards() { + void EnableRewards(bool use_new_tab = false) { // Load rewards page - ui_test_utils::NavigateToURL(browser(), rewards_url()); + GURL page_url = use_new_tab ? new_tab_url() : rewards_url(); + ui_test_utils::NavigateToURL(browser(), page_url); WaitForLoadStop(contents()); // Opt in and create wallet to enable rewards ASSERT_TRUE(ExecJs(contents(), @@ -2959,6 +2965,12 @@ IN_PROC_BROWSER_TEST_F(BraveRewardsBrowserTest, EXPECT_FALSE(is_showing_notification); } +IN_PROC_BROWSER_TEST_F( + BraveRewardsBrowserTest, + NewTabPageWidgetEnableRewards) { + EnableRewards(true); +} + struct BraveAdsUpgradePathParamInfo { // |preferences| should be set to the name of the preferences filename located // at "src/brave/test/data/rewards-data/migration" diff --git a/components/brave_rewards/browser/rewards_service_impl.cc b/components/brave_rewards/browser/rewards_service_impl.cc index 876de7014011..b4983074bd51 100644 --- a/components/brave_rewards/browser/rewards_service_impl.cc +++ b/components/brave_rewards/browser/rewards_service_impl.cc @@ -2080,6 +2080,12 @@ void RewardsServiceImpl::SetAutoContribute(bool enabled) { } } +void RewardsServiceImpl::TriggerOnAdsEnabled( + bool ads_enabled) { + for (auto& observer : observers_) + observer.OnAdsEnabled(this, ads_enabled); +} + void RewardsServiceImpl::TriggerOnRewardsMainEnabled( bool rewards_main_enabled) { for (auto& observer : observers_) diff --git a/components/brave_rewards/browser/rewards_service_impl.h b/components/brave_rewards/browser/rewards_service_impl.h index 8486773f9052..b505def750e0 100644 --- a/components/brave_rewards/browser/rewards_service_impl.h +++ b/components/brave_rewards/browser/rewards_service_impl.h @@ -216,6 +216,7 @@ class RewardsServiceImpl : public RewardsService, void RefreshPublisher( const std::string& publisher_key, RefreshPublisherCallback callback) override; + void TriggerOnAdsEnabled(bool ads_enabled) override; void OnSaveRecurringTipUI( SaveRecurringTipCallback callback, diff --git a/components/brave_rewards/browser/rewards_service_impl_unittest.cc b/components/brave_rewards/browser/rewards_service_impl_unittest.cc index 1e327565efd1..88c6994eac9e 100644 --- a/components/brave_rewards/browser/rewards_service_impl_unittest.cc +++ b/components/brave_rewards/browser/rewards_service_impl_unittest.cc @@ -51,6 +51,7 @@ class MockRewardsServiceObserver : public RewardsServiceObserver { void(RewardsService*, const brave_rewards::PublisherBanner)); MOCK_METHOD4(OnPanelPublisherInfo, void(RewardsService*, int, ledger::PublisherInfoPtr, uint64_t)); + MOCK_METHOD2(OnAdsEnabled, void(RewardsService*, bool)); }; class RewardsServiceTest : public testing::Test { diff --git a/components/brave_rewards/browser/rewards_service_observer.h b/components/brave_rewards/browser/rewards_service_observer.h index 88f79ac82806..9c76308679bc 100644 --- a/components/brave_rewards/browser/rewards_service_observer.h +++ b/components/brave_rewards/browser/rewards_service_observer.h @@ -61,6 +61,9 @@ class RewardsServiceObserver : public base::CheckedObserver { const std::string& viewing_id, const std::string& probi, const int32_t type) {} + virtual void OnAdsEnabled( + brave_rewards::RewardsService* rewards_service, + bool ads_enabled) {} virtual void OnRewardsMainEnabled( brave_rewards::RewardsService* rewards_service, bool rewards_main_enabled) {} diff --git a/components/brave_rewards/resources/extension/brave_rewards/components/app.tsx b/components/brave_rewards/resources/extension/brave_rewards/components/app.tsx index 9f0b7b3affe9..a421fe3159b8 100644 --- a/components/brave_rewards/resources/extension/brave_rewards/components/app.tsx +++ b/components/brave_rewards/resources/extension/brave_rewards/components/app.tsx @@ -66,6 +66,8 @@ export class RewardsPanel extends React.Component { const { externalWallet } = this.props.rewardsPanelData utils.getExternalWallet(this.actions, externalWallet) } + + this.handleGrantNotification() } componentDidUpdate (prevProps: Props, prevState: State) { @@ -80,6 +82,20 @@ export class RewardsPanel extends React.Component { } } + handleGrantNotification = () => { + const hash = window && window.location && window.location.hash + + if (!hash) { + return + } + + if (!hash.startsWith('#grant_')) { + return + } + + this.actions.getGrantCaptcha(hash.split('#grant_')[1]) + } + goToUphold = () => { const { externalWallet } = this.props.rewardsPanelData diff --git a/components/definitions/chromel.d.ts b/components/definitions/chromel.d.ts index 8d3ed845ba20..1013f54f8f20 100644 --- a/components/definitions/chromel.d.ts +++ b/components/definitions/chromel.d.ts @@ -94,10 +94,17 @@ declare namespace chrome.braveRewards { const solveGrantCaptcha: (solution: string, promotionId: string) => {} const getPendingContributionsTotal: (callback: (amount: number) => void) => {} const getNonVerifiedSettings: (callback: (nonVerified: boolean) => void) => {} + const onAdsEnabled: { + addListener: (callback: (enabled: boolean) => void) => void + } const onEnabledMain: { addListener: (callback: (enabledMain: boolean) => void) => void } + const getAdsEnabled: (callback: (enabled: boolean) => void) => {} + const getBalanceReports: (callback: (reports: Record) => void) => {} + const getAdsEstimatedEarnings: (callback: (amount: number) => void) => {} const getRewardsMainEnabled: (callback: (enabled: boolean) => void) => {} + const getWalletExists: (callback: (exists: boolean) => void) => {} const saveAdsSetting: (key: string, value: string) => {} const onPendingContributionSaved: { addListener: (callback: (result: number) => void) => void @@ -137,6 +144,8 @@ declare namespace chrome.braveRewards { } const onlyAnonWallet: (callback: (only: boolean) => void) => {} + + const openBrowserActionUI: (path?: string) => {} } declare namespace chrome.rewardsNotifications { diff --git a/components/definitions/newTab.d.ts b/components/definitions/newTab.d.ts index 54b6079fc071..3380caea75e3 100644 --- a/components/definitions/newTab.d.ts +++ b/components/definitions/newTab.d.ts @@ -46,6 +46,7 @@ declare namespace NewTab { gridSites: Site[] showEmptyPage: boolean bookmarks: Record + rewardsState: RewardsWidgetState } export interface EphemeralState { @@ -62,9 +63,72 @@ declare namespace NewTab { showStats: boolean showClock: boolean showTopSites: boolean + showRewards: boolean stats: Stats } + export interface RewardsWidgetState { + adsEstimatedEarnings: number + balance: RewardsBalance + dismissedNotifications: string[] + enabledAds: boolean + enabledMain: boolean + grants: GrantRecord[] + onlyAnonWallet: boolean + totalContribution: string + walletCreated: boolean + walletCreating: boolean + walletCreateFailed: boolean + walletCorrupted: boolean + } + + export const enum RewardsResult { + LEDGER_OK = 0, + LEDGER_ERROR = 1, + NO_PUBLISHER_STATE = 2, + NO_LEDGER_STATE = 3, + INVALID_PUBLISHER_STATE = 4, + INVALID_LEDGER_STATE = 5, + CAPTCHA_FAILED = 6, + NO_PUBLISHER_LIST = 7, + TOO_MANY_RESULTS = 8, + NOT_FOUND = 9, + REGISTRATION_VERIFICATION_FAILED = 10, + BAD_REGISTRATION_RESPONSE = 11, + WALLET_CREATED = 12, + GRANT_NOT_FOUND = 13, + WALLET_CORRUPT = 17 + } + + export interface RewardsReport { + ads: string + closing: string + contribute: string + deposit: string + donation: string + grant: string + tips: string + opening: string + total: string + } + + export interface GrantResponse { + promotionId?: string + status?: number + type?: string + } + + export interface GrantRecord { + type: string + promotionId: string + } + + export interface RewardsBalance { + total: number + rates: Record + wallets: Record + } + // In-memory state is a superset of PersistentState export type State = PersistentState & EphemeralState } diff --git a/components/resources/brave_components_strings.grd b/components/resources/brave_components_strings.grd index b5f7df14d0c6..6a33016fda0c 100644 --- a/components/resources/brave_components_strings.grd +++ b/components/resources/brave_components_strings.grd @@ -142,6 +142,7 @@ Show Brave Stats Show Clock Show Top Sites + Show Rewards Learn more @@ -537,7 +538,7 @@ tokens Total Transactions - Turn on Ads + Turn on Brave Ads This enables both ads and automatic contributions. You can turn them on or off separately at any time. Activate Rewards Tweet @@ -616,6 +617,22 @@ Uphold may require you to verify your identity based on services requested. Brave Software Inc. does not process, store, or access any of the personal information that you provide to Uphold when you establish an account with them. + + about Brave Rewards + By clicking 'Turn on Rewards', you indicate that you have read and agree to the + Estimated earnings so far this month + Tips and contributions this month + Claim my Rewards + Turning on... + Turn on Rewards + Feeling Generous? + Ready to start earning? + Try Brave Rewards again and pay it forward as you earn! + Turn on Brave Rewards and get paid to view privacy respecting ads. + Way to Go! + Your rewards from Ads are here + You have a UGP grant from Brave. + Done From 7c27e4598e5508966c6530cdb4094c38a83c970e Mon Sep 17 00:00:00 2001 From: Pete Miller Date: Tue, 29 Oct 2019 14:09:35 -0700 Subject: [PATCH 2/2] New Tab Page WebUI: fixed position background (and gradient overlay) so that scroll is a nicer UX --- browser/ui/webui/brave_webui_source.cc | 2 +- .../brave_ads/browser/ads_service_impl.cc | 2 +- .../browser/ads_service_impl_unittest.cc | 2 +- .../brave_new_tab_ui/apiEventsToStore.ts | 21 ++++++++++++------- .../components/default/grid/index.ts | 1 + .../components/default/page/index.ts | 4 ++-- .../components/default/rewards/style.ts | 6 +++--- .../containers/newTab/index.tsx | 2 +- .../containers/newTab/settings.tsx | 16 +++++++------- .../brave_rewards/browser/rewards_service.h | 2 +- .../browser/rewards_service_impl.cc | 2 +- .../browser/rewards_service_impl.h | 2 +- .../resources/brave_components_strings.grd | 2 +- 13 files changed, 36 insertions(+), 28 deletions(-) diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index b4e130801f21..264bef764226 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -201,7 +201,7 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "rewardsWidgetTermsOfService", IDS_BRAVE_UI_TERMS_OF_SERVICE }, { "rewardsWidgetTurnOnAds", IDS_BRAVE_UI_TURN_ON_ADS }, { "rewardsWidgetClaimMyRewards", IDS_REWARDS_WIDGET_CLAIM_MY_REWARDS }, - { "rewardsWidgetWalletFailedButton", IDS_BRAVE_UI_WALLET_FAILED_BUTTON }, + { "rewardsWidgetWalletFailedButton", IDS_BRAVE_UI_WALLET_FAILED_BUTTON }, // NOLINT { "rewardsWidgetAboutRewards", IDS_REWARDS_WIDGET_ABOUT_REWARDS }, { "rewardsWidgetServiceText", IDS_REWARDS_WIDGET_SERVICE_TEXT }, { "rewardsWidgetEstimatedEarnings", IDS_REWARDS_WIDGET_ESTIMATED_EARNINGS }, // NOLINT diff --git a/components/brave_ads/browser/ads_service_impl.cc b/components/brave_ads/browser/ads_service_impl.cc index e6eebd4860b3..95a68200f361 100644 --- a/components/brave_ads/browser/ads_service_impl.cc +++ b/components/brave_ads/browser/ads_service_impl.cc @@ -411,7 +411,7 @@ bool AdsServiceImpl::IsSupportedRegion() const { void AdsServiceImpl::SetEnabled( const bool is_enabled) { SetBooleanPref(prefs::kEnabled, is_enabled); - rewards_service_->TriggerOnAdsEnabled(is_enabled); + rewards_service_->OnAdsEnabled(is_enabled); } void AdsServiceImpl::SetAdsPerHour( diff --git a/components/brave_ads/browser/ads_service_impl_unittest.cc b/components/brave_ads/browser/ads_service_impl_unittest.cc index 2fc1bdf2a8a1..258c4d299e53 100644 --- a/components/brave_ads/browser/ads_service_impl_unittest.cc +++ b/components/brave_ads/browser/ads_service_impl_unittest.cc @@ -144,7 +144,7 @@ class MockRewardsService : public RewardsService { brave_rewards::SaveRecurringTipCallback)); MOCK_METHOD2(RefreshPublisher, void(const std::string&, brave_rewards::RefreshPublisherCallback)); - MOCK_METHOD1(TriggerOnAdsEnabled, void(bool)); + MOCK_METHOD1(OnAdsEnabled, void(bool)); MOCK_METHOD0(GetAllNotifications, const brave_rewards::RewardsNotificationService::RewardsNotificationsMap&()); MOCK_METHOD3(SaveInlineMediaInfo, diff --git a/components/brave_new_tab_ui/apiEventsToStore.ts b/components/brave_new_tab_ui/apiEventsToStore.ts index 88b31d1249ae..f61e3a3162ff 100644 --- a/components/brave_new_tab_ui/apiEventsToStore.ts +++ b/components/brave_new_tab_ui/apiEventsToStore.ts @@ -35,9 +35,7 @@ export function wireApiEventsToStore () { .then((initialData) => { if (initialData.preferences.showRewards) { rewardsInitData() - window.setInterval(() => { - fetchCreatedWalletData() - }, 30000) + setRewardsFetchInterval() } getActions().setInitialData(initialData) // Listen for API changes and dispatch to store @@ -56,11 +54,10 @@ export function rewardsInitData () { .then((preInitialRewardsData) => { getActions().setPreInitialRewardsData(preInitialRewardsData) chrome.braveRewards.getWalletExists((exists: boolean) => { - if (!exists) { - return + if (exists) { + fetchCreatedWalletData() + getActions().onWalletExists(exists) } - fetchCreatedWalletData() - getActions().onWalletExists(exists) }) }) .catch(e => { @@ -68,6 +65,16 @@ export function rewardsInitData () { }) } +function setRewardsFetchInterval () { + window.setInterval(() => { + chrome.braveRewards.getWalletExists((exists: boolean) => { + if (exists) { + fetchCreatedWalletData() + } + }) + }, 30000) +} + function fetchCreatedWalletData () { getRewardsInitialData() .then((initialRewardsData) => { diff --git a/components/brave_new_tab_ui/components/default/grid/index.ts b/components/brave_new_tab_ui/components/default/grid/index.ts index 03c09b2c3eb7..16ec3338d45e 100644 --- a/components/brave_new_tab_ui/components/default/grid/index.ts +++ b/components/brave_new_tab_ui/components/default/grid/index.ts @@ -35,6 +35,7 @@ export const Header = styled<{}, 'header'>('header')` grid-area: topsites; margin: 0 46px 0 46px; justify-self: start; + align-items: normal; } > *:nth-child(5) { diff --git a/components/brave_new_tab_ui/components/default/page/index.ts b/components/brave_new_tab_ui/components/default/page/index.ts index bcbb68b99801..a91731ef2461 100644 --- a/components/brave_new_tab_ui/components/default/page/index.ts +++ b/components/brave_new_tab_ui/components/default/page/index.ts @@ -40,7 +40,7 @@ export const App = styled('div')` ` export const PosterBackground = styled('div')` - position: absolute; + position: fixed; top: 0; bottom: 0; left: 0; @@ -70,7 +70,7 @@ export const Gradient = styled('div')` rgba(0, 0, 0, 0.6) 100% ); z-index: 2; - position: absolute; + position: fixed; top: 0; left: 0; width: 100%; diff --git a/components/brave_new_tab_ui/components/default/rewards/style.ts b/components/brave_new_tab_ui/components/default/rewards/style.ts index c96a0efa1de0..79762df040f1 100644 --- a/components/brave_new_tab_ui/components/default/rewards/style.ts +++ b/components/brave_new_tab_ui/components/default/rewards/style.ts @@ -17,7 +17,7 @@ export const WidgetWrapper = styled<{}, 'div'>('div')` export const Footer = styled<{}, 'div'>('div')` max-width: 275px; - margin-top: 10px; + margin-top: 25px; ` export const BatIcon = styled<{}, 'div'>('div')` @@ -43,7 +43,7 @@ export const ServiceText = styled<{}, 'span'>('span')` ` export const ServiceLink = styled<{}, 'a'>('a')` - color: ${palette.blue500}; + color: #B0DBFF; font-weight: 600; text-decoration: none; ` @@ -116,7 +116,7 @@ export const ConvertedAmount = styled<{}, 'span'>('span')` export const AmountDescription = styled<{}, 'span'>('span')` font-size: 14px; - color: rgb(128, 158, 255); + color: #fff; ` export const NotificationWrapper = styled(WidgetWrapper)` diff --git a/components/brave_new_tab_ui/containers/newTab/index.tsx b/components/brave_new_tab_ui/containers/newTab/index.tsx index f8b2cd478b75..d1bad01c2c6a 100644 --- a/components/brave_new_tab_ui/containers/newTab/index.tsx +++ b/components/brave_new_tab_ui/containers/newTab/index.tsx @@ -210,7 +210,7 @@ class NewTabPage extends React.Component { showWidget={newTabData.showRewards} hideWidget={this.toggleShowRewards} onDismissNotification={this.dismissNotification} - menuPosition={'right'} + menuPosition={'left'} /> {this.props.newTabData.gridSites.length ? { size='small' /> + + {getLocale('showRewards')} + + {getLocale('showBraveStats')} { size='small' /> - - {getLocale('showRewards')} - - } diff --git a/components/brave_rewards/browser/rewards_service.h b/components/brave_rewards/browser/rewards_service.h index 7b3111befb4c..2ce29e27eb56 100644 --- a/components/brave_rewards/browser/rewards_service.h +++ b/components/brave_rewards/browser/rewards_service.h @@ -214,7 +214,7 @@ class RewardsService : public KeyedService { RewardsServicePrivateObserver* observer) = 0; virtual void GetTransactionHistory( GetTransactionHistoryCallback callback) = 0; - virtual void TriggerOnAdsEnabled(bool ads_enabled) = 0; + virtual void OnAdsEnabled(bool ads_enabled) = 0; virtual void RefreshPublisher( const std::string& publisher_key, diff --git a/components/brave_rewards/browser/rewards_service_impl.cc b/components/brave_rewards/browser/rewards_service_impl.cc index b4983074bd51..c190a38232f1 100644 --- a/components/brave_rewards/browser/rewards_service_impl.cc +++ b/components/brave_rewards/browser/rewards_service_impl.cc @@ -2080,7 +2080,7 @@ void RewardsServiceImpl::SetAutoContribute(bool enabled) { } } -void RewardsServiceImpl::TriggerOnAdsEnabled( +void RewardsServiceImpl::OnAdsEnabled( bool ads_enabled) { for (auto& observer : observers_) observer.OnAdsEnabled(this, ads_enabled); diff --git a/components/brave_rewards/browser/rewards_service_impl.h b/components/brave_rewards/browser/rewards_service_impl.h index b505def750e0..315574bd994c 100644 --- a/components/brave_rewards/browser/rewards_service_impl.h +++ b/components/brave_rewards/browser/rewards_service_impl.h @@ -216,7 +216,7 @@ class RewardsServiceImpl : public RewardsService, void RefreshPublisher( const std::string& publisher_key, RefreshPublisherCallback callback) override; - void TriggerOnAdsEnabled(bool ads_enabled) override; + void OnAdsEnabled(bool ads_enabled) override; void OnSaveRecurringTipUI( SaveRecurringTipCallback callback, diff --git a/components/resources/brave_components_strings.grd b/components/resources/brave_components_strings.grd index 6a33016fda0c..e5933dc4d0ce 100644 --- a/components/resources/brave_components_strings.grd +++ b/components/resources/brave_components_strings.grd @@ -142,7 +142,7 @@ Show Brave Stats Show Clock Show Top Sites - Show Rewards + Show Brave Rewards Learn more