From ce7a8004b14c0171996fe4ad47c8408e2a72ed4d Mon Sep 17 00:00:00 2001 From: ryanml Date: Thu, 1 Oct 2020 00:49:27 -0700 Subject: [PATCH 1/3] Merge pull request #6681 from brave/ntp-update-bdc Adding Bitcoin.com widget to the NTP --- browser/BUILD.gn | 16 +- browser/brave_profile_prefs.cc | 10 + browser/extensions/BUILD.gn | 38 ++- browser/extensions/api/moonpay_api.cc | 90 +++++ browser/extensions/api/moonpay_api.h | 63 ++++ .../extensions/api/moonpay_api_browsertest.cc | 165 ++++++++++ .../api/settings_private/brave_prefs_util.cc | 9 + .../brave_new_tab_browser_proxy.js | 10 + .../brave_new_tab_page.html | 7 + .../brave_new_tab_page/brave_new_tab_page.js | 5 + browser/ui/BUILD.gn | 2 + .../ui/webui/brave_new_tab_message_handler.cc | 19 ++ browser/ui/webui/brave_webui_source.cc | 13 + .../settings/brave_appearance_handler.cc | 31 +- .../webui/settings/brave_appearance_handler.h | 1 + .../settings_localized_strings_provider.cc | 5 + common/extensions/api/BUILD.gn | 9 + .../extensions/api/_moonpay_api_features.json | 14 + common/extensions/api/moonpay.json | 77 +++++ .../actions/bitcoin_dot_com_actions.ts | 11 + components/brave_new_tab_ui/api/getActions.ts | 6 +- .../brave_new_tab_ui/api/initialData.ts | 12 +- .../brave_new_tab_ui/api/preferences.ts | 4 + .../default/bitcoinDotCom/assets/icons.ts | 12 + .../default/bitcoinDotCom/assets/logo.png | Bin 0 -> 546 bytes .../components/default/bitcoinDotCom/fiat.ts | 54 +++ .../default/bitcoinDotCom/index.tsx | 311 ++++++++++++++++++ .../components/default/bitcoinDotCom/style.ts | 245 ++++++++++++++ .../components/default/index.ts | 2 + .../default/widget/assets/ellipsis.tsx | 9 +- .../components/default/widget/index.tsx | 3 + .../components/default/widget/widgetMenu.tsx | 4 +- .../constants/bitcoin_dot_com_types.ts | 9 + .../constants/new_tab_types.ts | 14 + .../brave_new_tab_ui/containers/app.tsx | 9 +- .../containers/newTab/index.tsx | 83 ++++- .../containers/newTab/settings.tsx | 11 +- .../settings/assets/bitcoin-dot-com.png | Bin 0 -> 28003 bytes .../containers/newTab/settings/moreCards.tsx | 25 +- .../reducers/bitcoin_dot_com_reducer.ts | 33 ++ components/brave_new_tab_ui/reducers/index.ts | 2 + .../reducers/new_tab_reducer.ts | 3 +- components/brave_new_tab_ui/reducers/utils.ts | 11 + .../storage/new_tab_storage.ts | 5 +- .../brave_new_tab_ui/stories/default.tsx | 4 +- components/definitions/chromel.d.ts | 7 + components/definitions/newTab.d.ts | 4 +- components/moonpay/browser/BUILD.gn | 16 + .../moonpay/browser/buildflags/BUILD.gn | 10 + .../moonpay/browser/buildflags/buildflags.gni | 5 + .../moonpay/browser/moonpay_pref_utils.cc | 24 ++ .../moonpay/browser/moonpay_pref_utils.h | 23 ++ components/moonpay/browser/regions.h | 26 ++ components/moonpay/common/BUILD.gn | 10 + components/moonpay/common/pref_names.cc | 13 + components/moonpay/common/pref_names.h | 13 + components/ntp_widget_utils/browser/BUILD.gn | 15 - .../browser/buildflags/BUILD.gn | 9 - .../browser/buildflags/buildflags.gni | 8 - components/resources/BUILD.gn | 3 + .../resources/bitcoin_dot_com_strings.grdp | 12 + .../resources/brave_components_strings.grd | 1 + .../test/brave_new_tab_ui/api/data_test.ts | 3 +- test/BUILD.gn | 24 +- 64 files changed, 1606 insertions(+), 86 deletions(-) create mode 100644 browser/extensions/api/moonpay_api.cc create mode 100644 browser/extensions/api/moonpay_api.h create mode 100644 browser/extensions/api/moonpay_api_browsertest.cc create mode 100644 common/extensions/api/_moonpay_api_features.json create mode 100644 common/extensions/api/moonpay.json create mode 100644 components/brave_new_tab_ui/actions/bitcoin_dot_com_actions.ts create mode 100644 components/brave_new_tab_ui/components/default/bitcoinDotCom/assets/icons.ts create mode 100644 components/brave_new_tab_ui/components/default/bitcoinDotCom/assets/logo.png create mode 100644 components/brave_new_tab_ui/components/default/bitcoinDotCom/fiat.ts create mode 100644 components/brave_new_tab_ui/components/default/bitcoinDotCom/index.tsx create mode 100644 components/brave_new_tab_ui/components/default/bitcoinDotCom/style.ts create mode 100644 components/brave_new_tab_ui/constants/bitcoin_dot_com_types.ts create mode 100644 components/brave_new_tab_ui/containers/newTab/settings/assets/bitcoin-dot-com.png create mode 100644 components/brave_new_tab_ui/reducers/bitcoin_dot_com_reducer.ts create mode 100644 components/brave_new_tab_ui/reducers/utils.ts create mode 100644 components/moonpay/browser/BUILD.gn create mode 100644 components/moonpay/browser/buildflags/BUILD.gn create mode 100644 components/moonpay/browser/buildflags/buildflags.gni create mode 100644 components/moonpay/browser/moonpay_pref_utils.cc create mode 100644 components/moonpay/browser/moonpay_pref_utils.h create mode 100644 components/moonpay/browser/regions.h create mode 100644 components/moonpay/common/BUILD.gn create mode 100644 components/moonpay/common/pref_names.cc create mode 100644 components/moonpay/common/pref_names.h delete mode 100644 components/ntp_widget_utils/browser/buildflags/BUILD.gn delete mode 100644 components/ntp_widget_utils/browser/buildflags/buildflags.gni create mode 100644 components/resources/bitcoin_dot_com_strings.grdp diff --git a/browser/BUILD.gn b/browser/BUILD.gn index f163fbfbc23e..5438807bf7b7 100644 --- a/browser/BUILD.gn +++ b/browser/BUILD.gn @@ -1,7 +1,7 @@ import("//brave/build/config.gni") import("//brave/components/binance/browser/buildflags/buildflags.gni") import("//brave/components/brave_together/buildflags/buildflags.gni") -import("//brave/components/ntp_widget_utils/browser/buildflags/buildflags.gni") +import("//brave/components/moonpay/browser/buildflags/buildflags.gni") import("//brave/components/gemini/browser/buildflags/buildflags.gni") import("//brave/browser/tor/buildflags/buildflags.gni") import("//brave/components/brave_ads/browser/buildflags/buildflags.gni") @@ -139,6 +139,7 @@ source_set("browser_process") { "//brave/components/greaselion/browser/buildflags", "//brave/components/ipfs/browser/buildflags", "//brave/components/l10n/browser", + "//brave/components/moonpay/browser/buildflags", "//brave/components/ntp_background_images/browser", "//brave/components/ntp_tiles", "//brave/components/p3a", @@ -305,12 +306,6 @@ source_set("browser_process") { ] } - if (ntp_widget_utils_enabled) { - deps += [ - "//brave/components/ntp_widget_utils/browser", - ] - } - if (ipfs_enabled) { sources += [ "ipfs/content_browser_client_helper.cc", @@ -342,6 +337,13 @@ source_set("browser_process") { ] } + if (moonpay_enabled) { + deps += [ + "//brave/components/moonpay/browser", + "//brave/components/moonpay/common", + ] + } + if (enable_tor) { deps += [ "//brave/browser/tor", diff --git a/browser/brave_profile_prefs.cc b/browser/brave_profile_prefs.cc index 1c9400570260..a624c4220fd8 100644 --- a/browser/brave_profile_prefs.cc +++ b/browser/brave_profile_prefs.cc @@ -19,6 +19,7 @@ #include "brave/components/brave_wallet/browser/buildflags/buildflags.h" #include "brave/components/brave_wayback_machine/buildflags.h" #include "brave/components/brave_webtorrent/browser/buildflags/buildflags.h" +#include "brave/components/moonpay/browser/buildflags/buildflags.h" #include "brave/components/ipfs/browser/buildflags/buildflags.h" #include "brave/components/speedreader/buildflags.h" #include "chrome/browser/net/prediction_options.h" @@ -61,6 +62,11 @@ #include "brave/components/gemini/browser/pref_names.h" #endif +#if BUILDFLAG(MOONPAY_ENABLED) +#include "brave/components/moonpay/browser/moonpay_pref_utils.h" +#include "brave/components/moonpay/common/pref_names.h" +#endif + #if BUILDFLAG(ENABLE_BRAVE_PERF_PREDICTOR) #include "brave/components/brave_perf_predictor/browser/perf_predictor_tab_helper.h" #include "brave/components/brave_perf_predictor/browser/p3a_bandwidth_savings_tracker.h" @@ -274,6 +280,10 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { speedreader::SpeedreaderService::RegisterPrefs(registry); #endif +#if BUILDFLAG(MOONPAY_ENABLED) + moonpay::MoonpayPrefUtils::RegisterPrefs(registry); +#endif + #if !defined(OS_ANDROID) BraveOmniboxClientImpl::RegisterPrefs(registry); #endif diff --git a/browser/extensions/BUILD.gn b/browser/extensions/BUILD.gn index 0f0a40332342..a748652ccac8 100644 --- a/browser/extensions/BUILD.gn +++ b/browser/extensions/BUILD.gn @@ -7,7 +7,7 @@ import("//brave/components/brave_wayback_machine/buildflags/buildflags.gni") import("//brave/components/brave_webtorrent/browser/buildflags/buildflags.gni") import("//brave/components/brave_together/buildflags/buildflags.gni") import("//brave/components/ipfs/browser/buildflags/buildflags.gni") -import("//brave/components/ntp_widget_utils/browser/buildflags/buildflags.gni") +import("//brave/components/moonpay/browser/buildflags/buildflags.gni") import("//build/config/features.gni") import("//components/gcm_driver/config.gni") @@ -106,8 +106,15 @@ source_set("extensions") { "//url", ] - if (ntp_widget_utils_enabled) { + if (moonpay_enabled) { + sources += [ + "api/moonpay_api.cc", + "api/moonpay_api.h", + ] + deps += [ + "//brave/components/moonpay/browser/", + "//brave/components/moonpay/common/", "//brave/components/ntp_widget_utils/browser", ] } @@ -156,9 +163,32 @@ source_set("extensions") { "api/gemini_api.h", ] deps += [ - "//brave/common", "//brave/components/gemini/browser", - "//extensions/browser", + "//brave/components/ntp_widget_utils/browser", + ] + } + + if (binance_enabled) { + sources += [ + "api/binance_api.cc", + "api/binance_api.h", + ] + + deps += [ + "//brave/components/binance/browser", + "//brave/components/ntp_widget_utils/browser", + ] + } + + if (brave_together_enabled) { + sources += [ + "api/brave_together_api.cc", + "api/brave_together_api.h", + ] + + deps += [ + "//brave/components/brave_together/browser", + "//brave/components/ntp_widget_utils/browser", ] } } diff --git a/browser/extensions/api/moonpay_api.cc b/browser/extensions/api/moonpay_api.cc new file mode 100644 index 000000000000..7bee67009c68 --- /dev/null +++ b/browser/extensions/api/moonpay_api.cc @@ -0,0 +1,90 @@ +/* Copyright (c) 2020 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/extensions/api/moonpay_api.h" + +#include +#include +#include + +#include "brave/browser/profiles/profile_util.h" +#include "brave/components/moonpay/browser/regions.h" +#include "brave/components/moonpay/common/pref_names.h" +#include "brave/components/ntp_widget_utils/browser/ntp_widget_utils_region.h" +#include "chrome/browser/profiles/profile.h" +#include "components/prefs/pref_service.h" + +namespace { +bool IsMoonpayAPIAvailable(content::BrowserContext* context) { + return brave::IsRegularProfile(context); +} +} + +namespace extensions { +namespace api { + +ExtensionFunction::ResponseAction +MoonpayIsBitcoinDotComSupportedFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + + if (!IsMoonpayAPIAvailable(browser_context())) { + return RespondNow(Error("Not available in Tor/incognito/guest profile")); + } + + bool is_supported = ntp_widget_utils::IsRegionSupported( + profile->GetPrefs(), moonpay::bitcoin_dot_com_supported_regions, true); + return RespondNow(OneArgument( + std::make_unique(is_supported))); +} + +ExtensionFunction::ResponseAction +MoonpayOnBuyBitcoinDotComCryptoFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + + if (!IsMoonpayAPIAvailable(browser_context())) { + return RespondNow(Error("Not available in Tor/incognito/guest profile")); + } + + profile->GetPrefs()->SetBoolean(kMoonpayHasBoughtBitcoinDotComCrypto, true); + profile->GetPrefs()->SetBoolean(kMoonpayHasInteractedBitcoinDotCom, true); + + return RespondNow(NoArguments()); +} + +ExtensionFunction::ResponseAction +MoonpayOnInteractionBitcoinDotComFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + + if (!IsMoonpayAPIAvailable(browser_context())) { + return RespondNow(Error("Not available in Tor/incognito/guest profile")); + } + + profile->GetPrefs()->SetBoolean(kMoonpayHasInteractedBitcoinDotCom, true); + + return RespondNow(NoArguments()); +} + +ExtensionFunction::ResponseAction +MoonpayGetBitcoinDotComInteractionsFunction::Run() { + Profile* profile = Profile::FromBrowserContext(browser_context()); + + if (!IsMoonpayAPIAvailable(browser_context())) { + return RespondNow(Error("Not available in Tor/incognito/guest profile")); + } + + bool has_bought = profile->GetPrefs()->GetBoolean( + kMoonpayHasBoughtBitcoinDotComCrypto); + bool has_interacted = profile->GetPrefs()->GetBoolean( + kMoonpayHasInteractedBitcoinDotCom); + + auto interactions = std::make_unique(); + interactions->SetBoolean("boughtCrypto", has_bought); + interactions->SetBoolean("interacted", has_interacted); + + return RespondNow(OneArgument(std::move(interactions))); +} + +} // namespace api +} // namespace extensions diff --git a/browser/extensions/api/moonpay_api.h b/browser/extensions/api/moonpay_api.h new file mode 100644 index 000000000000..58fde8d05bb5 --- /dev/null +++ b/browser/extensions/api/moonpay_api.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2020 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_EXTENSIONS_API_MOONPAY_API_H_ +#define BRAVE_BROWSER_EXTENSIONS_API_MOONPAY_API_H_ + +#include +#include + +#include "extensions/browser/extension_function.h" + +class Profile; + +namespace extensions { +namespace api { + +class MoonpayIsBitcoinDotComSupportedFunction : + public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("moonpay.isBitcoinDotComSupported", UNKNOWN) + + protected: + ~MoonpayIsBitcoinDotComSupportedFunction() override {} + ResponseAction Run() override; +}; + +class MoonpayOnBuyBitcoinDotComCryptoFunction : + public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("moonpay.onBuyBitcoinDotComCrypto", UNKNOWN) + + protected: + ~MoonpayOnBuyBitcoinDotComCryptoFunction() override {} + ResponseAction Run() override; +}; + +class MoonpayOnInteractionBitcoinDotComFunction : + public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("moonpay.onInteractionBitcoinDotCom", UNKNOWN) + + protected: + ~MoonpayOnInteractionBitcoinDotComFunction() override {} + ResponseAction Run() override; +}; + +class MoonpayGetBitcoinDotComInteractionsFunction : + public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("moonpay.getBitcoinDotComInteractions", UNKNOWN) + + protected: + ~MoonpayGetBitcoinDotComInteractionsFunction() override {} + ResponseAction Run() override; +}; + + +} // namespace api +} // namespace extensions + +#endif // BRAVE_BROWSER_EXTENSIONS_API_MOONPAY_API_H_ diff --git a/browser/extensions/api/moonpay_api_browsertest.cc b/browser/extensions/api/moonpay_api_browsertest.cc new file mode 100644 index 000000000000..33b7e1b6d24b --- /dev/null +++ b/browser/extensions/api/moonpay_api_browsertest.cc @@ -0,0 +1,165 @@ +/* Copyright (c) 2019 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 "base/path_service.h" +#include "brave/browser/extensions/api/moonpay_api.h" +#include "brave/common/brave_paths.h" +#include "brave/components/moonpay/common/pref_names.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_test.h" +#include "extensions/browser/api_test_utils.h" +#include "extensions/common/extension_builder.h" + +// npm run test -- brave_browser_tests --filter=MoonpayAPIBrowserTest.* + +using extensions::api::MoonpayOnBuyBitcoinDotComCryptoFunction; +using extensions::api::MoonpayOnInteractionBitcoinDotComFunction; +using extensions::api::MoonpayGetBitcoinDotComInteractionsFunction; +using extension_function_test_utils::RunFunctionAndReturnSingleResult; +using extension_function_test_utils::RunFunction; +using extension_function_test_utils::ToDictionary; +using extensions::api_test_utils::RunFunctionFlags; + +class MoonpayAPIBrowserTest : public InProcessBrowserTest { + public: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + extension_ = extensions::ExtensionBuilder("Test").Build(); + + brave::RegisterPathProvider(); + base::FilePath test_data_dir; + base::PathService::Get(brave::DIR_TEST_DATA, &test_data_dir); + } + + scoped_refptr extension() { + return extension_; + } + + Profile* profile() { return browser()->profile(); } + + private: + scoped_refptr extension_; +}; + +IN_PROC_BROWSER_TEST_F(MoonpayAPIBrowserTest, + MoonpayOnBuyBitcoinDotComCryptoFunctionTest) { + scoped_refptr buy_function( + new MoonpayOnBuyBitcoinDotComCryptoFunction()); + buy_function->set_extension(extension().get()); + + // kMoonpayHasBoughtBitcoinDotComCrypto should initially be false + ASSERT_FALSE(profile()->GetPrefs()->GetBoolean( + kMoonpayHasBoughtBitcoinDotComCrypto)); + // kMoonpayHasInteractedBitcoinDotCom should initially be false + ASSERT_FALSE(profile()->GetPrefs()->GetBoolean( + kMoonpayHasInteractedBitcoinDotCom)); + + // Call moonpay.onBuyBitcoinDotComCrypto + RunFunction(buy_function.get(), std::string("[]"), browser(), + RunFunctionFlags()); + + // kMoonpayHasBoughtBitcoinDotComCrypto should be true + ASSERT_TRUE(profile()->GetPrefs()->GetBoolean( + kMoonpayHasBoughtBitcoinDotComCrypto)); + // kMoonpayHasBoughtBitcoinDotComCrypto should also be true + ASSERT_TRUE(profile()->GetPrefs()->GetBoolean( + kMoonpayHasInteractedBitcoinDotCom)); +} + +IN_PROC_BROWSER_TEST_F(MoonpayAPIBrowserTest, + MoonpayOnInteractionBitcoinDotComFunction) { + scoped_refptr interact_function( + new MoonpayOnInteractionBitcoinDotComFunction()); + interact_function->set_extension(extension().get()); + + // kMoonpayHasInteractedBitcoinDotCom should initially be false + ASSERT_FALSE(profile()->GetPrefs()->GetBoolean( + kMoonpayHasInteractedBitcoinDotCom)); + + // Call moonpay.onInteractionBitcoinDotCom + RunFunction(interact_function.get(), std::string("[]"), browser(), + RunFunctionFlags()); + + // kMoonpayHasBoughtBitcoinDotComCrypto should be true + ASSERT_TRUE(profile()->GetPrefs()->GetBoolean( + kMoonpayHasInteractedBitcoinDotCom)); +} + +IN_PROC_BROWSER_TEST_F(MoonpayAPIBrowserTest, + MoonpayGetBitcoinDotComInteractionsFunctionNone) { + scoped_refptr get_function( + new MoonpayGetBitcoinDotComInteractionsFunction()); + get_function->set_extension(extension().get()); + + std::unique_ptr result; + result.reset(ToDictionary( + RunFunctionAndReturnSingleResult(get_function.get(), + std::string("[]"), + browser()))); + bool interacted; + bool bought_crypto; + result->GetBoolean("interacted", &interacted); + result->GetBoolean("boughtCrypto", &bought_crypto); + EXPECT_EQ(interacted, false); + EXPECT_EQ(bought_crypto, false); +} + +IN_PROC_BROWSER_TEST_F(MoonpayAPIBrowserTest, + MoonpayGetBitcoinDotComInteractionsFunctionOne) { + scoped_refptr interact_function( + new MoonpayOnInteractionBitcoinDotComFunction()); + interact_function->set_extension(extension().get()); + + // Set an interaction + RunFunction(interact_function.get(), std::string("[]"), browser(), + RunFunctionFlags()); + + scoped_refptr get_function( + new MoonpayGetBitcoinDotComInteractionsFunction()); + get_function->set_extension(extension().get()); + + std::unique_ptr result; + result.reset(ToDictionary( + RunFunctionAndReturnSingleResult(get_function.get(), + std::string("[]"), + browser()))); + bool interacted; + bool bought_crypto; + result->GetBoolean("interacted", &interacted); + result->GetBoolean("boughtCrypto", &bought_crypto); + EXPECT_EQ(interacted, true); + EXPECT_EQ(bought_crypto, false); +} + +IN_PROC_BROWSER_TEST_F(MoonpayAPIBrowserTest, + MoonpayGetBitcoinDotComInteractionsFunctionBoth) { + scoped_refptr buy_function( + new MoonpayOnBuyBitcoinDotComCryptoFunction()); + buy_function->set_extension(extension().get()); + + // Mark a buy interaction + RunFunction(buy_function.get(), std::string("[]"), browser(), + RunFunctionFlags()); + + scoped_refptr get_function( + new MoonpayGetBitcoinDotComInteractionsFunction()); + get_function->set_extension(extension().get()); + + std::unique_ptr result; + result.reset(ToDictionary( + RunFunctionAndReturnSingleResult(get_function.get(), + std::string("[]"), + browser()))); + bool interacted; + bool bought_crypto; + result->GetBoolean("interacted", &interacted); + result->GetBoolean("boughtCrypto", &bought_crypto); + EXPECT_EQ(interacted, true); + EXPECT_EQ(bought_crypto, true); +} diff --git a/browser/extensions/api/settings_private/brave_prefs_util.cc b/browser/extensions/api/settings_private/brave_prefs_util.cc index 779a4221350b..a555683e2f13 100644 --- a/browser/extensions/api/settings_private/brave_prefs_util.cc +++ b/browser/extensions/api/settings_private/brave_prefs_util.cc @@ -8,6 +8,7 @@ #include "brave/common/pref_names.h" #include "brave/components/brave_wayback_machine/buildflags.h" #include "brave/components/brave_rewards/common/pref_names.h" +#include "brave/components/moonpay/browser/buildflags/buildflags.h" #include "brave/components/ntp_background_images/common/pref_names.h" #include "chrome/browser/extensions/api/settings_private/prefs_util.h" #include "chrome/common/extensions/api/settings_private.h" @@ -20,6 +21,10 @@ #endif +#if BUILDFLAG(MOONPAY_ENABLED) +#include "brave/components/moonpay/common/pref_names.h" +#endif + namespace extensions { using ntp_background_images::prefs::kNewTabPageSuperReferralThemesOption; @@ -100,6 +105,10 @@ const PrefsUtil::TypedPrefMap& BravePrefsUtil::GetWhitelistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_brave_whitelist)[kNewTabPageShowGemini] = settings_api::PrefType::PREF_TYPE_BOOLEAN; +#if BUILDFLAG(MOONPAY_ENABLED) + (*s_brave_whitelist)[kMoonpayNewTabPageShowBitcoinDotCom] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; +#endif // Clear browsing data on exit prefs. (*s_brave_whitelist)[browsing_data::prefs::kDeleteBrowsingHistoryOnExit] = settings_api::PrefType::PREF_TYPE_BOOLEAN; diff --git a/browser/resources/settings/brave_new_tab_page/brave_new_tab_browser_proxy.js b/browser/resources/settings/brave_new_tab_page/brave_new_tab_browser_proxy.js index 416c35e8cfed..f5e99de1af6d 100644 --- a/browser/resources/settings/brave_new_tab_page/brave_new_tab_browser_proxy.js +++ b/browser/resources/settings/brave_new_tab_page/brave_new_tab_browser_proxy.js @@ -28,6 +28,11 @@ cr.define('settings', function() { * @return {!Promise} */ getIsGeminiSupported() {} + + /** + * @return {!Promise} + */ + getIsBitcoinDotComSupported() {} } /** @@ -53,6 +58,11 @@ cr.define('settings', function() { getIsGeminiSupported() { return cr.sendWithPromise('getIsGeminiSupported') } + + /** @override */ + getIsBitcoinDotComSupported() { + return cr.sendWithPromise('getIsBitcoinDotComSupported') + } } cr.addSingletonGetter(BraveNewTabBrowserProxyImpl); diff --git a/browser/resources/settings/brave_new_tab_page/brave_new_tab_page.html b/browser/resources/settings/brave_new_tab_page/brave_new_tab_page.html index 32b6bf02e999..67f521ae32cb 100644 --- a/browser/resources/settings/brave_new_tab_page/brave_new_tab_page.html +++ b/browser/resources/settings/brave_new_tab_page/brave_new_tab_page.html @@ -77,6 +77,13 @@ label="$i18n{braveNewTabGemini}"> + { this.isGeminiSupported_ = isGeminiSupported; }) + this.browserProxy_.getIsBitcoinDotComSupported().then(isBitcoinDotComSupported => { + this.isBitcoinDotComSupported_ = isBitcoinDotComSupported; + }) this.addWebUIListener('super-referral-active-state-changed', (isSuperReferralActive) => { this.isSuperReferralActive_ = isSuperReferralActive; diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index 3fa4306e4c9e..2ae5763b8da1 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -205,8 +205,10 @@ source_set("ui") { "//brave/components/brave_wayback_machine:buildflags", "//brave/components/l10n/browser", "//brave/components/l10n/common", + "//brave/components/moonpay/browser/buildflags:buildflags", "//brave/components/ntp_background_images/browser", "//brave/components/ntp_background_images/common", + "//brave/components/ntp_widget_utils/browser", "//brave/components/p3a:buildflags", "//brave/components/vector_icons", "//brave/components/webcompat_reporter/browser", diff --git a/browser/ui/webui/brave_new_tab_message_handler.cc b/browser/ui/webui/brave_new_tab_message_handler.cc index 89589f72f3d3..138bbac295fb 100644 --- a/browser/ui/webui/brave_new_tab_message_handler.cc +++ b/browser/ui/webui/brave_new_tab_message_handler.cc @@ -19,6 +19,7 @@ #include "brave/components/brave_ads/browser/ads_service.h" #include "brave/components/brave_ads/browser/ads_service_factory.h" #include "brave/components/brave_perf_predictor/browser/buildflags.h" +#include "brave/components/moonpay/browser/buildflags/buildflags.h" #include "brave/components/ntp_background_images/browser/features.h" #include "brave/components/ntp_background_images/browser/view_counter_service.h" #include "brave/components/ntp_background_images/common/pref_names.h" @@ -38,6 +39,10 @@ using ntp_background_images::ViewCounterServiceFactory; #include "brave/components/brave_perf_predictor/common/pref_names.h" #endif +#if BUILDFLAG(MOONPAY_ENABLED) +#include "brave/components/moonpay/common/pref_names.h" +#endif + namespace { bool IsPrivateNewTab(Profile* profile) { @@ -100,6 +105,11 @@ base::DictionaryValue GetPreferencesDictionary(PrefService* prefs) { pref_data.SetBoolean( "showGemini", prefs->GetBoolean(kNewTabPageShowGemini)); +#if BUILDFLAG(MOONPAY_ENABLED) + pref_data.SetBoolean( + "showBitcoinDotCom", + prefs->GetBoolean(kMoonpayNewTabPageShowBitcoinDotCom)); +#endif return pref_data; } @@ -264,6 +274,11 @@ void BraveNewTabMessageHandler::OnJavascriptAllowed() { pref_change_registrar_.Add(kNewTabPageShowGemini, base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, base::Unretained(this))); +#if BUILDFLAG(MOONPAY_ENABLED) + pref_change_registrar_.Add(kMoonpayNewTabPageShowBitcoinDotCom, + base::Bind(&BraveNewTabMessageHandler::OnPreferencesChanged, + base::Unretained(this))); +#endif } void BraveNewTabMessageHandler::OnJavascriptDisallowed() { @@ -341,6 +356,10 @@ void BraveNewTabMessageHandler::HandleSaveNewTabPagePref( settingsKey = kNewTabPageShowAddCard; } else if (settingsKeyInput == "showGemini") { settingsKey = kNewTabPageShowGemini; +#if BUILDFLAG(MOONPAY_ENABLED) + } else if (settingsKeyInput == "showBitcoinDotCom") { + settingsKey = kMoonpayNewTabPageShowBitcoinDotCom; +#endif } else { LOG(ERROR) << "Invalid setting key"; return; diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index 2015b4d43a01..8dc3fab365bb 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -11,6 +11,7 @@ #include "brave/components/ipfs/browser/buildflags/buildflags.h" #include "base/strings/utf_string_conversions.h" #include "brave/common/url_constants.h" +#include "brave/components/moonpay/browser/buildflags/buildflags.h" #include "components/grit/brave_components_resources.h" #include "components/grit/brave_components_strings.h" #include "components/grit/components_resources.h" @@ -174,6 +175,9 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "rewardsWidgetDesc", IDS_BRAVE_NEW_TAB_REWARDS_WIDGET_DESC }, { "binanceWidgetDesc", IDS_BRAVE_NEW_TAB_BINANCE_WIDGET_DESC }, { "geminiWidgetDesc", IDS_BRAVE_NEW_TAB_GEMINI_WIDGET_DESC }, +#if BUILDFLAG(MOONPAY_ENABLED) + { "bitcoinDotComWidgetDesc", IDS_BRAVE_NEW_TAB_BITCOIN_DOT_COM_WIDGET_DESC }, // NOLINT +#endif { "braveRewardsTitle", IDS_BRAVE_NEW_TAB_BRAVE_REWARDS_TITLE }, // Private Tab - General { "learnMore", IDS_BRAVE_PRIVATE_NEW_TAB_LEARN_MORE }, @@ -331,6 +335,15 @@ void CustomizeWebUIHTMLSource(const std::string &name, { "geminiWidgetUnitPrice", IDS_GEMINI_WIDGET_UNIT_PRICE }, { "geminiWidgetTotalPrice", IDS_GEMINI_WIDGET_TOTAL_PRICE }, { "geminiWidgetTotalAmount", IDS_GEMINI_WIDGET_TOTAL_AMOUNT }, +#if BUILDFLAG(MOONPAY_ENABLED) + // Bitcoin.com widget + { "bitcoinDotComWidgetCurrency", IDS_BITCOIN_DOT_COM_CURRENCY }, + { "bitcoinDotComWidgetAmount", IDS_BITCOIN_DOT_COM_AMOUNT }, + { "bitcoinDotComWidgetEnterAmount", IDS_BITCOIN_DOT_COM_ENTER_AMOUNT }, + { "bitcoinDotComWidgetFooterCopyOne", IDS_BITCOIN_DOT_COM_FOOTER_COPY_ONE }, // NOLINT + { "bitcoinDotComWidgetFooterCopyTwo", IDS_BITCOIN_DOT_COM_FOOTER_COPY_TWO }, // NOLINT + { "bitcoinDotComWidgetBuy", IDS_BINANCE_WIDGET_BUY }, +#endif } }, { std::string("wallet"), { diff --git a/browser/ui/webui/settings/brave_appearance_handler.cc b/browser/ui/webui/settings/brave_appearance_handler.cc index c15f9202fe59..e9667f8c7da0 100644 --- a/browser/ui/webui/settings/brave_appearance_handler.cc +++ b/browser/ui/webui/settings/brave_appearance_handler.cc @@ -12,7 +12,8 @@ #include "brave/components/binance/browser/buildflags/buildflags.h" #include "brave/components/brave_together/buildflags/buildflags.h" #include "brave/components/gemini/browser/buildflags/buildflags.h" -#include "brave/components/ntp_widget_utils/browser/buildflags/buildflags.h" +#include "brave/components/moonpay/browser/buildflags/buildflags.h" +#include "brave/components/ntp_widget_utils/browser/ntp_widget_utils_region.h" #include "brave/common/pref_names.h" #include "brave/components/ntp_background_images/browser/ntp_background_images_data.h" #include "brave/components/ntp_background_images/browser/view_counter_service.h" @@ -21,10 +22,6 @@ #include "chrome/browser/profiles/profile.h" #include "content/public/browser/web_ui.h" -#if BUILDFLAG(NTP_WIDGET_UTILS_ENABLED) -#include "brave/components/ntp_widget_utils/browser/ntp_widget_utils_region.h" -#endif - #if BUILDFLAG(BINANCE_ENABLED) #include "brave/components/binance/browser/regions.h" #endif @@ -37,6 +34,10 @@ #include "brave/components/gemini/browser/regions.h" #endif +#if BUILDFLAG(MOONPAY_ENABLED) +#include "brave/components/moonpay/browser/regions.h" +#endif + using ntp_background_images::ViewCounterServiceFactory; using ntp_background_images::prefs::kNewTabPageSuperReferralThemesOption; @@ -99,6 +100,10 @@ void BraveAppearanceHandler::RegisterMessages() { "getIsGeminiSupported", base::BindRepeating(&BraveAppearanceHandler::GetIsGeminiSupported, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "getIsBitcoinDotComSupported", + base::BindRepeating(&BraveAppearanceHandler::GetIsBitcoinDotComSupported, + base::Unretained(this))); } void BraveAppearanceHandler::SetBraveThemeType(const base::ListValue* args) { @@ -179,6 +184,22 @@ void BraveAppearanceHandler::GetIsGeminiSupported( ResolveJavascriptCallback(args->GetList()[0], base::Value(is_supported)); } +void BraveAppearanceHandler::GetIsBitcoinDotComSupported( + const base::ListValue* args) { + CHECK_EQ(args->GetSize(), 1U); + + AllowJavascript(); + +#if !BUILDFLAG(MOONPAY_ENABLED) + bool is_supported = false; +#else + bool is_supported = ntp_widget_utils::IsRegionSupported( + profile_->GetPrefs(), moonpay::bitcoin_dot_com_supported_regions, true); +#endif + + ResolveJavascriptCallback(args->GetList()[0], base::Value(is_supported)); +} + void BraveAppearanceHandler::OnBraveDarkModeChanged() { // GetBraveThemeType() should be used because settings option displays all // available options including default. diff --git a/browser/ui/webui/settings/brave_appearance_handler.h b/browser/ui/webui/settings/brave_appearance_handler.h index f6f423372cc4..9b7ec94e348c 100644 --- a/browser/ui/webui/settings/brave_appearance_handler.h +++ b/browser/ui/webui/settings/brave_appearance_handler.h @@ -35,6 +35,7 @@ class BraveAppearanceHandler : public settings::SettingsPageUIHandler { void GetIsBinanceSupported(const base::ListValue* args); void GetIsBraveTogetherSupported(const base::ListValue* args); void GetIsGeminiSupported(const base::ListValue* args); + void GetIsBitcoinDotComSupported(const base::ListValue* args); Profile* profile_ = nullptr; PrefChangeRegistrar local_state_change_registrar_; diff --git a/chromium_src/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chromium_src/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc index 7fba7bf118df..44dd71468fdc 100644 --- a/chromium_src/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc +++ b/chromium_src/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc @@ -10,8 +10,10 @@ #include "base/strings/utf_string_conversions.h" #include "brave/browser/ui/webui/settings/brave_privacy_handler.h" #include "brave/browser/version_info.h" +#include "brave/components/moonpay/browser/buildflags/buildflags.h" #include "chrome/browser/ui/webui/webui_util.h" #include "chrome/common/pref_names.h" +#include "components/grit/brave_components_strings.h" #include "components/prefs/pref_service.h" namespace settings { @@ -260,6 +262,9 @@ void BraveAddCommonStrings(content::WebUIDataSource* html_source, { "braveNewTabBraveRewards", IDS_SETTINGS_NEW_TAB_BRAVE_REWARDS }, { "braveNewTabBinance", IDS_SETTINGS_NEW_TAB_BINANCE }, { "braveNewTabGemini", IDS_SETTINGS_NEW_TAB_GEMINI }, +#if BUILDFLAG(MOONPAY_ENABLED) + { "braveNewTabBitcoinDotCom", IDS_SETTINGS_NEW_TAB_BITCOIN_DOT_COM }, +#endif { "braveNewTabTogether", IDS_SETTINGS_NEW_TAB_TOGETHER }, { "braveNewTabTopSites", IDS_SETTINGS_NEW_TAB_TOP_SITES }, { "braveNewTabClock", IDS_SETTINGS_NEW_TAB_CLOCK }, diff --git a/common/extensions/api/BUILD.gn b/common/extensions/api/BUILD.gn index b9658e6b562e..6f2cd033c0f2 100644 --- a/common/extensions/api/BUILD.gn +++ b/common/extensions/api/BUILD.gn @@ -3,6 +3,7 @@ import("//brave/components/gemini/browser/buildflags/buildflags.gni") import("//brave/components/brave_wallet/browser/buildflags/buildflags.gni") import("//brave/components/brave_together/buildflags/buildflags.gni") import("//brave/components/ipfs/browser/buildflags/buildflags.gni") +import("//brave/components/moonpay/browser/buildflags/buildflags.gni") import("//tools/grit/grit_rule.gni") import("//tools/json_schema_compiler/json_features.gni") import("//tools/json_schema_compiler/json_schema_api.gni") @@ -34,6 +35,10 @@ json_features("api_features") { if (brave_together_enabled) { sources += [ "_brave_together_api_features.json" ] } + + if (moonpay_enabled) { + sources += [ "_moonpay_api_features.json" ] + } } json_features("permission_features") { @@ -92,6 +97,10 @@ if (brave_together_enabled) { brave_extensions_api_schema_sources += [ "brave_together.json" ] } +if (moonpay_enabled) { + brave_extensions_api_schema_sources += [ "moonpay.json" ] +} + brave_extensions_api_uncompiled_sources = [] brave_extensions_api_root_namespace = "extensions::api::%(namespace)s" brave_extensions_api_schema_include_rules = diff --git a/common/extensions/api/_moonpay_api_features.json b/common/extensions/api/_moonpay_api_features.json new file mode 100644 index 000000000000..71135812b34e --- /dev/null +++ b/common/extensions/api/_moonpay_api_features.json @@ -0,0 +1,14 @@ +// 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/. + +{ + "moonpay": { + "channel": "stable", + "contexts": ["webui"], + "dependencies": [], + "matches": [ + "chrome://newtab/*" + ] + } +} diff --git a/common/extensions/api/moonpay.json b/common/extensions/api/moonpay.json new file mode 100644 index 000000000000..02912b1451d8 --- /dev/null +++ b/common/extensions/api/moonpay.json @@ -0,0 +1,77 @@ +// 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/. + +[ + { + "namespace": "moonpay", + "description": "Use the chrome.moonpay API to to receive info pertaining to the Moonpay affiliated services.", + "compiler_options": { + "implemented_in": "brave/browser/extensions/api/moonpay_api.h" + }, + "events": [ + ], + "functions": [ + { + "name": "isBitcoinDotComSupported", + "type": "function", + "description": "Fetches whether or not bitcoin.com is supported", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "supported", + "type": "boolean" + } + ] + } + ] + }, + { + "name": "onBuyBitcoinDotComCrypto", + "type": "function", + "description": "Marks when a user has used the bitcoin.com widget to buy crypto", + "parameters": [] + }, + { + "name": "onInteractionBitcoinDotCom", + "type": "function", + "description": "Marks user interaction with the bitcoin.com widget", + "parameters": [] + }, + { + "name": "getBitcoinDotComInteractions", + "type": "function", + "description": "Fetches user interactions with bitcoin.com", + "parameters": [ + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "interactions", + "type": "object", + "properties": { + "boughtCrypto": { + "type": "boolean", + "description": "Has clicked the buy button" + }, + "interacted": { + "type": "boolean", + "description": "Has interacted" + } + } + } + ] + } + ] + } + ], + "types": [ + ], + "properties": { + } + } +] diff --git a/components/brave_new_tab_ui/actions/bitcoin_dot_com_actions.ts b/components/brave_new_tab_ui/actions/bitcoin_dot_com_actions.ts new file mode 100644 index 000000000000..91339768e28d --- /dev/null +++ b/components/brave_new_tab_ui/actions/bitcoin_dot_com_actions.ts @@ -0,0 +1,11 @@ +// Copyright (c) 2020 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/. + +import { action } from 'typesafe-actions' +import { types } from '../constants/bitcoin_dot_com_types' + +export const buyBitcoinDotComCrypto = () => action(types.BUY_BITCOIN_DOT_COM_CRYPTO) + +export const interactionBitcoinDotCom = () => action(types.INTERACTION_BITCOIN_DOT_COM) diff --git a/components/brave_new_tab_ui/api/getActions.ts b/components/brave_new_tab_ui/api/getActions.ts index b47fef431ee4..1cfda47235e7 100644 --- a/components/brave_new_tab_ui/api/getActions.ts +++ b/components/brave_new_tab_ui/api/getActions.ts @@ -9,17 +9,19 @@ import * as gridSitesActions from '../actions/grid_sites_actions' import * as binanceActions from '../actions/binance_actions' import * as rewardsActions from '../actions/rewards_actions' import * as geminiActions from '../actions/gemini_actions' +import * as bitcoinDotComActions from '../actions/bitcoin_dot_com_actions' import store from '../store' +import { NewTabActions } from '../constants/new_tab_types' /** * Get actions from the C++ back-end down to front-end components */ -let actions: typeof newTabActions & typeof gridSitesActions & typeof binanceActions & typeof rewardsActions & typeof geminiActions +let actions: NewTabActions export default function getActions () { if (actions) { return actions } - const allActions = Object.assign({}, newTabActions, gridSitesActions, binanceActions, rewardsActions, geminiActions) + const allActions = Object.assign({}, newTabActions, gridSitesActions, binanceActions, rewardsActions, geminiActions, bitcoinDotComActions) actions = bindActionCreators(allActions, store.dispatch.bind(store)) return actions } diff --git a/components/brave_new_tab_ui/api/initialData.ts b/components/brave_new_tab_ui/api/initialData.ts index 516c9d869d74..ca724ec9eaa5 100644 --- a/components/brave_new_tab_ui/api/initialData.ts +++ b/components/brave_new_tab_ui/api/initialData.ts @@ -18,6 +18,7 @@ export type InitialData = { brandedWallpaperData: undefined | NewTab.BrandedWallpaper togetherSupported: boolean geminiSupported: boolean + bitcoinDotComSupported: boolean } export type PreInitialRewardsData = { @@ -47,7 +48,8 @@ export async function getInitialData (): Promise { defaultSuperReferralTopSites, brandedWallpaperData, togetherSupported, - geminiSupported + geminiSupported, + bitcoinDotComSupported ] = await Promise.all([ preferencesAPI.getPreferences(), statsAPI.getStats(), @@ -68,6 +70,11 @@ export async function getInitialData (): Promise { chrome.gemini.isSupported((supported: boolean) => { resolve(supported) }) + }), + new Promise((resolve) => { + chrome.moonpay.isBitcoinDotComSupported((supported: boolean) => { + resolve(supported) + }) }) ]) console.timeStamp('Got all initial data.') @@ -79,7 +86,8 @@ export async function getInitialData (): Promise { defaultSuperReferralTopSites, brandedWallpaperData, togetherSupported, - geminiSupported + geminiSupported, + bitcoinDotComSupported } as InitialData } catch (e) { console.error(e) diff --git a/components/brave_new_tab_ui/api/preferences.ts b/components/brave_new_tab_ui/api/preferences.ts index 573a63b19e4c..0bd7082bd7c7 100644 --- a/components/brave_new_tab_ui/api/preferences.ts +++ b/components/brave_new_tab_ui/api/preferences.ts @@ -75,6 +75,10 @@ export function saveShowGemini (value: boolean): void { sendSavePref('showGemini', value) } +export function saveShowBitcoinDotCom (value: boolean): void { + sendSavePref('showBitcoinDotCom', value) +} + export function addChangeListener (listener: PreferencesUpdatedHandler): void { window.cr.addWebUIListener('preferences-changed', listener) } diff --git a/components/brave_new_tab_ui/components/default/bitcoinDotCom/assets/icons.ts b/components/brave_new_tab_ui/components/default/bitcoinDotCom/assets/icons.ts new file mode 100644 index 000000000000..bef15eee30de --- /dev/null +++ b/components/brave_new_tab_ui/components/default/bitcoinDotCom/assets/icons.ts @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License. v. 2.0. If a copy of the MPL was not distributed with this file. + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +export default { + 'BCH': 'data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjMyIiB3aWR0aD0iMzIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48Y2lyY2xlIGN4PSIxNiIgY3k9IjE2IiBmaWxsPSIjMDBDNDhCIiByPSIxNiIvPjxwYXRoIGQ9Ik0yMS4yMDcgMTAuNTM0Yy0uNzc2LTEuOTcyLTIuNzIyLTIuMTUtNC45ODgtMS43MWwtLjgwNy0yLjgxMy0xLjcxMi40OTEuNzg2IDIuNzRjLS40NS4xMjgtLjkwOC4yNy0xLjM2My40MWwtLjc5LTIuNzU4LTEuNzExLjQ5LjgwNSAyLjgxM2MtLjM2OC4xMTQtLjczLjIyNi0xLjA4NS4zMjhsLS4wMDMtLjAxLTIuMzYyLjY3Ny41MjUgMS44M3MxLjI1OC0uMzg4IDEuMjQzLS4zNThjLjY5NC0uMTk5IDEuMDM1LjEzOSAxLjIuNDY4bC45MiAzLjIwNGMuMDQ3LS4wMTMuMTEtLjAyOS4xODQtLjA0bC0uMTgxLjA1MiAxLjI4NyA0LjQ5Yy4wMzIuMjI3LjAwNC42MTItLjQ4Ljc1Mi4wMjcuMDEzLTEuMjQ2LjM1Ni0xLjI0Ni4zNTZsLjI0NyAyLjE0MyAyLjIyOC0uNjRjLjQxNS0uMTE3LjgyNS0uMjI3IDEuMjI2LS4zNGwuODE3IDIuODQ1IDEuNzEtLjQ5LS44MDctMi44MTVhNjUuNzQgNjUuNzQgMCAwIDAgMS4zNzItLjM4bC44MDIgMi44MDMgMS43MTMtLjQ5MS0uODE0LTIuODRjMi44MzEtLjk5MSA0LjYzOC0yLjI5NCA0LjExMy01LjA3LS40MjItMi4yMzQtMS43MjQtMi45MTItMy40NzEtMi44MzYuODQ4LS43OSAxLjIxMy0xLjg1OC42NDItMy4zem0tLjY1IDYuNzdjLjYxIDIuMTI3LTMuMSAyLjkyOS00LjI2IDMuMjYzbC0xLjA4MS0zLjc3YzEuMTYtLjMzMyA0LjcwNC0xLjcxIDUuMzQuNTA4em0tMi4zMjItNS4wOWMuNTU0IDEuOTM1LTIuNTQ3IDIuNTgtMy41MTQgMi44NTdsLS45OC0zLjQxOWMuOTY2LS4yNzcgMy45MTUtMS40NTUgNC40OTQuNTYzeiIgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9nPjwvc3ZnPgo=', + 'BTC': 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48Y2lyY2xlIGN4PSIxNiIgY3k9IjE2IiByPSIxNiIgZmlsbD0iI0Y3OTMxQSIvPjxwYXRoIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0ibm9uemVybyIgZD0iTTIzLjE4OSAxNC4wMmMuMzE0LTIuMDk2LTEuMjgzLTMuMjIzLTMuNDY1LTMuOTc1bC43MDgtMi44NC0xLjcyOC0uNDMtLjY5IDIuNzY1Yy0uNDU0LS4xMTQtLjkyLS4yMi0xLjM4NS0uMzI2bC42OTUtMi43ODNMMTUuNTk2IDZsLS43MDggMi44MzljLS4zNzYtLjA4Ni0uNzQ2LS4xNy0xLjEwNC0uMjZsLjAwMi0uMDA5LTIuMzg0LS41OTUtLjQ2IDEuODQ2czEuMjgzLjI5NCAxLjI1Ni4zMTJjLjcuMTc1LjgyNi42MzguODA1IDEuMDA2bC0uODA2IDMuMjM1Yy4wNDguMDEyLjExLjAzLjE4LjA1N2wtLjE4My0uMDQ1LTEuMTMgNC41MzJjLS4wODYuMjEyLS4zMDMuNTMxLS43OTMuNDEuMDE4LjAyNS0xLjI1Ni0uMzEzLTEuMjU2LS4zMTNsLS44NTggMS45NzggMi4yNS41NjFjLjQxOC4xMDUuODI4LjIxNSAxLjIzMS4zMThsLS43MTUgMi44NzIgMS43MjcuNDMuNzA4LTIuODRjLjQ3Mi4xMjcuOTMuMjQ1IDEuMzc4LjM1N2wtLjcwNiAyLjgyOCAxLjcyOC40My43MTUtMi44NjZjMi45NDguNTU4IDUuMTY0LjMzMyA2LjA5Ny0yLjMzMy43NTItMi4xNDYtLjAzNy0zLjM4NS0xLjU4OC00LjE5MiAxLjEzLS4yNiAxLjk4LTEuMDAzIDIuMjA3LTIuNTM4em0tMy45NSA1LjUzOGMtLjUzMyAyLjE0Ny00LjE0OC45ODYtNS4zMi42OTVsLjk1LTMuODA1YzEuMTcyLjI5MyA0LjkyOS44NzIgNC4zNyAzLjExem0uNTM1LTUuNTY5Yy0uNDg3IDEuOTUzLTMuNDk1Ljk2LTQuNDcuNzE3bC44Ni0zLjQ1Yy45NzUuMjQzIDQuMTE4LjY5NiAzLjYxIDIuNzMzeiIvPjwvZz48L3N2Zz4=', + 'ETH': 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48Y2lyY2xlIGN4PSIxNiIgY3k9IjE2IiByPSIxNiIgZmlsbD0iIzYyN0VFQSIvPjxnIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZmlsbC1vcGFjaXR5PSIuNjAyIiBkPSJNMTYuNDk4IDR2OC44N2w3LjQ5NyAzLjM1eiIvPjxwYXRoIGQ9Ik0xNi40OTggNEw5IDE2LjIybDcuNDk4LTMuMzV6Ii8+PHBhdGggZmlsbC1vcGFjaXR5PSIuNjAyIiBkPSJNMTYuNDk4IDIxLjk2OHY2LjAyN0wyNCAxNy42MTZ6Ii8+PHBhdGggZD0iTTE2LjQ5OCAyNy45OTV2LTYuMDI4TDkgMTcuNjE2eiIvPjxwYXRoIGZpbGwtb3BhY2l0eT0iLjIiIGQ9Ik0xNi40OTggMjAuNTczbDcuNDk3LTQuMzUzLTcuNDk3LTMuMzQ4eiIvPjxwYXRoIGZpbGwtb3BhY2l0eT0iLjYwMiIgZD0iTTkgMTYuMjJsNy40OTggNC4zNTN2LTcuNzAxeiIvPjwvZz48L2c+PC9zdmc+', + 'LTC': 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxjaXJjbGUgY3g9IjE2IiBjeT0iMTYiIHI9IjE2IiBmaWxsPSIjQkZCQkJCIi8+PHBhdGggZmlsbD0iI0ZGRiIgZD0iTTEwLjQyNyAxOS4yMTRMOSAxOS43NjhsLjY4OC0yLjc1OSAxLjQ0NC0uNThMMTMuMjEzIDhoNS4xMjlsLTEuNTE5IDYuMTk2IDEuNDEtLjU3MS0uNjggMi43NS0xLjQyNy41NzEtLjg0OCAzLjQ4M0gyM0wyMi4xMjcgMjRIOS4yNTJ6Ii8+PC9nPjwvc3ZnPg==', + 'XLM': 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDI1MCAyNTAiPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggZD0iTTIwMywyNi4xNmwtMjguNDYsMTQuNS0xMzcuNDMsNzBhODIuNDksODIuNDksMCwwLDEtLjctMTAuNjlBODEuODcsODEuODcsMCwwLDEsMTU4LjIsMjguNmwxNi4yOS04LjMsMi40My0xLjI0QTEwMCwxMDAsMCwwLDAsMTguMTgsMTAwcTAsMy44Mi4yOSw3LjYxYTE4LjE5LDE4LjE5LDAsMCwxLTkuODgsMTcuNThMMCwxMjkuNTdWMTUwbDI1LjI5LTEyLjg5LDAsMCw4LjE5LTQuMTgsOC4wNy00LjExdjBMMTg2LjQzLDU1bDE2LjI4LTguMjksMzMuNjUtMTcuMTVWOS4xNFoiLz48cGF0aCBkPSJNMjM2LjM2LDUwLDQ5Ljc4LDE0NSwzMy41LDE1My4zMSwwLDE3MC4zOHYyMC40MWwzMy4yNy0xNi45NSwyOC40Ni0xNC41TDE5OS4zLDg5LjI0QTgzLjQ1LDgzLjQ1LDAsMCwxLDIwMCwxMDAsODEuODcsODEuODcsMCwwLDEsNzguMDksMTcxLjM2bC0xLC41My0xNy42Niw5QTEwMCwxMDAsMCwwLDAsMjE4LjE4LDEwMGMwLTIuNTctLjEtNS4xNC0uMjktNy42OGExOC4yLDE4LjIsMCwwLDEsOS44Ny0xNy41OGw4LjYtNC4zOFoiLz48L2c+PC9nPjwvc3ZnPgo=', + 'XRP': 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIj48Y2lyY2xlIGN4PSIxNiIgY3k9IjE2IiByPSIxNiIgZmlsbD0iIzIzMjkyRiIvPjxwYXRoIGQ9Ik0yMy4wNyA4aDIuODlsLTYuMDE1IDUuOTU3YTUuNjIxIDUuNjIxIDAgMCAxLTcuODkgMEw2LjAzNSA4SDguOTNsNC41NyA0LjUyM2EzLjU1NiAzLjU1NiAwIDAgMCA0Ljk5NiAwTDIzLjA3IDh6TTguODk1IDI0LjU2M0g2bDYuMDU1LTUuOTkzYTUuNjIxIDUuNjIxIDAgMCAxIDcuODkgMEwyNiAyNC41NjJoLTIuODk1TDE4LjUgMjBhMy41NTYgMy41NTYgMCAwIDAtNC45OTYgMGwtNC42MSA0LjU2M3oiIGZpbGw9IiNGRkYiLz48L2c+PC9zdmc+' +} diff --git a/components/brave_new_tab_ui/components/default/bitcoinDotCom/assets/logo.png b/components/brave_new_tab_ui/components/default/bitcoinDotCom/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..833d52ef266a0ea7217d4a2e303707b8a364ac0b GIT binary patch literal 546 zcmV+-0^R+IP)?Z1&#p$K0qZ5Fd7wnyT-r>qA%-$%sM4CwMn&Jz}I4?@YRd2M4K?xqX4dy z)Z%0y{RfCslM9MU7#J8+fNbURk^&IB1IQLB0y+xFo&#d1L)aTY>?8>L2v|)>kTZ~d z1ISiL&PW8B!T1Bn)=5GVPe)=y?F9jDm(1dVoWx3n0Am8KB?FYCR+NBf=lp`oqRjM+ z5(P(KD5WZR<|XUtC>R+Snlmtf!W^Rdb09b@8O6>Z#G3CjFxc+@|NsAPgqYWU28NA5 zom@K+Vj30<41(MY3@3ILBo-xtg_wbuDJ_kG;nQ*k2Hr>p2H^`pJ<-K!#ztUyMjj^y z2G)E3|9@6vU|^rj!0>m%|Np-i{r~@WF;Mdz28IK902Lu+?ls5J`v3p{d`Uz>R7eeD z_zcIXkywfU|K*s7RZFhAQ40u$+|Umq z7K+k2IDq~);tQvtA4H=*z!$$@xuG9KEDQ{I%5!4U`9G!u_`+%E2hpex@Wn4!jt)Tt kY+4X>2AK*N85w>80Bi0QaQ-cpmH+?%07*qoM6N<$g1c4T=l}o! literal 0 HcmV?d00001 diff --git a/components/brave_new_tab_ui/components/default/bitcoinDotCom/fiat.ts b/components/brave_new_tab_ui/components/default/bitcoinDotCom/fiat.ts new file mode 100644 index 000000000000..4b0209dabb00 --- /dev/null +++ b/components/brave_new_tab_ui/components/default/bitcoinDotCom/fiat.ts @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License. v. 2.0. If a copy of the MPL was not distributed with this file. + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +export default { + 'AED': 'United Arab Emirates dirham', + 'ARS': 'Argentine peso', + 'AUD': 'Australian dollar', + 'AZN': 'Azerbaijani manat', + 'BGN': 'Bulgarian lev', + 'BRL': 'Brazilian real', + 'CAD': 'Canadian dollar', + 'CHF': 'Swiss franc', + 'CLP': 'Chilean peso', + 'COP': 'Colombian peso', + 'CRC': 'Costa Rican colon', + 'CZK': 'Czech koruna', + 'DKK': 'Danish krone', + 'DOP': 'Dominican peso', + 'EUR': 'Euro', + 'GBP': 'Pound sterling', + 'GEL': 'Georgian lari', + 'HKD': 'Hong Kong dollar', + 'HUF': 'Hungarian forint', + 'IDR': 'Indonesian rupiah', + 'ILS': 'Israeli new shekel', + 'INR': 'Indian rupee', + 'JPY': 'Japanese yen', + 'KRW': 'South Korean won', + 'KZT': 'Kazakhstani tenge', + 'MAD': 'Moroccan dirham', + 'MDL': 'Moldovan leu', + 'MXN': 'Mexican peso', + 'MYR': 'Malaysian ringgit', + 'NAD': 'Namibian dollar', + 'NGN': 'Nigerian naira', + 'NOK': 'Norwegian krone', + 'NZD': 'New Zealand dollar', + 'PEN': 'Peruvian sol', + 'PHP': 'Philippine peso', + 'PLN': 'Polish złoty', + 'QAR': 'Qatari riyal', + 'RON': 'Romanian leu', + 'RUB': 'Russian ruble', + 'SEK': 'Swedish krona', + 'SGD': 'Singapore dollar', + 'TRY': 'Turkish lira', + 'TWD': 'New Taiwan dollar', + 'UAH': 'Ukrainian hryvnia', + 'USD': 'United States dollar', + 'UYU': 'Uruguayan peso', + 'UZS': 'Uzbekistan som', + 'VND': 'Vietnamese đồng', + 'ZAR': 'South African rand' +} diff --git a/components/brave_new_tab_ui/components/default/bitcoinDotCom/index.tsx b/components/brave_new_tab_ui/components/default/bitcoinDotCom/index.tsx new file mode 100644 index 000000000000..51a75b7f99cc --- /dev/null +++ b/components/brave_new_tab_ui/components/default/bitcoinDotCom/index.tsx @@ -0,0 +1,311 @@ +/* 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 * as Styled from './style' +import { StyledTitleTab } from '../widgetTitleTab' +import assetIcons from './assets/icons' +import fiatData from './fiat' +import BitcoinDotComLogo from './assets/logo.png' +import { CaratDownIcon } from 'brave-ui/components/icons' +import { getLocale } from '../../../../common/locale' + +interface State { + selectedAsset: string + selectedFiat: string + currentAmount: string + assetsShowing: boolean + fiatCurrenciesShowing: boolean +} + +interface Props { + showContent: boolean + stackPosition: number + onShowContent: () => void + onBuyCrypto: () => void + onInteraction: () => void +} + +class BitcoinDotCom extends React.PureComponent { + private fiatCurrencies: Record = fiatData + private assets: Record = { + 'BCH': 'Bitcoin Cash', + 'BTC': 'Bitcoin', + 'ETH': 'Ethereum', + 'LTC': 'Litecoin', + 'XLM': 'Stellar', + 'XRP': 'Ripple' + } + + constructor (props: Props) { + super(props) + this.state = { + selectedAsset: 'BCH', + selectedFiat: 'EUR', + currentAmount: '', + assetsShowing: false, + fiatCurrenciesShowing: false + } + } + + renderTitle () { + const { showContent } = this.props + + return ( + + + + + + + {'Bitcoin.com'} + + + + ) + } + + toggleAssetsShowing = () => { + this.setState({ + assetsShowing: !this.state.assetsShowing + }) + this.props.onInteraction() + } + + setSelectedAsset = (asset: string) => { + this.setState({ + assetsShowing: false, + selectedAsset: asset + }) + } + + setCurrentAmount = (event: any) => { + const { target } = event + const { value } = target + const validation = /^[0-9]+\.?[0-9]?[0-9]?$/ + + this.props.onInteraction() + + if (!validation.test(value) && value !== '') { + event.preventDefault() + return + } + + this.setState({ currentAmount: value }) + } + + toggleFiatCurrenciesShowing = () => { + this.setState({ + fiatCurrenciesShowing: !this.state.fiatCurrenciesShowing + }) + this.props.onInteraction() + } + + setSelectedFiat = (fiat: string) => { + this.setState({ + fiatCurrenciesShowing: false, + selectedFiat: fiat + }) + } + + hideFiatCurrencyOptions = () => { + this.setState({ + fiatCurrenciesShowing: false + }) + } + + openBuyURL = () => { + const { + currentAmount, + selectedAsset, + selectedFiat + } = this.state + + const amount = currentAmount.trim() + const asset = selectedAsset.toLowerCase() + const fiat = selectedFiat.toLowerCase() + + if (amount.length) { + window.open(`https://bitcoincom.moonpay.io/?currencyCode=${asset}&baseCurrencyCode=${fiat}&baseCurrencyAmount=${amount}`, '_blank', 'noopener') + } + + this.props.onBuyCrypto() + } + + openInfoURL = () => { + window.open('https://bitcoincom.moonpay.io/', '_blank', 'noopener') + this.props.onInteraction() + } + + renderCurrencyInput () { + const { assetsShowing, selectedAsset } = this.state + + return ( + <> + + + {getLocale('bitcoinDotComWidgetCurrency')} + + + + + + + {this.assets[selectedAsset]} + + + {selectedAsset} + + + + + + + {assetsShowing && this.renderCurrencyItems()} + + ) + } + + renderCurrencyItems () { + const { selectedAsset } = this.state + + return ( + + {Object.keys(this.assets).map((asset: string) => { + if (asset === selectedAsset) { + return null + } + + return ( + + + + {this.assets[asset]} + + + {asset} + + + ) + })} + + ) + } + + renderAmountInput () { + const { currentAmount, fiatCurrenciesShowing, selectedFiat } = this.state + + return ( + <> + + + {getLocale('bitcoinDotComWidgetAmount')} + + + + + + + {selectedFiat} + + + + + + {fiatCurrenciesShowing && this.renderFiatItems()} + + + ) + } + + renderFiatItems () { + const { selectedFiat } = this.state + + return ( + + {Object.keys(this.fiatCurrencies).map((fiat: string) => { + if (fiat === selectedFiat) { + return null + } + + return ( + + + {fiat} + + + {this.fiatCurrencies[fiat]} + + + ) + })} + + ) + } + + renderFooter () { + return ( + + + {getLocale('bitcoinDotComWidgetBuy')} + + + + {getLocale('bitcoinDotComWidgetFooterCopyOne')} + + + {getLocale('bitcoinDotComWidgetFooterCopyTwo')} + + + + ) + } + + renderTitleTab () { + const { onShowContent, stackPosition } = this.props + + return ( + + {this.renderTitle()} + + ) + } + + render () { + if (!this.props.showContent) { + return this.renderTitleTab() + } + + return ( + + {this.renderTitle()} + {this.renderCurrencyInput()} + {this.renderAmountInput()} + {this.renderFooter()} + + ) + } +} + +export const BitcoinDotComWidget = createWidget(BitcoinDotCom) diff --git a/components/brave_new_tab_ui/components/default/bitcoinDotCom/style.ts b/components/brave_new_tab_ui/components/default/bitcoinDotCom/style.ts new file mode 100644 index 000000000000..0cebd60ded89 --- /dev/null +++ b/components/brave_new_tab_ui/components/default/bitcoinDotCom/style.ts @@ -0,0 +1,245 @@ +/* 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, { css } from 'styled-components' + +interface StyleProps { + isFiat?: boolean + contentShowing?: boolean + dropdownShowing?: boolean +} + +export const WidgetWrapper = styled('div')` + padding: 6px 20px 12px 20px; + border-radius: 6px; + position: relative; + font-family: ${p => p.theme.fontFamily.body}; + overflow: hidden; + min-width: 284px; + background: #fff; +` + +export const Header = styled<{}, 'div'>('div')` + text-align: left; +` + +export const Title = styled('div')` + margin-top: 6px; + display: flex; + justify-content: flex-start; + align-items: center; + font-size: 18px; + font-weight: 600; + color: ${p => p.contentShowing ? '#000' : '#fff'}; + font-family: ${p => p.theme.fontFamily.heading}; +` + +export const BitcoinDotComIcon = styled<{}, 'div'>('div')` + width: 27px; + height: 27px; + margin-right: 7px; + margin-left: 2px; +` + +export const TitleText = styled<{}, 'div'>('div')` + margin-top: -2px; +` + +export const InputHeader = styled<{}, 'div'>('div')` + margin-top: 15px; +` + +export const InputLabel = styled<{}, 'span'>('span')` + color: #7E8496; + font-weight: bold; +` + +export const Dropdown = styled('div')` + border: 1px solid lightgray; + border-radius: 6px; + margin-top: 5px; + cursor: pointer; + + ${(p) => p.dropdownShowing + ? css` + border-bottom: none; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + ` : '' + } +` + +export const AssetItem = styled<{}, 'div'>('div')` + padding: 7px; + cursor: pointer; +` + +export const AssetImage = styled<{}, 'img'>('img')` + width: 24px; +` + +export const AssetTitle = styled<{}, 'span'>('span')` + font-weight: bold; + font-size: 14px; + margin: 0px 5px; + vertical-align: super; +` + +export const AssetSymbol = styled('span')` + font-size: 12px; + border-radius: 4px; + background: lightgray; + padding: 2px 7px; + margin-left: 10px; + vertical-align: super; + + ${(p) => p.dropdownShowing + ? css` + float: right; + margin-top: 5px; + min-width: 40px; + text-align: center; + ` : '' + } +` + +export const DropdownIcon = styled('div')` + width: 15px; + float: right; + margin-top: ${p => p.isFiat ? 1 : -28}px; + margin-right: ${p => p.isFiat ? 3 : 10}px; +` + +export const CurrencyItems = styled<{}, 'div'>('div')` + z-index: 1; + background: #fff; + overflow-y: scroll; + position: absolute; + min-width: 244px; + max-height: 155px; + border: 1px solid lightgray; + border-top: none; + left: auto; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; +` + +export const AmountInputWrapper = styled<{}, 'div'>('div')` + margin-top: 5px; +` + +export const AmountInput = styled('input')` + font-weight: bold; + height: 40px; + border: 1px solid lightgray; + border-radius: 6px; + font-size: 15px; + padding-left: 10px; + border-right: none; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; + + ${(p) => p.dropdownShowing + ? css` + border-bottom-left-radius: 0px; + ` : '' + } +` + +export const FiatLabel = styled<{}, 'span'>('span')` + color: #000; + font-weight: bold; + margin-left: 8px; + font-size: 15px; +` + +export const FiatDropdown = styled('div')` + border-radius: 6px; + border: 1px solid lightgray; + display: inline-block; + height: 40px; + vertical-align: bottom; + width: 64px; + padding-top: 10px; + cursor: pointer; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + + ${(p) => p.dropdownShowing + ? css` + border-bottom: none; + border-bottom-right-radius: 0px; + ` : '' + } +` + +export const FiatItems = styled<{}, 'div'>('div')` + z-index: 1; + background: #fff; + overflow-y: scroll; + position: absolute; + min-width: 244px; + max-height: 90px; + border: 1px solid lightgray; + border-top: none; + left: auto; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; +` + +export const FiatItem = styled<{}, 'div'>('div')` + color: #000; + padding: 7px 0px 7px 7px; + cursor: pointer; +` + +export const FiatSymbol = styled<{}, 'div'>('div')` + font-size: 12px; + border-radius: 4px; + background: lightgray; + padding: 2px 5px 2px 7px; + display: inline-block; + min-width: 40px; +` + +export const FiatName = styled<{}, 'span'>('span')` + font-size: 13px; + font-weight: bold; + margin-left: 8px; +` + +export const FooterWrapper = styled<{}, 'div'>('div')` + margin-top: 15px; +` + +export const BuyButton = styled<{}, 'button'>('button')` + width: 100%; + border-radius: 15px; + border: none; + background: #00D68F; + color: white; + padding: 12px 0px; + font-weight: bold; + font-size: 13px; + cursor: pointer; +` + +export const FooterInfo = styled<{}, 'div'>('div')` + font-size: 10px; + text-align: center; + margin-top: 15px; +` + +export const LinkLabel = styled<{}, 'span'>('span')` + font-weight: bold; + color: #000; + margin-right: 5px; +` + +export const Link = styled<{}, 'span'>('span')` + font-weight: bold; + color: #00C985; + text-decoration: underline; + cursor: pointer; +` diff --git a/components/brave_new_tab_ui/components/default/index.ts b/components/brave_new_tab_ui/components/default/index.ts index 31eb7f296795..a15f2c960674 100644 --- a/components/brave_new_tab_ui/components/default/index.ts +++ b/components/brave_new_tab_ui/components/default/index.ts @@ -13,6 +13,7 @@ import { BinanceWidget } from './binance' import { TogetherWidget } from './together' import { AddCardWidget } from './addCard' import { GeminiWidget } from './gemini' +import { BitcoinDotComWidget } from './bitcoinDotCom' import createWidget from './widget' export * from './page' @@ -34,5 +35,6 @@ export { TogetherWidget, AddCardWidget, GeminiWidget, + BitcoinDotComWidget, createWidget } diff --git a/components/brave_new_tab_ui/components/default/widget/assets/ellipsis.tsx b/components/brave_new_tab_ui/components/default/widget/assets/ellipsis.tsx index db90e421face..62e26b1decbb 100644 --- a/components/brave_new_tab_ui/components/default/widget/assets/ellipsis.tsx +++ b/components/brave_new_tab_ui/components/default/widget/assets/ellipsis.tsx @@ -4,11 +4,16 @@ import * as React from 'react' -export default class EllipsisIcon extends React.Component { +interface Props { + lightWidget?: boolean +} + +export default class EllipsisIcon extends React.Component { render () { + const fillColor = this.props.lightWidget ? '#495057' : '#ffffff' return ( - + ) } diff --git a/components/brave_new_tab_ui/components/default/widget/index.tsx b/components/brave_new_tab_ui/components/default/widget/index.tsx index 4f5f4f394428..08d6c262c577 100644 --- a/components/brave_new_tab_ui/components/default/widget/index.tsx +++ b/components/brave_new_tab_ui/components/default/widget/index.tsx @@ -18,6 +18,7 @@ export interface WidgetProps { widgetTitle?: string hideMenu?: boolean isForeground?: boolean + lightWidget?: boolean onLearnMore?: () => void onDisconnect?: () => void onRefreshData?: () => void @@ -55,6 +56,7 @@ const createWidget =

(WrappedComponent: React.ComponentType

widgetTitle, hideMenu, isForeground, + lightWidget, onLearnMore, onDisconnect, onRefreshData @@ -87,6 +89,7 @@ const createWidget =

(WrappedComponent: React.ComponentType

hideWidget={hideWidget as HideWidgetFunction} persistWidget={this.persistWidget} unpersistWidget={this.unpersistWidget} + lightWidget={lightWidget} /> } diff --git a/components/brave_new_tab_ui/components/default/widget/widgetMenu.tsx b/components/brave_new_tab_ui/components/default/widget/widgetMenu.tsx index 7bcfee157a4c..00e41ab780f7 100644 --- a/components/brave_new_tab_ui/components/default/widget/widgetMenu.tsx +++ b/components/brave_new_tab_ui/components/default/widget/widgetMenu.tsx @@ -26,6 +26,7 @@ interface Props { onLearnMore?: () => void onDisconnect?: () => void onRefreshData?: () => void + lightWidget?: boolean } interface State { @@ -87,6 +88,7 @@ export default class WidgetMenu extends React.PureComponent { widgetMenuPersist, widgetTitle, isForeground, + lightWidget, onLearnMore, onDisconnect, onRefreshData @@ -98,7 +100,7 @@ export default class WidgetMenu extends React.PureComponent { - + {showMenu && { saveShowBinance={PreferencesAPI.saveShowBinance} saveShowAddCard={PreferencesAPI.saveShowAddCard} saveShowGemini={PreferencesAPI.saveShowGemini} + saveShowBitcoinDotCom={PreferencesAPI.saveShowBitcoinDotCom} saveBrandedWallpaperOptIn={PreferencesAPI.saveBrandedWallpaperOptIn} /> ) @@ -62,7 +67,7 @@ const mapStateToProps = (state: NewTab.ApplicationState) => ({ }) const mapDispatchToProps = (dispatch: Dispatch) => { - const allActions = Object.assign({}, newTabActions, gridSitesActions, binanceActions, rewardsActions, geminiActions) + const allActions = Object.assign({}, newTabActions, gridSitesActions, binanceActions, rewardsActions, geminiActions, bitcoinDotComActions) return { actions: bindActionCreators(allActions, dispatch) } diff --git a/components/brave_new_tab_ui/containers/newTab/index.tsx b/components/brave_new_tab_ui/containers/newTab/index.tsx index 09707d3e0a4b..761c1ad2d244 100644 --- a/components/brave_new_tab_ui/containers/newTab/index.tsx +++ b/components/brave_new_tab_ui/containers/newTab/index.tsx @@ -16,7 +16,8 @@ import { TogetherWidget as Together, BinanceWidget as Binance, AddCardWidget as AddCard, - GeminiWidget as Gemini + GeminiWidget as Gemini, + BitcoinDotComWidget as BitcoinDotCom } from '../../components/default' import * as Page from '../../components/default/page' import BrandedWallpaperLogo from '../../components/default/brandedWallpaper/logo' @@ -29,14 +30,10 @@ import { generateQRData } from '../../binance-utils' // Types import { SortEnd } from 'react-sortable-hoc' -import * as newTabActions from '../../actions/new_tab_actions' -import * as gridSitesActions from '../../actions/grid_sites_actions' -import * as binanceActions from '../../actions/binance_actions' -import * as geminiActions from '../../actions/gemini_actions' -import * as rewardsActions from '../../actions/rewards_actions' import { getLocale } from '../../../common/locale' import currencyData from '../../components/default/binance/data' import geminiData from '../../components/default/gemini/data' +import { NewTabActions } from '../../constants/new_tab_types' // NTP features import Settings from './settings' @@ -44,7 +41,7 @@ import Settings from './settings' interface Props { newTabData: NewTab.State gridSitesData: NewTab.GridSitesState - actions: typeof newTabActions & typeof gridSitesActions & typeof binanceActions & typeof rewardsActions & typeof geminiActions + actions: NewTabActions saveShowBackgroundImage: (value: boolean) => void saveShowClock: (value: boolean) => void saveShowTopSites: (value: boolean) => void @@ -54,6 +51,7 @@ interface Props { saveShowBinance: (value: boolean) => void saveShowAddCard: (value: boolean) => void saveShowGemini: (value: boolean) => void + saveShowBitcoinDotCom: (value: boolean) => void saveBrandedWallpaperOptIn: (value: boolean) => void } @@ -143,7 +141,8 @@ class NewTabPage extends React.Component { const oldShowBinance = prevProps.newTabData.showBinance const oldShowTogether = prevProps.newTabData.showTogether const oldShowGemini = prevProps.newTabData.showGemini - const { showRewards, showBinance, showTogether, showGemini } = this.props.newTabData + const oldShowBitcoinDotCom = prevProps.newTabData.showBitcoinDotCom + const { showRewards, showBinance, showTogether, showGemini, showBitcoinDotCom } = this.props.newTabData if (!oldShowRewards && showRewards) { this.props.actions.setForegroundStackWidget('rewards') @@ -161,6 +160,10 @@ class NewTabPage extends React.Component { this.props.actions.removeStackWidget('gemini') } else if (!oldShowGemini && showGemini) { this.props.actions.setForegroundStackWidget('gemini') + } else if (oldShowBitcoinDotCom && !showBitcoinDotCom) { + this.props.actions.removeStackWidget('bitcoinDotCom') + } else if (!oldShowBitcoinDotCom && showBitcoinDotCom) { + this.props.actions.setForegroundStackWidget('bitcoinDotCom') } } @@ -305,6 +308,16 @@ class NewTabPage extends React.Component { } } + toggleShowBitcoinDotCom = () => { + const { showBitcoinDotCom } = this.props.newTabData + + if (!showBitcoinDotCom) { + this.props.saveShowAddCard(true) + } + + this.props.saveShowBitcoinDotCom(!showBitcoinDotCom) + } + onBinanceClientUrl = (clientUrl: string) => { this.props.actions.onBinanceClientUrl(clientUrl) } @@ -373,6 +386,14 @@ class NewTabPage extends React.Component { } } + onBuyBitcoinDotComCrypto = () => { + this.props.actions.buyBitcoinDotComCrypto() + } + + onInteractionBitcoinDotCom = () => { + this.props.actions.interactionBitcoinDotCom() + } + onBinanceUserTLD = (userTLD: NewTab.BinanceTLD) => { this.props.actions.onBinanceUserTLD(userTLD) } @@ -617,7 +638,9 @@ class NewTabPage extends React.Component { showBinance, showTogether, showGemini, - geminiSupported + showBitcoinDotCom, + geminiSupported, + bitcoinDotComSupported } = this.props.newTabData const lookup = { 'rewards': { @@ -635,6 +658,10 @@ class NewTabPage extends React.Component { 'gemini': { display: showGemini && geminiSupported, render: this.renderGeminiWidget.bind(this) + }, + 'bitcoinDotCom': { + display: showBitcoinDotCom && bitcoinDotComSupported, + render: this.renderBitcoinDotComWidget.bind(this) } } @@ -662,13 +689,16 @@ class NewTabPage extends React.Component { showBinance, showTogether, geminiSupported, - showGemini + showGemini, + showBitcoinDotCom, + bitcoinDotComSupported } = this.props.newTabData return [ showRewards, togetherSupported && showTogether, binanceState.binanceSupported && showBinance, - geminiSupported && showGemini + geminiSupported && showGemini, + showBitcoinDotCom && bitcoinDotComSupported ].every((widget: boolean) => !widget) } @@ -859,6 +889,34 @@ class NewTabPage extends React.Component { ) } + renderBitcoinDotComWidget (showContent: boolean, position: number) { + const { newTabData } = this.props + const { showBitcoinDotCom, bitcoinDotComSupported, textDirection } = newTabData + + if (!showBitcoinDotCom || !bitcoinDotComSupported) { + return null + } + + return( + + ) + } + render () { const { newTabData, gridSitesData, actions } = this.props const { showSettingsMenu, focusMoreCards } = this.state @@ -990,6 +1048,9 @@ class NewTabPage extends React.Component { toggleShowGemini={this.toggleShowGemini} showGemini={newTabData.showGemini} focusMoreCards={focusMoreCards} + bitcoinDotComSupported={newTabData.bitcoinDotComSupported} + showBitcoinDotCom={newTabData.showBitcoinDotCom} + toggleShowBitcoinDotCom={this.toggleShowBitcoinDotCom} /> ) diff --git a/components/brave_new_tab_ui/containers/newTab/settings.tsx b/components/brave_new_tab_ui/containers/newTab/settings.tsx index bb251ce40ef2..218823f63971 100644 --- a/components/brave_new_tab_ui/containers/newTab/settings.tsx +++ b/components/brave_new_tab_ui/containers/newTab/settings.tsx @@ -49,6 +49,7 @@ export interface Props { toggleShowBinance: () => void toggleShowGemini: () => void toggleBrandedWallpaperOptIn: () => void + toggleShowBitcoinDotCom: () => void showBackgroundImage: boolean showStats: boolean showClock: boolean @@ -63,6 +64,8 @@ export interface Props { showGemini: boolean geminiSupported: boolean focusMoreCards: boolean + showBitcoinDotCom: boolean + bitcoinDotComSupported: boolean } type ActiveTabType = 'BackgroundImage' | 'BraveStats' | 'TopSites' | 'Clock' | 'MoreCards' @@ -190,7 +193,10 @@ export default class Settings extends React.PureComponent { togetherSupported, toggleShowGemini, geminiSupported, - showGemini + showGemini, + toggleShowBitcoinDotCom, + showBitcoinDotCom, + bitcoinDotComSupported } = this.props const { activeTab } = this.state @@ -289,6 +295,9 @@ export default class Settings extends React.PureComponent { showGemini={showGemini} toggleShowGemini={toggleShowGemini} geminiSupported={geminiSupported} + bitcoinDotComSupported={bitcoinDotComSupported} + showBitcoinDotCom={showBitcoinDotCom} + toggleShowBitcoinDotCom={toggleShowBitcoinDotCom} /> ) : null } diff --git a/components/brave_new_tab_ui/containers/newTab/settings/assets/bitcoin-dot-com.png b/components/brave_new_tab_ui/containers/newTab/settings/assets/bitcoin-dot-com.png new file mode 100644 index 0000000000000000000000000000000000000000..03bf456a28b6ebdf4655bef2b27f27f64508ba24 GIT binary patch literal 28003 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 void geminiSupported: boolean showGemini: boolean + showBitcoinDotCom: boolean + bitcoinDotComSupported: boolean + toggleShowBitcoinDotCom: () => void } class MoreCardsSettings extends React.PureComponent { @@ -79,7 +83,10 @@ class MoreCardsSettings extends React.PureComponent { showRewards, geminiSupported, toggleShowGemini, - showGemini + showGemini, + toggleShowBitcoinDotCom, + showBitcoinDotCom, + bitcoinDotComSupported } = this.props return ( @@ -131,6 +138,22 @@ class MoreCardsSettings extends React.PureComponent { : null } + { + bitcoinDotComSupported + ? + + + + {'Bitcoin.com'} + + + {getLocale('bitcoinDotComWidgetDesc')} + + + {this.renderToggleButton(showBitcoinDotCom, toggleShowBitcoinDotCom, false)} + + : null + } diff --git a/components/brave_new_tab_ui/reducers/bitcoin_dot_com_reducer.ts b/components/brave_new_tab_ui/reducers/bitcoin_dot_com_reducer.ts new file mode 100644 index 000000000000..095326e7a18a --- /dev/null +++ b/components/brave_new_tab_ui/reducers/bitcoin_dot_com_reducer.ts @@ -0,0 +1,33 @@ +/* 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 { Reducer } from 'redux' +import { types } from '../constants/bitcoin_dot_com_types' + +function performSideEffect (fn: () => void): void { + window.setTimeout(() => fn(), 0) +} + +const bitcoinDotComReducer: Reducer = (state: NewTab.State, action) => { + switch (action.type) { + case types.BUY_BITCOIN_DOT_COM_CRYPTO: + performSideEffect(async function () { + chrome.moonpay.onBuyBitcoinDotComCrypto() + }) + break + + case types.INTERACTION_BITCOIN_DOT_COM: + performSideEffect(async function () { + chrome.moonpay.onInteractionBitcoinDotCom() + }) + break + + default: + break + } + + return state +} + +export default bitcoinDotComReducer diff --git a/components/brave_new_tab_ui/reducers/index.ts b/components/brave_new_tab_ui/reducers/index.ts index b519c4781006..cc86e74dd700 100644 --- a/components/brave_new_tab_ui/reducers/index.ts +++ b/components/brave_new_tab_ui/reducers/index.ts @@ -12,6 +12,7 @@ import gridSitesReducer from './grid_sites_reducer' import binanceReducer from './binance_reducer' import rewardsReducer from './rewards_reducer' import geminiReducer from './gemini_reducer' +import bitcoinDotComReducer from './bitcoin_dot_com_reducer' export const newTabReducers = (state: NewTab.State | undefined, action: any) => { if (state === undefined) { @@ -23,6 +24,7 @@ export const newTabReducers = (state: NewTab.State | undefined, action: any) => state = binanceReducer(state, action) state = rewardsReducer(state, action) state = geminiReducer(state, action) + state = bitcoinDotComReducer(state, action) if (state !== startingState) { storage.debouncedSave(state) diff --git a/components/brave_new_tab_ui/reducers/new_tab_reducer.ts b/components/brave_new_tab_ui/reducers/new_tab_reducer.ts index 57f5a487b238..29a2db69a323 100644 --- a/components/brave_new_tab_ui/reducers/new_tab_reducer.ts +++ b/components/brave_new_tab_ui/reducers/new_tab_reducer.ts @@ -39,7 +39,8 @@ export const newTabReducer: Reducer = (state: NewTab.S brandedWallpaperData: initialDataPayload.brandedWallpaperData, ...initialDataPayload.privateTabData, togetherSupported: initialDataPayload.togetherSupported, - geminiSupported: initialDataPayload.geminiSupported + geminiSupported: initialDataPayload.geminiSupported, + bitcoinDotComSupported: initialDataPayload.bitcoinDotComSupported } if (state.brandedWallpaperData && !state.brandedWallpaperData.isSponsored) { // Update feature flag if this is super referral wallpaper. diff --git a/components/brave_new_tab_ui/reducers/utils.ts b/components/brave_new_tab_ui/reducers/utils.ts new file mode 100644 index 000000000000..30b1d140967f --- /dev/null +++ b/components/brave_new_tab_ui/reducers/utils.ts @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +export default function performSideEffectLoader (storage: NewTab.State) { + type SideEffectFunction = (currentState: NewTab.State) => void + + return function (fn: SideEffectFunction): void { + window.setTimeout(() => fn(storage), 0) + } +} diff --git a/components/brave_new_tab_ui/storage/new_tab_storage.ts b/components/brave_new_tab_ui/storage/new_tab_storage.ts index 2c6eb53860b8..fe5c44ce6bcc 100644 --- a/components/brave_new_tab_ui/storage/new_tab_storage.ts +++ b/components/brave_new_tab_ui/storage/new_tab_storage.ts @@ -21,11 +21,13 @@ export const defaultState: NewTab.State = { showBinance: false, showAddCard: false, showGemini: false, + showBitcoinDotCom: false, brandedWallpaperOptIn: false, isBrandedWallpaperNotificationDismissed: true, showEmptyPage: false, togetherSupported: false, geminiSupported: false, + bitcoinDotComSupported: false, isIncognito: chrome.extension.inIncognitoContext, useAlternativePrivateSearchEngine: false, isTor: false, @@ -61,7 +63,7 @@ export const defaultState: NewTab.State = { currentStackWidget: '', removedStackWidgets: [], // Order is ascending, with last entry being in the foreground - widgetStackOrder: ['together', 'binance', 'gemini', 'rewards'], + widgetStackOrder: ['together', 'bitcoinDotCom', 'binance', 'gemini', 'rewards'], binanceState: { userTLD: 'com', initialFiat: 'USD', @@ -252,6 +254,7 @@ export const debouncedSave = debounce((data: NewTab.State) => { const dataToSave = { togetherSupported: data.togetherSupported, geminiSupported: data.geminiSupported, + bitcoinDotComSupported: data.bitcoinDotComSupported, rewardsState: data.rewardsState, binanceState: data.binanceState, geminiState: data.geminiState, diff --git a/components/brave_new_tab_ui/stories/default.tsx b/components/brave_new_tab_ui/stories/default.tsx index 04ef161e9bdf..964debe0674d 100644 --- a/components/brave_new_tab_ui/stories/default.tsx +++ b/components/brave_new_tab_ui/stories/default.tsx @@ -17,6 +17,7 @@ import * as gridSitesActions from '../actions/grid_sites_actions' import * as rewardsActions from '../actions/rewards_actions' import * as binanceActions from '../actions/binance_actions' import * as geminiActions from '../actions/gemini_actions' +import * as bitcoinDotComActions from '../actions/bitcoin_dot_com_actions' import store from '../store' import { getNewTabData, getGridSitesData } from './default/data/storybookState' @@ -49,7 +50,7 @@ storiesOf('New Tab/Containers', module) ) diff --git a/components/definitions/chromel.d.ts b/components/definitions/chromel.d.ts index d849dba74d1c..1fa9565c8806 100644 --- a/components/definitions/chromel.d.ts +++ b/components/definitions/chromel.d.ts @@ -191,6 +191,13 @@ declare namespace chrome.braveTogether { const isSupported: (callback: (supported: boolean) => void) => {} } +declare namespace chrome.moonpay { + const isBitcoinDotComSupported: (callback: (supported: boolean) => void) => {} + const onBuyBitcoinDotComCrypto: () => void + const onInteractionBitcoinDotCom: () => void + const getBitcoinDotComInteractions: (callback: (interactions: Record) => void) => {} +} + declare namespace chrome.rewardsNotifications { const addNotification: (type: number, args: string[], id: string) => {} const deleteNotification: (id: string) => {} diff --git a/components/definitions/newTab.d.ts b/components/definitions/newTab.d.ts index 195d2c3c69c3..ef8c35b2e85d 100644 --- a/components/definitions/newTab.d.ts +++ b/components/definitions/newTab.d.ts @@ -72,7 +72,7 @@ declare namespace NewTab { url: string } - export type StackWidget = 'rewards' | 'binance' | 'together' | 'gemini' | '' + export type StackWidget = 'rewards' | 'binance' | 'together' | 'gemini' | 'bitcoinDotCom' | '' export interface LegacyState { pinnedTopSites: Site[] @@ -97,6 +97,7 @@ declare namespace NewTab { export interface PersistentState { togetherSupported: boolean geminiSupported: boolean + bitcoinDotComSupported: boolean showEmptyPage: boolean rewardsState: RewardsWidgetState currentStackWidget: StackWidget @@ -126,6 +127,7 @@ declare namespace NewTab { showBinance: boolean showAddCard: boolean showGemini: boolean + showBitcoinDotCom: boolean brandedWallpaperOptIn: boolean isBrandedWallpaperNotificationDismissed: boolean stats: Stats, diff --git a/components/moonpay/browser/BUILD.gn b/components/moonpay/browser/BUILD.gn new file mode 100644 index 000000000000..4ffc229edb08 --- /dev/null +++ b/components/moonpay/browser/BUILD.gn @@ -0,0 +1,16 @@ +import("//brave/components/moonpay/browser/buildflags/buildflags.gni") + +assert(moonpay_enabled) + +source_set("browser") { + sources = [ + "moonpay_pref_utils.cc", + "moonpay_pref_utils.h", + "regions.h", + ] + + deps = [ + "//brave/components/moonpay/common", + "//components/prefs", + ] +} diff --git a/components/moonpay/browser/buildflags/BUILD.gn b/components/moonpay/browser/buildflags/BUILD.gn new file mode 100644 index 000000000000..5334a9879b81 --- /dev/null +++ b/components/moonpay/browser/buildflags/BUILD.gn @@ -0,0 +1,10 @@ +import("buildflags.gni") +import("//build/buildflag_header.gni") +import("//brave/components/moonpay/browser/buildflags/buildflags.gni") + +buildflag_header("buildflags") { + header = "buildflags.h" + flags = [ + "MOONPAY_ENABLED=$moonpay_enabled", + ] +} diff --git a/components/moonpay/browser/buildflags/buildflags.gni b/components/moonpay/browser/buildflags/buildflags.gni new file mode 100644 index 000000000000..e93ef13b6ea4 --- /dev/null +++ b/components/moonpay/browser/buildflags/buildflags.gni @@ -0,0 +1,5 @@ +import("//build/config/features.gni") + +declare_args() { + moonpay_enabled = is_mac || is_linux || is_win +} diff --git a/components/moonpay/browser/moonpay_pref_utils.cc b/components/moonpay/browser/moonpay_pref_utils.cc new file mode 100644 index 000000000000..cb8ea687b129 --- /dev/null +++ b/components/moonpay/browser/moonpay_pref_utils.cc @@ -0,0 +1,24 @@ +/* Copyright (c) 2020 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/components/moonpay/browser/moonpay_pref_utils.h" + +#include "brave/components/moonpay/common/pref_names.h" +#include "components/prefs/pref_registry_simple.h" + +namespace moonpay { + +MoonpayPrefUtils::MoonpayPrefUtils() {} + +MoonpayPrefUtils::~MoonpayPrefUtils() {} + +// static +void MoonpayPrefUtils::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterBooleanPref(kMoonpayNewTabPageShowBitcoinDotCom, true); + registry->RegisterBooleanPref(kMoonpayHasBoughtBitcoinDotComCrypto, false); + registry->RegisterBooleanPref(kMoonpayHasInteractedBitcoinDotCom, false); +} + +} // namespace moonpay diff --git a/components/moonpay/browser/moonpay_pref_utils.h b/components/moonpay/browser/moonpay_pref_utils.h new file mode 100644 index 000000000000..880fd32c74a6 --- /dev/null +++ b/components/moonpay/browser/moonpay_pref_utils.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2020 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_COMPONENTS_MOONPAY_BROWSER_MOONPAY_PREF_UTILS_H_ +#define BRAVE_COMPONENTS_MOONPAY_BROWSER_MOONPAY_PREF_UTILS_H_ + +class PrefRegistrySimple; + +namespace moonpay { + +class MoonpayPrefUtils { + public: + MoonpayPrefUtils(); + ~MoonpayPrefUtils(); + + static void RegisterPrefs(PrefRegistrySimple* registry); +}; + +} // namespace moonpay + +#endif // BRAVE_COMPONENTS_MOONPAY_BROWSER_MOONPAY_PREF_UTILS_H_ diff --git a/components/moonpay/browser/regions.h b/components/moonpay/browser/regions.h new file mode 100644 index 000000000000..43091b09170b --- /dev/null +++ b/components/moonpay/browser/regions.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2020 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_COMPONENTS_MOONPAY_BROWSER_REGIONS_H_ +#define BRAVE_COMPONENTS_MOONPAY_BROWSER_REGIONS_H_ + +#include +#include + +namespace moonpay { + +const std::vector bitcoin_dot_com_supported_regions = { + "GB", "AT", "BE", "BG", "HR", + "CY", "CZ", "DK", "EE", "FI", + "FR", "DE", "GR", "HU", "IE", + "IT", "LV", "LT", "LU", "MT", + "NL", "PL", "PT", "RO", "SK", + "SI", "ES", "SE", "CA", "AU", + "RU", "CH", "ZA", "NZ" +}; + +} // namespace moonpay + +#endif // BRAVE_COMPONENTS_MOONPAY_BROWSER_REGIONS_H_ diff --git a/components/moonpay/common/BUILD.gn b/components/moonpay/common/BUILD.gn new file mode 100644 index 000000000000..259667149355 --- /dev/null +++ b/components/moonpay/common/BUILD.gn @@ -0,0 +1,10 @@ +import("//brave/components/moonpay/browser/buildflags/buildflags.gni") + +assert(moonpay_enabled) + +source_set("common") { + sources = [ + "pref_names.cc", + "pref_names.h", + ] +} diff --git a/components/moonpay/common/pref_names.cc b/components/moonpay/common/pref_names.cc new file mode 100644 index 000000000000..652eec9be2cd --- /dev/null +++ b/components/moonpay/common/pref_names.cc @@ -0,0 +1,13 @@ +/* Copyright (c) 2020 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/components/moonpay/common/pref_names.h" + +const char kMoonpayHasBoughtBitcoinDotComCrypto[] = + "moonpay.has_bought_bitcoin_dot_com_crypto"; +const char kMoonpayHasInteractedBitcoinDotCom[] = + "moonpay.has_interacted_bitcoin_dot_com"; +const char kMoonpayNewTabPageShowBitcoinDotCom[] = + "moonpay.new_tab_page.show_bitcoin_dot_com"; diff --git a/components/moonpay/common/pref_names.h b/components/moonpay/common/pref_names.h new file mode 100644 index 000000000000..9289669d20de --- /dev/null +++ b/components/moonpay/common/pref_names.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2020 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_COMPONENTS_MOONPAY_COMMON_PREF_NAMES_H_ +#define BRAVE_COMPONENTS_MOONPAY_COMMON_PREF_NAMES_H_ + +extern const char kMoonpayHasBoughtBitcoinDotComCrypto[]; +extern const char kMoonpayHasInteractedBitcoinDotCom[]; +extern const char kMoonpayNewTabPageShowBitcoinDotCom[]; + +#endif // BRAVE_COMPONENTS_MOONPAY_COMMON_PREF_NAMES_H_ diff --git a/components/ntp_widget_utils/browser/BUILD.gn b/components/ntp_widget_utils/browser/BUILD.gn index 05c6b3225f2f..2b436065c72f 100644 --- a/components/ntp_widget_utils/browser/BUILD.gn +++ b/components/ntp_widget_utils/browser/BUILD.gn @@ -1,13 +1,6 @@ import("//brave/build/config.gni") -import("//brave/components/ntp_widget_utils/browser/buildflags/buildflags.gni") - -assert(ntp_widget_utils_enabled) source_set("browser") { - public_deps = [ - "buildflags" - ] - sources = [ "ntp_widget_utils_oauth.cc", "ntp_widget_utils_oauth.h", @@ -21,12 +14,4 @@ source_set("browser") { "//components/prefs", "//crypto", ] - - configs += [ ":ntp_widget_utils_config" ] -} - -config("ntp_widget_utils_config") { - defines = [ - "NTP_WIDGET_UTILS_ENABLED=\"ntp_widget_utils_enabled\"", - ] } diff --git a/components/ntp_widget_utils/browser/buildflags/BUILD.gn b/components/ntp_widget_utils/browser/buildflags/BUILD.gn deleted file mode 100644 index 7eb27bc379eb..000000000000 --- a/components/ntp_widget_utils/browser/buildflags/BUILD.gn +++ /dev/null @@ -1,9 +0,0 @@ -import("//build/buildflag_header.gni") -import("//brave/components/ntp_widget_utils/browser/buildflags/buildflags.gni") - -buildflag_header("buildflags") { - header = "buildflags.h" - flags = [ - "NTP_WIDGET_UTILS_ENABLED=$ntp_widget_utils_enabled", - ] -} diff --git a/components/ntp_widget_utils/browser/buildflags/buildflags.gni b/components/ntp_widget_utils/browser/buildflags/buildflags.gni deleted file mode 100644 index 60c89700504b..000000000000 --- a/components/ntp_widget_utils/browser/buildflags/buildflags.gni +++ /dev/null @@ -1,8 +0,0 @@ -import("//brave/build/config.gni") -import("//brave/components/binance/browser/buildflags/buildflags.gni") -import("//brave/components/brave_together/buildflags/buildflags.gni") -import("//brave/components/gemini/browser/buildflags/buildflags.gni") - -declare_args() { - ntp_widget_utils_enabled = binance_enabled || gemini_enabled || brave_together_enabled -} diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn index cbaecc9e3ca1..3daa4a4e82d8 100644 --- a/components/resources/BUILD.gn +++ b/components/resources/BUILD.gn @@ -2,6 +2,7 @@ import("//brave/components/brave_perf_predictor/browser/buildflags/buildflags.gn import("//brave/components/brave_rewards/browser/buildflags/buildflags.gni") import("//brave/components/brave_wallet/browser/buildflags/buildflags.gni") import("//brave/components/ipfs/browser/buildflags/buildflags.gni") +import("//brave/components/moonpay/browser/buildflags/buildflags.gni") import("//brave/components/speedreader/buildflags.gni") import("//build/config/locales.gni") import("//extensions/buildflags/buildflags.gni") @@ -41,6 +42,7 @@ grit("static_resources") { "enable_brave_perf_predictor=$enable_brave_perf_predictor", "enable_speedreader=$enable_speedreader", "ipfs_enabled=$ipfs_enabled", + "moonpay_enabled=$moonpay_enabled", ] grit_flags = [ @@ -100,6 +102,7 @@ grit("strings") { defines = [ "enable_speedreader=$enable_speedreader", "ipfs_enabled=$ipfs_enabled", + "moonpay_enabled=$moonpay_enabled", ] foreach(locale, locales_with_fake_bidi) { diff --git a/components/resources/bitcoin_dot_com_strings.grdp b/components/resources/bitcoin_dot_com_strings.grdp new file mode 100644 index 000000000000..ade86a077d60 --- /dev/null +++ b/components/resources/bitcoin_dot_com_strings.grdp @@ -0,0 +1,12 @@ + + + + Purchase crypto from Bitcoin.com. + Currency + Amount + Enter amount + New to crypto? + Get the wallet here to get started + Bitcoin.com + + diff --git a/components/resources/brave_components_strings.grd b/components/resources/brave_components_strings.grd index eb9999fe04fc..c875220c6738 100644 --- a/components/resources/brave_components_strings.grd +++ b/components/resources/brave_components_strings.grd @@ -943,6 +943,7 @@ + diff --git a/components/test/brave_new_tab_ui/api/data_test.ts b/components/test/brave_new_tab_ui/api/data_test.ts index 06eee5d91317..8f7e1e3d4160 100644 --- a/components/test/brave_new_tab_ui/api/data_test.ts +++ b/components/test/brave_new_tab_ui/api/data_test.ts @@ -9,13 +9,14 @@ import * as topSitesActions from '../../../brave_new_tab_ui/actions/grid_sites_a import * as binanceActions from '../../../brave_new_tab_ui/actions/binance_actions' import * as rewardsActions from '../../../brave_new_tab_ui/actions/rewards_actions' import * as geminiActions from '../../../brave_new_tab_ui/actions/gemini_actions' +import * as bitcoinDotComActions from '../../../brave_new_tab_ui/actions/bitcoin_dot_com_actions' import { types as topSitesTypes } from '../../../brave_new_tab_ui/constants/grid_sites_types' describe('new tab data api tests', () => { describe('getActions', () => { it('returns an object with the same keys mimicking the original new tab actions', () => { const assertion = getActions() - const actions = Object.assign({}, newTabActions, topSitesActions, binanceActions, rewardsActions, geminiActions) + const actions = Object.assign({}, newTabActions, topSitesActions, binanceActions, rewardsActions, geminiActions, bitcoinDotComActions) expect(Object.keys(assertion)).toEqual(Object.keys(actions)) }) it('can call an action from getActions', () => { diff --git a/test/BUILD.gn b/test/BUILD.gn index 745f74d1b6c2..653c44885425 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -4,7 +4,6 @@ import("//brave/browser/tor/buildflags/buildflags.gni") import("//brave/browser/translate/buildflags/buildflags.gni") import("//brave/components/brave_ads/browser/buildflags/buildflags.gni") import("//brave/components/binance/browser/buildflags/buildflags.gni") -import("//brave/components/ntp_widget_utils/browser/buildflags/buildflags.gni") import("//brave/components/gemini/browser/buildflags/buildflags.gni") import("//brave/components/brave_referrals/buildflags/buildflags.gni") import("//brave/components/brave_rewards/browser/buildflags/buildflags.gni") @@ -15,6 +14,7 @@ import("//brave/components/brave_wayback_machine/buildflags/buildflags.gni") import("//brave/components/brave_webtorrent/browser/buildflags/buildflags.gni") import("//brave/components/greaselion/browser/buildflags/buildflags.gni") import("//brave/components/ipfs/browser/buildflags/buildflags.gni") +import("//brave/components/moonpay/browser/buildflags/buildflags.gni") import("//brave/components/speedreader/buildflags.gni") import("//brave/test/testing.gni") import("//components/gcm_driver/config.gni") @@ -118,6 +118,8 @@ test("brave_unit_tests") { "//brave/components/ntp_background_images/browser/ntp_background_images_source_unittest.cc", "//brave/components/ntp_background_images/browser/view_counter_model_unittest.cc", "//brave/components/ntp_background_images/browser/view_counter_service_unittest.cc", + "//brave/components/ntp_widget_utils/browser/ntp_widget_utils_region_unittest.cc", + "//brave/components/ntp_widget_utils/browser/ntp_widget_utils_oauth_unittest.cc", "//brave/components/p3a/brave_p2a_protocols_unittest.cc", "//brave/components/rappor/log_uploader_unittest.cc", "//brave/components/translate/core/browser/translate_language_list_unittest.cc", @@ -139,6 +141,7 @@ test("brave_unit_tests") { "//brave/components/brave_private_cdn", "//brave/components/brave_referrals/common", "//brave/components/ntp_background_images/browser", + "//brave/components/ntp_widget_utils/browser", "//brave/vendor/brave_base", "//chrome:browser_dependencies", "//chrome:child_dependencies", @@ -258,16 +261,6 @@ test("brave_unit_tests") { ] } - if (ntp_widget_utils_enabled) { - sources += [ - "//brave/components/ntp_widget_utils/browser/ntp_widget_utils_region_unittest.cc", - "//brave/components/ntp_widget_utils/browser/ntp_widget_utils_oauth_unittest.cc" - ] - deps += [ - "//brave/components/ntp_widget_utils/browser" - ] - } - if (is_linux) { configs += [ "//brave/build/linux:linux_channel_names", @@ -642,6 +635,15 @@ test("brave_browser_tests") { ] } + if (moonpay_enabled) { + sources += [ + "//brave/browser/extensions/api/moonpay_api_browsertest.cc", + ] + deps += [ + "//brave/components/moonpay/common", + ] + } + if (brave_rewards_enabled) { sources += [ "//brave/components/brave_rewards/browser/test/common/rewards_browsertest_context_helper.cc", From 659ac25f12aa6be817f72b450c699825308c0879 Mon Sep 17 00:00:00 2001 From: ryanml Date: Fri, 2 Oct 2020 00:38:35 -0700 Subject: [PATCH 2/3] Merge pull request #6754 from brave/fix-11959 Ensures both bitcoin.com dropdowns aren't open at the same time --- .../default/bitcoinDotCom/index.tsx | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/components/brave_new_tab_ui/components/default/bitcoinDotCom/index.tsx b/components/brave_new_tab_ui/components/default/bitcoinDotCom/index.tsx index 51a75b7f99cc..019920b1b408 100644 --- a/components/brave_new_tab_ui/components/default/bitcoinDotCom/index.tsx +++ b/components/brave_new_tab_ui/components/default/bitcoinDotCom/index.tsx @@ -68,9 +68,18 @@ class BitcoinDotCom extends React.PureComponent { } toggleAssetsShowing = () => { + const { assetsShowing } = this.state + + if (!assetsShowing) { + this.setState({ + fiatCurrenciesShowing: false + }) + } + this.setState({ - assetsShowing: !this.state.assetsShowing + assetsShowing: !assetsShowing }) + this.props.onInteraction() } @@ -97,9 +106,18 @@ class BitcoinDotCom extends React.PureComponent { } toggleFiatCurrenciesShowing = () => { + const { fiatCurrenciesShowing } = this.state + + if (!fiatCurrenciesShowing) { + this.setState({ + assetsShowing: false + }) + } + this.setState({ - fiatCurrenciesShowing: !this.state.fiatCurrenciesShowing + fiatCurrenciesShowing: !fiatCurrenciesShowing }) + this.props.onInteraction() } From 85a2b5cd63ec5fbe258a91e484c1ec27617783c7 Mon Sep 17 00:00:00 2001 From: ryanml Date: Fri, 2 Oct 2020 04:36:05 -0700 Subject: [PATCH 3/3] Merge pull request #6756 from brave/fix-11958 Fixes breaking amount input field on Windows and Linux --- browser/extensions/BUILD.gn | 14 -------------- browser/extensions/api/moonpay_api.cc | 5 ++++- .../components/default/bitcoinDotCom/style.ts | 3 ++- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/browser/extensions/BUILD.gn b/browser/extensions/BUILD.gn index a748652ccac8..e59488288ad2 100644 --- a/browser/extensions/BUILD.gn +++ b/browser/extensions/BUILD.gn @@ -59,20 +59,6 @@ source_set("extensions") { configs += [ ":infura_config" ] } - if (binance_enabled) { - sources += [ - "api/binance_api.cc", - "api/binance_api.h", - ] - } - - if (brave_together_enabled) { - sources += [ - "api/brave_together_api.cc", - "api/brave_together_api.h", - ] - } - deps = [ ":resources", "//base", diff --git a/browser/extensions/api/moonpay_api.cc b/browser/extensions/api/moonpay_api.cc index 7bee67009c68..4e7f9e8c9003 100644 --- a/browser/extensions/api/moonpay_api.cc +++ b/browser/extensions/api/moonpay_api.cc @@ -18,7 +18,10 @@ namespace { bool IsMoonpayAPIAvailable(content::BrowserContext* context) { - return brave::IsRegularProfile(context); + Profile* profile = Profile::FromBrowserContext(context); + return !brave::IsTorProfile(profile) && + !profile->IsIncognitoProfile() && + !profile->IsGuestSession(); } } diff --git a/components/brave_new_tab_ui/components/default/bitcoinDotCom/style.ts b/components/brave_new_tab_ui/components/default/bitcoinDotCom/style.ts index 0cebd60ded89..3b13a2a316ce 100644 --- a/components/brave_new_tab_ui/components/default/bitcoinDotCom/style.ts +++ b/components/brave_new_tab_ui/components/default/bitcoinDotCom/style.ts @@ -139,6 +139,7 @@ export const AmountInput = styled('input')` border-right: none; border-top-right-radius: 0px; border-bottom-right-radius: 0px; + width: 75%; ${(p) => p.dropdownShowing ? css` @@ -160,7 +161,7 @@ export const FiatDropdown = styled('div')` display: inline-block; height: 40px; vertical-align: bottom; - width: 64px; + width: 25%; padding-top: 10px; cursor: pointer; border-top-left-radius: 0px;