Support to upload multiple custom images for NTP background (#15311)
* Support to upload multiple custom images for NTP background This PR allows users to upload multiple images for NTP background. * The maximum number of images is 24 * These images are stored in a dedicated directory. * Users can select one of these or toggle on "Random" button. * Users also can remove these images.
This commit is contained in:
@@ -103,6 +103,14 @@ void CustomBackgroundFileManager::MoveImage(
|
||||
MakeSureDirExists(std::move(on_check_dir));
|
||||
}
|
||||
|
||||
void CustomBackgroundFileManager::RemoveImage(
|
||||
const base::FilePath& file_path,
|
||||
base::OnceCallback<void(bool /*result*/)> callback) {
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE, {base::MayBlock()},
|
||||
base::BindOnce(base::DeleteFile, file_path), std::move(callback));
|
||||
}
|
||||
|
||||
base::FilePath CustomBackgroundFileManager::GetCustomBackgroundDirectory()
|
||||
const {
|
||||
return profile_->GetPath().AppendASCII(
|
||||
@@ -191,7 +199,7 @@ void CustomBackgroundFileManager::SaveImageAsPNG(
|
||||
base::FilePath modified_path = target_path;
|
||||
for (int i = 1; base::PathExists(modified_path); ++i) {
|
||||
modified_path = target_path.InsertBeforeExtensionASCII(
|
||||
base::StringPrintf("(%d)", i));
|
||||
base::StringPrintf("-%d", i));
|
||||
}
|
||||
|
||||
if (!base::WriteFile(
|
||||
|
||||
@@ -8,10 +8,21 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "brave/components/ntp_background_images/browser/url_constants.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/url_util.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
class FilePath;
|
||||
@@ -32,10 +43,124 @@ class Profile;
|
||||
// * Manages custom images
|
||||
// * Have a list of custom images in Prefs.
|
||||
// * Make it sure that we have local files mapped to entries.
|
||||
// * Deregisters custom image(TODO)
|
||||
// * Deregisters custom image
|
||||
// * remove the local file.
|
||||
class CustomBackgroundFileManager final {
|
||||
public:
|
||||
// |Converter| is a convience class to convert values to and from each layer.
|
||||
//
|
||||
// [Web UI] - GURL with percent encoded path.
|
||||
// || The path value should be same with the value in PrefService
|
||||
// (path)
|
||||
// ||
|
||||
// (value)
|
||||
// ||
|
||||
// [PrefService] - std::string. The value is a file name. Encoding is
|
||||
// || usually UTF8, but on some platforms it might not be
|
||||
// (value) specified.
|
||||
// ||
|
||||
// (file name)
|
||||
// ||
|
||||
// [File System] - base::FilePath. On Windows, it uses wide string.
|
||||
// and on other platforms, uses string but encoding might
|
||||
// not be specified.
|
||||
//
|
||||
// This class is designed to be used for a short time and then destroyed.
|
||||
// Don't pass this class around code base. Use this just right where you
|
||||
// need to convert values.
|
||||
//
|
||||
// example:
|
||||
// auto file_path = Converter(url, file_manager).To<base::FilePath>();
|
||||
// auto url = Converter(prefs_value).To<GURL>();
|
||||
//
|
||||
template <class FromT>
|
||||
class Converter final {
|
||||
public:
|
||||
explicit Converter(const FromT& value,
|
||||
CustomBackgroundFileManager* file_manager = nullptr)
|
||||
: file_manager_(file_manager) {
|
||||
if constexpr (std::is_same_v<FromT, std::string>) {
|
||||
DCHECK(!base::StartsWith(value,
|
||||
ntp_background_images::kCustomWallpaperURL))
|
||||
<< "URLs should be passed in as a GURL";
|
||||
value_ = value;
|
||||
} else if constexpr (std::is_same_v<FromT, GURL>) {
|
||||
// GURL(webui data url) -> std::string(prefs value)
|
||||
// When GURL is given, its path is percent encoded pref name. So
|
||||
// decode it and create Converter with it.
|
||||
DCHECK(value.SchemeIs("chrome") &&
|
||||
value.host() == ntp_background_images::kCustomWallpaperHost)
|
||||
<< "Not a custom wallpaper URL";
|
||||
|
||||
// remove leading slash
|
||||
const auto path = value.path().substr(1);
|
||||
DCHECK(!path.empty()) << "URL path is empty " << value;
|
||||
url::RawCanonOutputT<char16_t> decoded_value;
|
||||
url::DecodeURLEscapeSequences(path.data(), path.length(),
|
||||
url::DecodeURLMode::kUTF8OrIsomorphic,
|
||||
&decoded_value);
|
||||
value_ = base::UTF16ToUTF8(
|
||||
std::u16string(decoded_value.data(), decoded_value.length()));
|
||||
} else {
|
||||
// FilePath(local file path) -> std::string(prefs value)
|
||||
static_assert(std::is_same_v<FromT, base::FilePath>,
|
||||
"FromT must be one of std::string, GURL, base::FilePath");
|
||||
|
||||
// When base::FilePath is given, its file name is used as pref value.
|
||||
// But the file path's underlying type and encoding is platform
|
||||
// dependent. So, extract file name and convert it to UTF8 if needed.
|
||||
#if defined(OS_WIN)
|
||||
auto file_name = base::SysWideToUTF8(value.BaseName().value());
|
||||
#else
|
||||
auto file_name = std::string(value.BaseName().value().c_str());
|
||||
#endif
|
||||
DCHECK(!file_name.empty())
|
||||
<< "Couldn't extract file name from the given path " << value;
|
||||
value_ = file_name;
|
||||
}
|
||||
}
|
||||
Converter(const Converter&) = delete;
|
||||
Converter& operator=(const Converter&) = delete;
|
||||
Converter(Converter&&) noexcept = delete;
|
||||
Converter& operator=(Converter&&) noexcept = delete;
|
||||
~Converter() = default;
|
||||
|
||||
// Converter functions. Not allowing to convert to what it was created from.
|
||||
template <class ToT>
|
||||
[[nodiscard]] std::enable_if_t<!std::is_same_v<FromT, ToT>, ToT> To()
|
||||
const&& {
|
||||
if constexpr (std::is_same_v<ToT, std::string>) {
|
||||
return value_;
|
||||
} else if constexpr (std::is_same_v<ToT, GURL>) {
|
||||
// std::string(pref_value) -> GURL(webui data url)
|
||||
// Do percent encoding and compose it with base url so that it can
|
||||
// be used as webui data url.
|
||||
url::RawCanonOutputT<char> encoded;
|
||||
url::EncodeURIComponent(value_.c_str(), value_.length(), &encoded);
|
||||
return GURL(ntp_background_images::kCustomWallpaperURL +
|
||||
std::string(encoded.data(), encoded.length()));
|
||||
} else {
|
||||
static_assert(std::is_same_v<ToT, base::FilePath>,
|
||||
"ToT must be one of std::string, GURL, base::FilePath");
|
||||
// std::string(pref_value) -> base::FilePath(local file path)
|
||||
DCHECK(file_manager_) << "Converting to local file path requires "
|
||||
"CustomBackgroundFileManager";
|
||||
base::FilePath file_path =
|
||||
file_manager_->GetCustomBackgroundDirectory();
|
||||
#if defined(OS_WIN)
|
||||
file_path = file_path.Append(base::SysUTF8ToWide(value_));
|
||||
#else
|
||||
file_path = file_path.Append(value_);
|
||||
#endif
|
||||
return file_path;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
raw_ptr<CustomBackgroundFileManager> file_manager_ = nullptr;
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
using SaveFileCallback = base::OnceCallback<void(const base::FilePath&)>;
|
||||
|
||||
explicit CustomBackgroundFileManager(Profile* profile);
|
||||
@@ -48,6 +173,8 @@ class CustomBackgroundFileManager final {
|
||||
SaveFileCallback callback);
|
||||
void MoveImage(const base::FilePath& source_file_path,
|
||||
base::OnceCallback<void(bool /*result*/)> callback);
|
||||
void RemoveImage(const base::FilePath& file_path,
|
||||
base::OnceCallback<void(bool /*result*/)> callback);
|
||||
|
||||
base::FilePath GetCustomBackgroundDirectory() const;
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ IN_PROC_BROWSER_TEST_F(CustomBackgroundFileManagerBrowserTest,
|
||||
kTestImageName);
|
||||
if (i > 0) {
|
||||
expected_path = expected_path.InsertBeforeExtensionASCII(
|
||||
base::StringPrintf("(%d)", i));
|
||||
base::StringPrintf("-%d", i));
|
||||
}
|
||||
|
||||
auto check_res =
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/notreached.h"
|
||||
#include "base/ranges/algorithm.h"
|
||||
#include "brave/components/constants/pref_names.h"
|
||||
#include "components/pref_registry/pref_registry_syncable.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
@@ -135,6 +136,13 @@ void NTPBackgroundPrefs::AddCustomImageToList(const std::string& file_name) {
|
||||
update->GetList().Append(file_name);
|
||||
}
|
||||
|
||||
void NTPBackgroundPrefs::RemoveCustomImageFromList(
|
||||
const std::string& file_name) {
|
||||
ListPrefUpdate update(service_, NTPBackgroundPrefs::kCustomImageListPrefName);
|
||||
auto& list = update->GetList();
|
||||
list.erase(base::ranges::remove(update->GetList(), file_name), list.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> NTPBackgroundPrefs::GetCustomImageList() const {
|
||||
const auto* list = service_->GetList(kCustomImageListPrefName);
|
||||
std::vector<std::string> result;
|
||||
|
||||
@@ -85,6 +85,7 @@ class NTPBackgroundPrefs final {
|
||||
absl::variant<GURL, std::string> GetSelectedValue() const;
|
||||
|
||||
void AddCustomImageToList(const std::string& file_name);
|
||||
void RemoveCustomImageFromList(const std::string& file_name);
|
||||
std::vector<std::string> GetCustomImageList() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "brave/components/constants/pref_names.h"
|
||||
#include "brave/components/ntp_background_images/browser/ntp_background_images_data.h"
|
||||
#include "brave/components/ntp_background_images/browser/ntp_background_images_service.h"
|
||||
#include "brave/components/ntp_background_images/browser/url_constants.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "chrome/common/url_constants.h"
|
||||
@@ -79,28 +80,20 @@ bool NTPCustomBackgroundImagesServiceDelegate::IsCustomImageBackgroundEnabled()
|
||||
return NTPBackgroundPrefs(profile_->GetPrefs()).IsCustomImageType();
|
||||
}
|
||||
|
||||
base::FilePath NTPCustomBackgroundImagesServiceDelegate::
|
||||
GetCustomBackgroundImageLocalFilePath() const {
|
||||
if (!IsCustomImageBackgroundEnabled())
|
||||
return base::FilePath();
|
||||
base::FilePath
|
||||
NTPCustomBackgroundImagesServiceDelegate::GetCustomBackgroundImageLocalFilePath(
|
||||
const GURL& url) const {
|
||||
return CustomBackgroundFileManager::Converter(url, file_manager_.get())
|
||||
.To<base::FilePath>();
|
||||
}
|
||||
|
||||
auto value = NTPBackgroundPrefs(profile_->GetPrefs()).GetSelectedValue();
|
||||
if (!absl::holds_alternative<std::string>(value)) {
|
||||
// This can happen during migration.
|
||||
return base::FilePath();
|
||||
}
|
||||
GURL NTPCustomBackgroundImagesServiceDelegate::GetCustomBackgroundImageURL()
|
||||
const {
|
||||
DCHECK(IsCustomImageBackgroundEnabled());
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// On Windows path is wchar type and we should convert it to utf8.
|
||||
// So we suppose |value| is utf8.
|
||||
const auto file_name =
|
||||
base::FilePath::FromUTF8Unsafe(absl::get<std::string>(value)).value();
|
||||
#else
|
||||
// On other platform, path's encoding is not specified, and we store value
|
||||
// as it was given.
|
||||
const auto file_name = absl::get<std::string>(value);
|
||||
#endif
|
||||
return file_manager_->GetCustomBackgroundDirectory().Append(file_name);
|
||||
auto prefs = NTPBackgroundPrefs(profile_->GetPrefs());
|
||||
auto name = absl::get<std::string>(prefs.GetSelectedValue());
|
||||
return CustomBackgroundFileManager::Converter(name).To<GURL>();
|
||||
}
|
||||
|
||||
bool NTPCustomBackgroundImagesServiceDelegate::IsColorBackgroundEnabled()
|
||||
|
||||
@@ -40,7 +40,9 @@ class NTPCustomBackgroundImagesServiceDelegate
|
||||
|
||||
// NTPCustomBackgroundImagesService::Delegate overrides:
|
||||
bool IsCustomImageBackgroundEnabled() const override;
|
||||
base::FilePath GetCustomBackgroundImageLocalFilePath() const override;
|
||||
base::FilePath GetCustomBackgroundImageLocalFilePath(
|
||||
const GURL& url) const override;
|
||||
GURL GetCustomBackgroundImageURL() const override;
|
||||
bool IsColorBackgroundEnabled() const override;
|
||||
std::string GetColor() const override;
|
||||
bool ShouldUseRandomValue() const override;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "brave/browser/ui/webui/new_tab_page/brave_new_tab_page_handler.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/containers/span.h"
|
||||
@@ -43,10 +42,6 @@
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsNTPPromotionEnabled(Profile* profile) {
|
||||
@@ -85,6 +80,7 @@ BraveNewTabPageHandler::BraveNewTabPageHandler(
|
||||
page_(std::move(pending_page)),
|
||||
profile_(profile),
|
||||
web_contents_(web_contents),
|
||||
file_manager_(std::make_unique<CustomBackgroundFileManager>(profile_)),
|
||||
weak_factory_(this) {
|
||||
InitForSearchPromotion();
|
||||
}
|
||||
@@ -130,12 +126,56 @@ void BraveNewTabPageHandler::ChooseLocalCustomBackground() {
|
||||
file_types.extension_description_overrides.push_back(
|
||||
brave_l10n::GetLocalizedResourceUTF16String(IDS_UPLOAD_IMAGE_FORMAT));
|
||||
select_file_dialog_->SelectFile(
|
||||
ui::SelectFileDialog::SELECT_OPEN_FILE, std::u16string(),
|
||||
ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE, std::u16string(),
|
||||
profile_->last_selected_directory(), &file_types, 0,
|
||||
base::FilePath::StringType(), web_contents_->GetTopLevelNativeWindow(),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::UseCustomImageBackground(
|
||||
const std::string& selected_background) {
|
||||
auto decoded_background = selected_background;
|
||||
if (!decoded_background.empty()) {
|
||||
decoded_background =
|
||||
CustomBackgroundFileManager::Converter(GURL(decoded_background))
|
||||
.To<std::string>();
|
||||
}
|
||||
|
||||
auto pref = NTPBackgroundPrefs(profile_->GetPrefs());
|
||||
pref.SetType(NTPBackgroundPrefs::Type::kCustomImage);
|
||||
pref.SetSelectedValue(decoded_background);
|
||||
pref.SetShouldUseRandomValue(decoded_background.empty());
|
||||
|
||||
OnBackgroundUpdated();
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::GetCustomImageBackgrounds(
|
||||
GetCustomImageBackgroundsCallback callback) {
|
||||
std::vector<brave_new_tab_page::mojom::CustomBackgroundPtr> backgrounds;
|
||||
for (const auto& name :
|
||||
NTPBackgroundPrefs(profile_->GetPrefs()).GetCustomImageList()) {
|
||||
auto value = brave_new_tab_page::mojom::CustomBackground::New();
|
||||
value->url = CustomBackgroundFileManager::Converter(name).To<GURL>();
|
||||
backgrounds.push_back(std::move(value));
|
||||
}
|
||||
|
||||
std::move(callback).Run(std::move(backgrounds));
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::RemoveCustomImageBackground(
|
||||
const std::string& background) {
|
||||
if (background.empty())
|
||||
return;
|
||||
|
||||
auto file_path = CustomBackgroundFileManager::Converter(GURL(background),
|
||||
file_manager_.get())
|
||||
.To<base::FilePath>();
|
||||
file_manager_->RemoveImage(
|
||||
file_path,
|
||||
base::BindOnce(&BraveNewTabPageHandler::OnRemoveCustomImageBackground,
|
||||
weak_factory_.GetWeakPtr(), file_path));
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::UseBraveBackground(
|
||||
const std::string& selected_background) {
|
||||
// Call ntp custom background images service.
|
||||
@@ -223,11 +263,18 @@ void BraveNewTabPageHandler::OnSavedCustomImage(const base::FilePath& path) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
auto file_name = base::SysWideToUTF8(path.BaseName().value());
|
||||
#else
|
||||
auto file_name = std::string(path.BaseName().value().c_str());
|
||||
#endif
|
||||
if (brave_new_tab_page::mojom::kMaxCustomImageBackgrounds -
|
||||
NTPBackgroundPrefs(profile_->GetPrefs())
|
||||
.GetCustomImageList()
|
||||
.size() <=
|
||||
0) {
|
||||
// We can't save more images.
|
||||
file_manager_->RemoveImage(path, base::DoNothing());
|
||||
return;
|
||||
}
|
||||
|
||||
auto file_name =
|
||||
CustomBackgroundFileManager::Converter(path).To<std::string>();
|
||||
DCHECK(!file_name.empty());
|
||||
|
||||
auto background_pref = NTPBackgroundPrefs(profile_->GetPrefs());
|
||||
@@ -235,16 +282,51 @@ void BraveNewTabPageHandler::OnSavedCustomImage(const base::FilePath& path) {
|
||||
background_pref.SetSelectedValue(file_name);
|
||||
background_pref.AddCustomImageToList(file_name);
|
||||
OnBackgroundUpdated();
|
||||
OnCustomImageBackgroundsUpdated();
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::OnRemoveCustomImageBackground(
|
||||
const base::FilePath& path,
|
||||
bool success) {
|
||||
if (!success) {
|
||||
LOG(ERROR) << "Failed to remove custom image " << path;
|
||||
return;
|
||||
}
|
||||
|
||||
auto file_name =
|
||||
CustomBackgroundFileManager::Converter(path).To<std::string>();
|
||||
DCHECK(!file_name.empty());
|
||||
|
||||
auto background_pref = NTPBackgroundPrefs(profile_->GetPrefs());
|
||||
background_pref.RemoveCustomImageFromList(file_name);
|
||||
if (background_pref.GetType() == NTPBackgroundPrefs::Type::kCustomImage &&
|
||||
absl::get<std::string>(background_pref.GetSelectedValue()) == file_name) {
|
||||
if (auto custom_images = background_pref.GetCustomImageList();
|
||||
!custom_images.empty()) {
|
||||
background_pref.SetSelectedValue(custom_images.front());
|
||||
} else {
|
||||
// Reset to default
|
||||
background_pref.SetType(NTPBackgroundPrefs::Type::kBrave);
|
||||
background_pref.SetSelectedValue({});
|
||||
background_pref.SetShouldUseRandomValue(true);
|
||||
}
|
||||
OnBackgroundUpdated();
|
||||
}
|
||||
|
||||
OnCustomImageBackgroundsUpdated();
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::OnBackgroundUpdated() {
|
||||
if (IsCustomBackgroundImageEnabled()) {
|
||||
auto value = brave_new_tab_page::mojom::CustomBackground::New();
|
||||
// Add a timestamp to the url to prevent the browser from using a cached
|
||||
// version when "Upload an image" is used multiple times.
|
||||
std::string time_string = std::to_string(base::Time::Now().ToTimeT());
|
||||
std::string local_string(ntp_background_images::kCustomWallpaperURL);
|
||||
value->url = GURL(local_string + "?ts=" + time_string);
|
||||
|
||||
NTPBackgroundPrefs prefs(profile_->GetPrefs());
|
||||
auto selected_value = prefs.GetSelectedValue();
|
||||
DCHECK(absl::holds_alternative<std::string>(selected_value));
|
||||
const std::string file_name = absl::get<std::string>(selected_value);
|
||||
if (!file_name.empty())
|
||||
value->url = CustomBackgroundFileManager::Converter(file_name).To<GURL>();
|
||||
value->use_random_item = prefs.ShouldUseRandomValue();
|
||||
page_->OnBackgroundUpdated(
|
||||
brave_new_tab_page::mojom::Background::NewCustom(std::move(value)));
|
||||
return;
|
||||
@@ -305,14 +387,23 @@ void BraveNewTabPageHandler::OnBackgroundUpdated() {
|
||||
brave_new_tab_page::mojom::Background::NewBrave(std::move(value)));
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::OnCustomImageBackgroundsUpdated() {
|
||||
std::vector<brave_new_tab_page::mojom::CustomBackgroundPtr> backgrounds;
|
||||
for (const auto& name :
|
||||
NTPBackgroundPrefs(profile_->GetPrefs()).GetCustomImageList()) {
|
||||
auto value = brave_new_tab_page::mojom::CustomBackground::New();
|
||||
value->url = CustomBackgroundFileManager::Converter(name).To<GURL>();
|
||||
backgrounds.push_back(std::move(value));
|
||||
}
|
||||
|
||||
page_->OnCustomImageBackgroundsUpdated(std::move(backgrounds));
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::FileSelected(const base::FilePath& path,
|
||||
int index,
|
||||
void* params) {
|
||||
profile_->set_last_selected_directory(path.DirName());
|
||||
|
||||
if (!file_manager_)
|
||||
file_manager_ = std::make_unique<CustomBackgroundFileManager>(profile_);
|
||||
|
||||
file_manager_->SaveImage(
|
||||
path, base::BindOnce(&BraveNewTabPageHandler::OnSavedCustomImage,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
@@ -320,6 +411,22 @@ void BraveNewTabPageHandler::FileSelected(const base::FilePath& path,
|
||||
select_file_dialog_ = nullptr;
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::MultiFilesSelected(
|
||||
const std::vector<base::FilePath>& files,
|
||||
void* params) {
|
||||
NTPBackgroundPrefs prefs(profile_->GetPrefs());
|
||||
auto available_image_count =
|
||||
brave_new_tab_page::mojom::kMaxCustomImageBackgrounds -
|
||||
prefs.GetCustomImageList().size();
|
||||
for (const auto& path : files) {
|
||||
if (available_image_count == 0)
|
||||
break;
|
||||
|
||||
FileSelected(path, 0, params);
|
||||
available_image_count--;
|
||||
}
|
||||
}
|
||||
|
||||
void BraveNewTabPageHandler::FileSelectionCanceled(void* params) {
|
||||
select_file_dialog_ = nullptr;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
@@ -51,6 +52,12 @@ class BraveNewTabPageHandler : public brave_new_tab_page::mojom::PageHandler,
|
||||
private:
|
||||
// brave_new_tab_page::mojom::PageHandler overrides:
|
||||
void ChooseLocalCustomBackground() override;
|
||||
void UseCustomImageBackground(
|
||||
const std::string& selected_background) override;
|
||||
void GetCustomImageBackgrounds(
|
||||
GetCustomImageBackgroundsCallback callback) override;
|
||||
void RemoveCustomImageBackground(const std::string& background) override;
|
||||
|
||||
void UseBraveBackground(const std::string& selected_background) override;
|
||||
void GetBraveBackgrounds(GetBraveBackgroundsCallback callback) override;
|
||||
void TryBraveSearchPromotion(const std::string& input,
|
||||
@@ -63,11 +70,14 @@ class BraveNewTabPageHandler : public brave_new_tab_page::mojom::PageHandler,
|
||||
|
||||
// Observe NTPCustomBackgroundImagesService.
|
||||
void OnBackgroundUpdated();
|
||||
void OnCustomImageBackgroundsUpdated();
|
||||
|
||||
// SelectFileDialog::Listener overrides:
|
||||
void FileSelected(const base::FilePath& path,
|
||||
int index,
|
||||
void* params) override;
|
||||
void MultiFilesSelected(const std::vector<base::FilePath>& files,
|
||||
void* params) override;
|
||||
void FileSelectionCanceled(void* params) override;
|
||||
|
||||
// TemplateURLServiceObserver overrides:
|
||||
@@ -77,6 +87,7 @@ class BraveNewTabPageHandler : public brave_new_tab_page::mojom::PageHandler,
|
||||
bool IsCustomBackgroundImageEnabled() const;
|
||||
bool IsColorBackgroundEnabled() const;
|
||||
void OnSavedCustomImage(const base::FilePath& path);
|
||||
void OnRemoveCustomImageBackground(const base::FilePath& path, bool success);
|
||||
|
||||
void OnSearchPromotionDismissed();
|
||||
void NotifySearchPromotionDisabledIfNeeded() const;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Stats } from '../api/stats'
|
||||
import { PrivateTabData } from '../api/privateTabData'
|
||||
import { NewTabAdsData } from '../api/newTabAdsData'
|
||||
import { InitialData } from '../api/initialData'
|
||||
import { Background } from '../api/background'
|
||||
import { Background, CustomBackground } from '../api/background'
|
||||
|
||||
export const statsUpdated = (stats: Stats) =>
|
||||
action(types.NEW_TAB_STATS_UPDATED, {
|
||||
@@ -54,5 +54,8 @@ export const customizeClicked = () => action(types.CUSTOMIZE_CLICKED, {})
|
||||
export const customBackgroundUpdated = (background: Background) =>
|
||||
action(types.BACKGROUND_UPDATED, { background })
|
||||
|
||||
export const customImageBackgroundsUpdated = (backgrounds: CustomBackground[]) =>
|
||||
action(types.CUSTOM_IMAGE_BACKGROUNDS_UPDATED, backgrounds)
|
||||
|
||||
export const searchPromotionDisabled = () =>
|
||||
action(types.SEARCH_PROMOTION_DISABLED, {})
|
||||
|
||||
@@ -34,10 +34,12 @@ interface API {
|
||||
pageCallbackRouter: BraveNewTabPage.PageCallbackRouter
|
||||
pageHandler: BraveNewTabPage.PageHandlerRemote
|
||||
addBackgroundUpdatedListener: (listener: BackgroundUpdated) => void
|
||||
addCustomImageBackgroundsUpdatedListener: (listener: CustomImageBackgroundsUpdated) => void
|
||||
addSearchPromotionDisabledListener: (listener: () => void) => void
|
||||
}
|
||||
|
||||
type BackgroundUpdated = (background: BraveNewTabPage.Background) => void
|
||||
type CustomImageBackgroundsUpdated = (backgrounds: BraveNewTabPage.CustomBackground[]) => void
|
||||
|
||||
let ntpBrowserAPIInstance: API
|
||||
|
||||
@@ -57,6 +59,10 @@ class NTPBrowserAPI implements API {
|
||||
this.pageCallbackRouter.onBackgroundUpdated.addListener(listener)
|
||||
}
|
||||
|
||||
addCustomImageBackgroundsUpdatedListener (listener: CustomImageBackgroundsUpdated) {
|
||||
this.pageCallbackRouter.onCustomImageBackgroundsUpdated.addListener(listener)
|
||||
}
|
||||
|
||||
addSearchPromotionDisabledListener (listener: () => void) {
|
||||
this.pageCallbackRouter.onSearchPromotionDisabled.addListener(listener)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export type InitialData = {
|
||||
privateTabData: privateTabDataAPI.PrivateTabData
|
||||
wallpaperData?: NewTab.Wallpaper
|
||||
braveBackgrounds: NewTab.BraveBackground[]
|
||||
customImageBackgrounds: NewTab.ImageBackground[]
|
||||
braveRewardsSupported: boolean
|
||||
braveTalkSupported: boolean
|
||||
geminiSupported: boolean
|
||||
@@ -58,7 +59,8 @@ export async function getInitialData (): Promise<InitialData> {
|
||||
ftxSupported,
|
||||
binanceSupported,
|
||||
searchPromotionEnabled,
|
||||
braveBackgrounds
|
||||
braveBackgrounds,
|
||||
customImageBackgrounds
|
||||
] = await Promise.all([
|
||||
preferencesAPI.getPreferences(),
|
||||
statsAPI.getStats(),
|
||||
@@ -101,7 +103,10 @@ export async function getInitialData (): Promise<InitialData> {
|
||||
}),
|
||||
getNTPBrowserAPI().pageHandler.isSearchPromotionEnabled().then(({ enabled }) => enabled),
|
||||
getNTPBrowserAPI().pageHandler.getBraveBackgrounds().then(({ backgrounds }) => {
|
||||
return backgrounds.map(background => { return { type: 'brave', wallpaperImageUrl: background.imageUrl.url, author: background.author, link: background.link.url } })
|
||||
return backgrounds.map(background => ({ type: 'brave', wallpaperImageUrl: background.imageUrl.url, author: background.author, link: background.link.url }))
|
||||
}),
|
||||
getNTPBrowserAPI().pageHandler.getCustomImageBackgrounds().then(({ backgrounds }) => {
|
||||
return backgrounds.map(background => ({ type: 'image', wallpaperImageUrl: background.url.url }))
|
||||
})
|
||||
])
|
||||
console.timeStamp('Got all initial data.')
|
||||
@@ -111,6 +116,7 @@ export async function getInitialData (): Promise<InitialData> {
|
||||
privateTabData,
|
||||
wallpaperData,
|
||||
braveBackgrounds,
|
||||
customImageBackgrounds,
|
||||
braveRewardsSupported,
|
||||
braveTalkSupported,
|
||||
geminiSupported,
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as statsAPI from './api/stats'
|
||||
import * as topSitesAPI from './api/topSites'
|
||||
import * as privateTabDataAPI from './api/privateTabData'
|
||||
import * as newTabAdsDataAPI from './api/newTabAdsData'
|
||||
import getNTPBrowserAPI, { Background } from './api/background'
|
||||
import getNTPBrowserAPI, { Background, CustomBackground } from './api/background'
|
||||
import { getInitialData, getRewardsInitialData, getRewardsPreInitialData } from './api/initialData'
|
||||
import * as backgroundData from './data/backgrounds'
|
||||
|
||||
@@ -44,6 +44,10 @@ async function onBackgroundUpdated (background: Background) {
|
||||
getActions().customBackgroundUpdated(background)
|
||||
}
|
||||
|
||||
async function onCustomImageBackgroundsUpdated (backgrounds: CustomBackground[]) {
|
||||
getActions().customImageBackgroundsUpdated(backgrounds)
|
||||
}
|
||||
|
||||
// Not marked as async so we don't return a promise
|
||||
// and confuse callers
|
||||
export function wireApiEventsToStore () {
|
||||
@@ -65,6 +69,7 @@ export function wireApiEventsToStore () {
|
||||
backgroundData.updateImages(initialData.braveBackgrounds)
|
||||
|
||||
getNTPBrowserAPI().addBackgroundUpdatedListener(onBackgroundUpdated)
|
||||
getNTPBrowserAPI().addCustomImageBackgroundsUpdatedListener(onCustomImageBackgroundsUpdated)
|
||||
getNTPBrowserAPI().addSearchPromotionDisabledListener(() => getActions().searchPromotionDisabled())
|
||||
})
|
||||
.catch(e => {
|
||||
|
||||
@@ -10,6 +10,8 @@ import "url/mojom/url.mojom";
|
||||
const string kRandomSolidColorValue = "solid";
|
||||
const string kRandomGradientColorValue = "gradient";
|
||||
|
||||
const int8 kMaxCustomImageBackgrounds = 24;
|
||||
|
||||
struct BraveBackground {
|
||||
string name;
|
||||
string author;
|
||||
@@ -48,6 +50,10 @@ interface PageHandlerFactory {
|
||||
interface PageHandler {
|
||||
// Choose custom background from local file system.
|
||||
ChooseLocalCustomBackground();
|
||||
// When |selectedBackground| is empty, should use random background.
|
||||
UseCustomImageBackground(string selectedBackground);
|
||||
GetCustomImageBackgrounds() => (array<CustomBackground> backgrounds);
|
||||
RemoveCustomImageBackground(string selectedBackground);
|
||||
|
||||
// When |seleteceBackground| is empty, should use random background.
|
||||
UseBraveBackground(string selected_background);
|
||||
@@ -60,7 +66,6 @@ interface PageHandler {
|
||||
// |use_random_color| is true
|
||||
UseColorBackground(string color, bool use_random_color);
|
||||
|
||||
|
||||
TryBraveSearchPromotion(string input, bool open_new_tab);
|
||||
DismissBraveSearchPromotion();
|
||||
IsSearchPromotionEnabled() => (bool enabled);
|
||||
@@ -70,5 +75,7 @@ interface PageHandler {
|
||||
interface Page {
|
||||
OnBackgroundUpdated(Background? background);
|
||||
|
||||
OnCustomImageBackgroundsUpdated(array<CustomBackground> backgrounds);
|
||||
|
||||
OnSearchPromotionDisabled();
|
||||
};
|
||||
|
||||
@@ -321,7 +321,7 @@ function getPageBackground (p: HasImageProps) {
|
||||
rgba(0, 0, 0, 0) 35%,
|
||||
rgba(0, 0, 0, 0) 80%,
|
||||
rgba(0, 0, 0, 0.6) 100%
|
||||
), url(${p.imageSrc});
|
||||
), url("${p.imageSrc}");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
|
||||
@@ -120,7 +120,7 @@ export const SettingsSidebarSVGContent = styled('div')<SettingsSidebarSVGContent
|
||||
/* Active version (hidden until item is active).
|
||||
This is a separate element so that we can:
|
||||
1. fade it in (no transition for background gradient) */
|
||||
&:after {
|
||||
&::after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
@@ -169,7 +169,7 @@ export const SettingsSidebarButtonText = styled('span')<{ isActive: boolean }>`
|
||||
1. fade it in (no transition for background gradient)
|
||||
2. still show ellipsis for overflowing text (which doesn't show for
|
||||
background-clip: text) */
|
||||
&:after {
|
||||
&::after {
|
||||
content: attr(data-text);
|
||||
position: absolute;
|
||||
opacity: var(--active-opacity, 0);
|
||||
@@ -600,6 +600,7 @@ export const StyledCustomBackgroundOption = styled('button')<{}>`
|
||||
|
||||
interface SelectionProps {
|
||||
selected: boolean
|
||||
removable?: boolean
|
||||
}
|
||||
|
||||
interface ColoredBackgroundProps {
|
||||
@@ -620,15 +621,18 @@ export const StyledSelectionBorder = styled('div')<SelectionProps>`
|
||||
: 'linear-gradient(122.53deg, #4C54D2 0%, #BF14A2 56.25%, #F73A1C 100%)'};
|
||||
padding: 2px;
|
||||
border-radius: 10px;
|
||||
&:after {
|
||||
&::after {
|
||||
content: url(${CheckedCircle});
|
||||
position:absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
`}
|
||||
`
|
||||
|
||||
${p => p.removable && css`
|
||||
&:hover::after { display: none; }
|
||||
`}
|
||||
`
|
||||
export const StyledUploadIconContainer = styled('div')<SelectionProps>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -657,7 +661,7 @@ export const StyledCustomBackgroundOptionImage = styled('div')<SelectionProps &
|
||||
${p => p.selected
|
||||
? css`border-radius: 8px;`
|
||||
: css`border-radius: 10px;`}
|
||||
background-image: url(${p => p.image});
|
||||
background-image: url("${p => p.image}");
|
||||
`
|
||||
|
||||
export const StyledCustomBackgroundOptionColor = styled('div')<SelectionProps & ColoredBackgroundProps>`
|
||||
|
||||
@@ -23,7 +23,8 @@ export const enum types {
|
||||
SET_MOST_VISITED_SITES = '@@newtab/SET_MOST_VISITED_SITES',
|
||||
TOP_SITES_STATE_UPDATED = '@@newtab/TOP_SITES_STATE_UPDATED',
|
||||
CUSTOMIZE_CLICKED = '@@newtab/CUSTOMIZE_CLICKED',
|
||||
BACKGROUND_UPDATED = '@@newtab/CUSTOM_BACKGROUND_UPDATED',
|
||||
BACKGROUND_UPDATED = '@@newtab/_BACKGROUND_UPDATED',
|
||||
CUSTOM_IMAGE_BACKGROUNDS_UPDATED = '@@newtab/CUSTOM_IMAGE_BACKGROUNDS_UPDATED',
|
||||
SEARCH_PROMOTION_DISABLED = '@@newtab/SEARCH_PROMOTION_DISABLED',
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,9 @@ function DefaultPage (props: Props) {
|
||||
saveBrandedWallpaperOptIn={PreferencesAPI.saveBrandedWallpaperOptIn}
|
||||
saveSetAllStackWidgets={PreferencesAPI.saveSetAllStackWidgets}
|
||||
getBraveNewsDisplayAd={getBraveNewsDisplayAd}
|
||||
useCustomBackgroundImage={() => getNTPBrowserAPI().pageHandler.chooseLocalCustomBackground() }
|
||||
chooseNewCustomBackgroundImage={() => getNTPBrowserAPI().pageHandler.chooseLocalCustomBackground() }
|
||||
setCustomImageBackground={background => getNTPBrowserAPI().pageHandler.useCustomImageBackground(background) }
|
||||
removeCustomImageBackground={background => getNTPBrowserAPI().pageHandler.removeCustomImageBackground(background) }
|
||||
setBraveBackground={selectedBackground => getNTPBrowserAPI().pageHandler.useBraveBackground(selectedBackground)}
|
||||
setColorBackground={(color, useRandomColor) => getNTPBrowserAPI().pageHandler.useColorBackground(color, useRandomColor) }
|
||||
/>
|
||||
|
||||
@@ -75,7 +75,9 @@ interface Props {
|
||||
saveShowFTX: (value: boolean) => void
|
||||
saveBrandedWallpaperOptIn: (value: boolean) => void
|
||||
saveSetAllStackWidgets: (value: boolean) => void
|
||||
useCustomBackgroundImage: () => void
|
||||
chooseNewCustomBackgroundImage: () => void
|
||||
setCustomImageBackground: (selectedBackground: string) => void
|
||||
removeCustomImageBackground: (background: string) => void
|
||||
setBraveBackground: (selectedBackground: string) => void
|
||||
setColorBackground: (color: string, useRandomColor: boolean) => void
|
||||
}
|
||||
@@ -1307,7 +1309,9 @@ class NewTabPage extends React.Component<Props, State> {
|
||||
toggleShowTopSites={this.toggleShowTopSites}
|
||||
setMostVisitedSettings={this.setMostVisitedSettings}
|
||||
toggleBrandedWallpaperOptIn={this.toggleShowBrandedWallpaper}
|
||||
useCustomBackgroundImage={this.props.useCustomBackgroundImage}
|
||||
chooseNewCustomImageBackground={this.props.chooseNewCustomBackgroundImage}
|
||||
setCustomImageBackground={this.props.setCustomImageBackground}
|
||||
removeCustomImageBackground={this.props.removeCustomImageBackground}
|
||||
setBraveBackground={this.props.setBraveBackground}
|
||||
setColorBackground={this.props.setColorBackground}
|
||||
showBackgroundImage={newTabData.showBackgroundImage}
|
||||
|
||||
@@ -66,7 +66,9 @@ export interface Props {
|
||||
toggleShowFTX: () => void
|
||||
toggleBrandedWallpaperOptIn: () => void
|
||||
toggleCards: (show: boolean) => void
|
||||
useCustomBackgroundImage: () => void
|
||||
chooseNewCustomImageBackground: () => void
|
||||
setCustomImageBackground: (selectedBackground: string) => void
|
||||
removeCustomImageBackground: (background: string) => void
|
||||
setBraveBackground: (selectedBackground: string) => void
|
||||
setColorBackground: (color: string, useRandomColor: boolean) => void
|
||||
onEnableRewards: () => void
|
||||
@@ -184,10 +186,6 @@ export default class Settings extends React.PureComponent<Props, State> {
|
||||
this.props.toggleShowBackgroundImage()
|
||||
}
|
||||
|
||||
useCustomBackgroundImage = () => {
|
||||
this.props.useCustomBackgroundImage()
|
||||
}
|
||||
|
||||
setBraveBackground = (selectedBackground: string) => {
|
||||
this.props.setBraveBackground(selectedBackground)
|
||||
}
|
||||
@@ -354,7 +352,9 @@ export default class Settings extends React.PureComponent<Props, State> {
|
||||
newTabData={this.props.newTabData}
|
||||
toggleBrandedWallpaperOptIn={toggleBrandedWallpaperOptIn}
|
||||
toggleShowBackgroundImage={this.toggleShowBackgroundImage}
|
||||
useCustomBackgroundImage={this.useCustomBackgroundImage}
|
||||
chooseNewCustomImageBackground={this.props.chooseNewCustomImageBackground}
|
||||
setCustomImageBackground={this.props.setCustomImageBackground}
|
||||
removeCustomImageBackground={this.props.removeCustomImageBackground}
|
||||
setBraveBackground={this.setBraveBackground}
|
||||
setColorBackground={this.setColorBackground}
|
||||
brandedWallpaperOptIn={brandedWallpaperOptIn}
|
||||
|
||||
@@ -21,12 +21,14 @@ interface Props {
|
||||
backgrounds: NewTab.BackgroundWallpaper[]
|
||||
currentValue?: string
|
||||
usingRandomColor: boolean
|
||||
renderExtraButton?: () => JSX.Element
|
||||
onSelectValue: (background: string, useRandomColor: boolean) => void
|
||||
onBack: () => void
|
||||
onToggleRandomColor: (on: boolean) => void
|
||||
onRemoveValue?: (background: string) => void
|
||||
}
|
||||
|
||||
function BackgroundChooser ({ title, backgrounds, onBack, onSelectValue, currentValue, usingRandomColor, onToggleRandomColor }: Props) {
|
||||
function BackgroundChooser ({ title, backgrounds, onBack, onSelectValue, currentValue, usingRandomColor, onToggleRandomColor, renderExtraButton, onRemoveValue }: Props) {
|
||||
const containerEl = React.useRef<HTMLDivElement>(null)
|
||||
React.useEffect(() => {
|
||||
containerEl.current?.scrollIntoView(true)
|
||||
@@ -45,8 +47,12 @@ function BackgroundChooser ({ title, backgrounds, onBack, onSelectValue, current
|
||||
<StyledCustomBackgroundSettings>
|
||||
{backgrounds.map((background) => {
|
||||
const value = background.type === 'color' ? background.wallpaperColor : background.wallpaperImageUrl
|
||||
return <BackgroundOption key={value} background={background} onSelectValue={color => onSelectValue(value, /* useRandomColor= */false)} selected={!usingRandomColor && currentValue === value} />
|
||||
return <BackgroundOption key={value} background={background}
|
||||
selected={!usingRandomColor && currentValue === value}
|
||||
onSelectValue={() => onSelectValue(value, /* useRandomColor= */false)}
|
||||
onRemoveValue={onRemoveValue ? () => { onRemoveValue(value) } : undefined} />
|
||||
})}
|
||||
{ renderExtraButton?.() }
|
||||
</StyledCustomBackgroundSettings>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -27,11 +27,15 @@ import BackgroundChooser from './backgroundChooser'
|
||||
import { images, defaultSolidBackgroundColor, solidColorsForBackground, gradientColorsForBackground, defaultGradientColor } from '../../../data/backgrounds'
|
||||
import SponsoredImageToggle from './sponsoredImagesToggle'
|
||||
|
||||
import { RANDOM_SOLID_COLOR_VALUE, RANDOM_GRADIENT_COLOR_VALUE, MAX_CUSTOM_IMAGE_BACKGROUNDS } from 'gen/brave/components/brave_new_tab_ui/brave_new_tab_page.mojom.m.js'
|
||||
|
||||
interface Props {
|
||||
newTabData: NewTab.State
|
||||
toggleBrandedWallpaperOptIn: () => void
|
||||
toggleShowBackgroundImage: () => void
|
||||
useCustomBackgroundImage: () => void
|
||||
chooseNewCustomImageBackground: () => void
|
||||
setCustomImageBackground: (selectedBackground: string) => void
|
||||
removeCustomImageBackground: (background: string) => void
|
||||
setBraveBackground: (selectedBackground: string) => void
|
||||
setColorBackground: (color: string, useRandomColor: boolean) => void
|
||||
brandedWallpaperOptIn: boolean
|
||||
@@ -42,6 +46,7 @@ interface Props {
|
||||
|
||||
enum Location {
|
||||
LIST,
|
||||
CUSTOM_IMAGES,
|
||||
BRAVE_BACKGROUNDS,
|
||||
SOLID_COLORS,
|
||||
GRADIENT_COLORS
|
||||
@@ -64,7 +69,11 @@ class BackgroundImageSettings extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
onClickCustomBackground = () => {
|
||||
this.props.useCustomBackgroundImage()
|
||||
if (this.props.newTabData.customImageBackgrounds?.length) {
|
||||
this.setState({ location: Location.CUSTOM_IMAGES })
|
||||
} else {
|
||||
this.props.chooseNewCustomImageBackground()
|
||||
}
|
||||
}
|
||||
|
||||
onClickBraveBackground = () => {
|
||||
@@ -79,6 +88,26 @@ class BackgroundImageSettings extends React.PureComponent<Props, State> {
|
||||
this.setState({ location: Location.GRADIENT_COLORS })
|
||||
}
|
||||
|
||||
renderUploadButton = (onClick: () => void, checked: boolean, showTitle: boolean) => {
|
||||
return (
|
||||
<StyledCustomBackgroundOption onClick={onClick}>
|
||||
<StyledSelectionBorder selected={checked}>
|
||||
<StyledUploadIconContainer selected={checked}>
|
||||
<UploadIcon />
|
||||
<StyledUploadLabel>
|
||||
{getLocale('customBackgroundImageOptionUploadLabel')}
|
||||
</StyledUploadLabel>
|
||||
</StyledUploadIconContainer>
|
||||
</StyledSelectionBorder>
|
||||
{showTitle && (
|
||||
<StyledCustomBackgroundOptionLabel>
|
||||
{getLocale('customBackgroundImageOptionTitle')}
|
||||
</StyledCustomBackgroundOptionLabel>
|
||||
)}
|
||||
</StyledCustomBackgroundOption>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
newTabData,
|
||||
@@ -100,6 +129,9 @@ class BackgroundImageSettings extends React.PureComponent<Props, State> {
|
||||
const usingRandomBraveBackground = newTabData.backgroundWallpaper?.type === 'brave' && !!newTabData.backgroundWallpaper.random
|
||||
const selectedBraveBackground = newTabData.backgroundWallpaper?.type === 'brave' ? newTabData.backgroundWallpaper.wallpaperImageUrl : undefined
|
||||
|
||||
const usingRandomCustomImageBackground = newTabData.backgroundWallpaper?.type === 'image' && !!newTabData.backgroundWallpaper.random
|
||||
const selectedCustomImageBackground = newTabData.backgroundWallpaper?.type === 'image' ? newTabData.backgroundWallpaper.wallpaperImageUrl : undefined
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.state.location === Location.LIST && (
|
||||
@@ -114,21 +146,7 @@ class BackgroundImageSettings extends React.PureComponent<Props, State> {
|
||||
</SettingsRow>
|
||||
{showBackgroundImage && featureCustomBackgroundEnabled && (
|
||||
<StyledCustomBackgroundSettings>
|
||||
<StyledCustomBackgroundOption
|
||||
onClick={this.onClickCustomBackground}
|
||||
>
|
||||
<StyledSelectionBorder selected={usingCustomImageBackground}>
|
||||
<StyledUploadIconContainer selected={usingCustomImageBackground}>
|
||||
<UploadIcon />
|
||||
<StyledUploadLabel>
|
||||
{getLocale('customBackgroundImageOptionUploadLabel')}
|
||||
</StyledUploadLabel>
|
||||
</StyledUploadIconContainer>
|
||||
</StyledSelectionBorder>
|
||||
<StyledCustomBackgroundOptionLabel>
|
||||
{getLocale('customBackgroundImageOptionTitle')}
|
||||
</StyledCustomBackgroundOptionLabel>
|
||||
</StyledCustomBackgroundOption>
|
||||
{this.renderUploadButton(this.onClickCustomBackground, usingCustomImageBackground, /* showTitle= */ true)}
|
||||
<StyledCustomBackgroundOption
|
||||
onClick={this.onClickBraveBackground}
|
||||
>
|
||||
@@ -197,7 +215,7 @@ class BackgroundImageSettings extends React.PureComponent<Props, State> {
|
||||
backgrounds={solidColorsForBackground}
|
||||
currentValue={selectedBackgroundColor}
|
||||
usingRandomColor={usingSolidColorBackground && usingRandomColor}
|
||||
onToggleRandomColor={on => this.props.setColorBackground(on ? 'solid' : (selectedBackgroundColor ?? defaultSolidBackgroundColor), on)}
|
||||
onToggleRandomColor={on => this.props.setColorBackground(on ? RANDOM_SOLID_COLOR_VALUE : (selectedBackgroundColor ?? defaultSolidBackgroundColor), on)}
|
||||
onSelectValue={this.props.setColorBackground}
|
||||
onBack={() => this.setLocation(Location.LIST)}
|
||||
/>
|
||||
@@ -208,11 +226,26 @@ class BackgroundImageSettings extends React.PureComponent<Props, State> {
|
||||
backgrounds={gradientColorsForBackground}
|
||||
currentValue={selectedBackgroundColor}
|
||||
usingRandomColor={usingGradientBackground && usingRandomColor}
|
||||
onToggleRandomColor={on => this.props.setColorBackground(on ? 'gradient' : (selectedBackgroundColor ?? defaultGradientColor), on)}
|
||||
onToggleRandomColor={on => this.props.setColorBackground(on ? RANDOM_GRADIENT_COLOR_VALUE : (selectedBackgroundColor ?? defaultGradientColor), on)}
|
||||
onSelectValue={this.props.setColorBackground}
|
||||
onBack={() => this.setLocation(Location.LIST)}
|
||||
/>
|
||||
}
|
||||
{this.state.location === Location.CUSTOM_IMAGES &&
|
||||
<BackgroundChooser
|
||||
title={getLocale('customBackgroundImageOptionTitle')}
|
||||
backgrounds={this.props.newTabData.customImageBackgrounds}
|
||||
currentValue={selectedCustomImageBackground}
|
||||
usingRandomColor={usingRandomCustomImageBackground}
|
||||
onToggleRandomColor={on => this.props.setCustomImageBackground(on ? '' : this.props.newTabData.customImageBackgrounds[0].wallpaperImageUrl)}
|
||||
onSelectValue={this.props.setCustomImageBackground}
|
||||
onBack={() => this.setLocation(Location.LIST)}
|
||||
renderExtraButton={ this.props.newTabData.customImageBackgrounds?.length < MAX_CUSTOM_IMAGE_BACKGROUNDS
|
||||
? () => this.renderUploadButton(this.props.chooseNewCustomImageBackground, /* checked= */false, /* showTitle= */ false)
|
||||
: undefined}
|
||||
onRemoveValue={this.props.removeCustomImageBackground}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// you can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import {
|
||||
StyledCustomBackgroundOption,
|
||||
@@ -12,16 +13,44 @@ import {
|
||||
StyledSelectionBorder
|
||||
} from '../../../components/default'
|
||||
|
||||
import { CloseCircleIcon } from 'brave-ui/components/icons'
|
||||
|
||||
interface Props {
|
||||
background: NewTab.BackgroundWallpaper
|
||||
selected: boolean
|
||||
onSelectValue: (value: NewTab.BackgroundWallpaper) => void
|
||||
onRemoveValue?: (value: NewTab.BackgroundWallpaper) => void
|
||||
}
|
||||
|
||||
export default function BackgroundOption ({ background, selected, onSelectValue }: Props) {
|
||||
const StyledRemoveButton = styled.div<{hovered: boolean}>`
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: unset;
|
||||
background: transparent;
|
||||
color: white;
|
||||
visibility: ${p => p.hovered ? 'visible' : 'hidden'};
|
||||
& svg {
|
||||
border-radius: 20px;
|
||||
filter: drop-shadow( 0 0 5px rgba(0, 0, 0, .7));
|
||||
}
|
||||
`
|
||||
|
||||
export default function BackgroundOption ({ background, selected, onSelectValue, onRemoveValue }: Props) {
|
||||
const [hovered, setHovered] = React.useState(false)
|
||||
return (
|
||||
<StyledCustomBackgroundOption onClick={_ => onSelectValue(background)}>
|
||||
<StyledSelectionBorder selected={selected}>
|
||||
<StyledCustomBackgroundOption onClick={_ => onSelectValue(background) } onMouseOver={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>
|
||||
<StyledSelectionBorder selected={selected} removable={!!onRemoveValue}>
|
||||
{ onRemoveValue &&
|
||||
<StyledRemoveButton hovered={hovered} onClick={(e) => {
|
||||
onRemoveValue(background)
|
||||
e.stopPropagation()
|
||||
}}>
|
||||
<CloseCircleIcon />
|
||||
</StyledRemoveButton>
|
||||
}
|
||||
{
|
||||
background.type === 'color'
|
||||
? <StyledCustomBackgroundOptionColor colorValue={background.wallpaperColor} selected={selected}/>
|
||||
|
||||
@@ -22,7 +22,7 @@ import { setMostVisitedSettings } from '../api/topSites'
|
||||
// Utils
|
||||
import { handleWidgetPrefsChange } from './stack_widget_reducer'
|
||||
import { NewTabAdsData } from '../api/newTabAdsData'
|
||||
import { Background } from '../api/background'
|
||||
import { Background, CustomBackground } from '../api/background'
|
||||
|
||||
let sideEffectState: NewTab.State = storage.load()
|
||||
|
||||
@@ -54,13 +54,19 @@ export const newTabReducer: Reducer<NewTab.State | undefined> = (state: NewTab.S
|
||||
// Auto-dismiss of together prompt only
|
||||
// takes effect on the next page view and not the
|
||||
// page view that the action occurred on.
|
||||
braveTalkPromptDismissed: state.braveTalkPromptDismissed || state.braveTalkPromptAutoDismissed
|
||||
braveTalkPromptDismissed: state.braveTalkPromptDismissed || state.braveTalkPromptAutoDismissed,
|
||||
customImageBackgrounds: initialDataPayload.customImageBackgrounds
|
||||
}
|
||||
|
||||
if (initialDataPayload.wallpaperData) {
|
||||
let backgroundWallpaper = initialDataPayload.wallpaperData.backgroundWallpaper
|
||||
if (backgroundWallpaper?.type === 'color' && backgroundWallpaper.random) {
|
||||
backgroundWallpaper = backgroundAPI.randomColorBackground(backgroundWallpaper.wallpaperColor)
|
||||
} else if (backgroundWallpaper?.type === 'image' && backgroundWallpaper.random) {
|
||||
const customBackgrounds = state.customImageBackgrounds
|
||||
if (customBackgrounds.length) {
|
||||
backgroundWallpaper = { ...customBackgrounds[Math.floor(Math.random() * customBackgrounds.length)], random: true }
|
||||
}
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -133,10 +139,17 @@ export const newTabReducer: Reducer<NewTab.State | undefined> = (state: NewTab.S
|
||||
const url = background.custom.url.url
|
||||
const color = background.custom.color
|
||||
const random = background.custom.useRandomItem
|
||||
if (url) {
|
||||
state.backgroundWallpaper = { type: 'image', wallpaperImageUrl: url }
|
||||
} else if (color) {
|
||||
if (color) {
|
||||
state.backgroundWallpaper = random ? backgroundAPI.randomColorBackground(color) : { type: 'color', wallpaperColor: color, random }
|
||||
} else if (url) {
|
||||
// Custom Image was specified
|
||||
state.backgroundWallpaper = { type: 'image', wallpaperImageUrl: url }
|
||||
} else if (random) {
|
||||
// Random custom image should be used.
|
||||
const customBackgrounds = state.customImageBackgrounds
|
||||
if (customBackgrounds.length) {
|
||||
state.backgroundWallpaper = { ...customBackgrounds[Math.floor(Math.random() * customBackgrounds.length)], random: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +168,14 @@ export const newTabReducer: Reducer<NewTab.State | undefined> = (state: NewTab.S
|
||||
}
|
||||
break
|
||||
|
||||
case types.CUSTOM_IMAGE_BACKGROUNDS_UPDATED:
|
||||
const customBackgrounds = payload as CustomBackground[]
|
||||
state = {
|
||||
...state,
|
||||
customImageBackgrounds: customBackgrounds.map(background => ({ type: 'image', wallpaperImageUrl: background.url.url }))
|
||||
}
|
||||
break
|
||||
|
||||
case types.NEW_TAB_PRIVATE_TAB_DATA_UPDATED:
|
||||
const privateTabData = payload as PrivateTabData
|
||||
state = {
|
||||
|
||||
@@ -145,7 +145,8 @@ export const defaultState: NewTab.State = {
|
||||
},
|
||||
ftxState: {
|
||||
optedIntoMarkets: false
|
||||
}
|
||||
},
|
||||
customImageBackgrounds: []
|
||||
}
|
||||
|
||||
if (chrome.extension.inIncognitoContext) {
|
||||
|
||||
@@ -58,7 +58,7 @@ updateImages(images.map((image): NewTab.BraveBackground => {
|
||||
}))
|
||||
|
||||
export const Regular = () => {
|
||||
const doNothing = (value: boolean) => value
|
||||
const doNothing = (value?: any) => value
|
||||
const state = store.getState()
|
||||
const newTabData = useNewTabData(state.newTabData)
|
||||
const gridSitesData = getGridSitesData(state.gridSitesData)
|
||||
@@ -84,7 +84,9 @@ export const Regular = () => {
|
||||
saveSetAllStackWidgets={doNothing}
|
||||
getBraveNewsDisplayAd={getBraveNewsDisplayAd}
|
||||
setBraveBackground={onUseBraveBackground}
|
||||
useCustomBackgroundImage={() => {}}
|
||||
chooseNewCustomBackgroundImage={doNothing}
|
||||
setCustomImageBackground={doNothing}
|
||||
removeCustomImageBackground={doNothing}
|
||||
setColorBackground={onChangeColoredBackground}
|
||||
/>
|
||||
)
|
||||
|
||||
Vendored
+3
-1
@@ -15,6 +15,7 @@ declare namespace NewTab {
|
||||
export type ImageBackground = {
|
||||
type: 'image'
|
||||
wallpaperImageUrl: string
|
||||
random?: boolean
|
||||
}
|
||||
|
||||
export type BraveBackground = Omit<ImageBackground, 'type'> & {
|
||||
@@ -157,7 +158,6 @@ declare namespace NewTab {
|
||||
torInitProgress: string,
|
||||
isTor: boolean
|
||||
isQwant: boolean
|
||||
backgroundWallpaper?: BackgroundWallpaper
|
||||
gridLayoutSize?: 'small'
|
||||
showGridSiteRemovedNotification?: boolean
|
||||
showBackgroundImage: boolean
|
||||
@@ -167,6 +167,8 @@ declare namespace NewTab {
|
||||
stats: Stats,
|
||||
braveTalkPromptAllowed: boolean
|
||||
brandedWallpaper?: BrandedWallpaper
|
||||
backgroundWallpaper?: BackgroundWallpaper
|
||||
customImageBackgrounds: ImageBackground[]
|
||||
}
|
||||
|
||||
export interface RewardsWidgetState {
|
||||
|
||||
@@ -48,9 +48,10 @@ base::Value::Dict NTPCustomBackgroundImagesService::GetBackground() const {
|
||||
base::Value::Dict data;
|
||||
data.Set(kIsBackgroundKey, true);
|
||||
if (delegate_->IsCustomImageBackgroundEnabled()) {
|
||||
data.Set(kWallpaperImageURLKey, kCustomWallpaperURL);
|
||||
data.Set(kWallpaperImageURLKey,
|
||||
delegate_->GetCustomBackgroundImageURL().spec());
|
||||
data.Set(kWallpaperTypeKey, "image");
|
||||
data.Set(kWallpaperRandomKey, false);
|
||||
data.Set(kWallpaperRandomKey, delegate_->ShouldUseRandomValue());
|
||||
} else if (delegate_->IsColorBackgroundEnabled()) {
|
||||
data.Set(kWallpaperColorKey, delegate_->GetColor());
|
||||
data.Set(kWallpaperTypeKey, "color");
|
||||
@@ -59,8 +60,9 @@ base::Value::Dict NTPCustomBackgroundImagesService::GetBackground() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
base::FilePath NTPCustomBackgroundImagesService::GetImageFilePath() {
|
||||
return delegate_->GetCustomBackgroundImageLocalFilePath();
|
||||
base::FilePath NTPCustomBackgroundImagesService::GetImageFilePath(
|
||||
const GURL& url) {
|
||||
return delegate_->GetCustomBackgroundImageLocalFilePath(url);
|
||||
}
|
||||
|
||||
void NTPCustomBackgroundImagesService::Shutdown() {
|
||||
|
||||
@@ -28,7 +28,9 @@ class NTPCustomBackgroundImagesService : public KeyedService {
|
||||
class Delegate {
|
||||
public:
|
||||
virtual bool IsCustomImageBackgroundEnabled() const = 0;
|
||||
virtual base::FilePath GetCustomBackgroundImageLocalFilePath() const = 0;
|
||||
virtual base::FilePath GetCustomBackgroundImageLocalFilePath(
|
||||
const GURL& url) const = 0;
|
||||
virtual GURL GetCustomBackgroundImageURL() const = 0;
|
||||
|
||||
virtual bool IsColorBackgroundEnabled() const = 0;
|
||||
virtual std::string GetColor() const = 0;
|
||||
@@ -50,7 +52,7 @@ class NTPCustomBackgroundImagesService : public KeyedService {
|
||||
|
||||
bool ShouldShowCustomBackground() const;
|
||||
base::Value::Dict GetBackground() const;
|
||||
base::FilePath GetImageFilePath();
|
||||
base::FilePath GetImageFilePath(const GURL& url);
|
||||
|
||||
private:
|
||||
// KeyedService overrides:
|
||||
|
||||
@@ -48,7 +48,7 @@ void NTPCustomImagesSource::StartDataRequest(
|
||||
const content::WebContents::Getter& wc_getter,
|
||||
GotDataCallback callback) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
GetImageFile(service_->GetImageFilePath(), std::move(callback));
|
||||
GetImageFile(service_->GetImageFilePath(url), std::move(callback));
|
||||
}
|
||||
|
||||
std::string NTPCustomImagesSource::GetMimeType(const GURL& url) {
|
||||
|
||||
@@ -10,13 +10,11 @@ namespace ntp_background_images {
|
||||
|
||||
constexpr char kBackgroundWallpaperHost[] = "background-wallpaper";
|
||||
constexpr char kBrandedWallpaperHost[] = "branded-wallpaper";
|
||||
constexpr char kCustomWallpaperHost[] = "custom-wallpaper";
|
||||
constexpr char kSuperReferralPath[] = "super-referral/";
|
||||
constexpr char kSponsoredImagesPath[] = "sponsored-images/";
|
||||
|
||||
constexpr char kCustomWallpaperFileName[] = "background.jpg";
|
||||
constexpr char kCustomWallpaperURL[] =
|
||||
"chrome://custom-wallpaper/background.jpg";
|
||||
constexpr char kCustomWallpaperHost[] = "custom-wallpaper";
|
||||
constexpr char kCustomWallpaperURL[] = "chrome://custom-wallpaper/";
|
||||
|
||||
constexpr char kCampaignsKey[] = "campaigns";
|
||||
|
||||
|
||||
@@ -122,9 +122,13 @@ class TestDelegate : public NTPCustomBackgroundImagesService::Delegate {
|
||||
bool IsCustomImageBackgroundEnabled() const override {
|
||||
return image_enabled_;
|
||||
}
|
||||
base::FilePath GetCustomBackgroundImageLocalFilePath() const override {
|
||||
base::FilePath GetCustomBackgroundImageLocalFilePath(
|
||||
const GURL& url) const override {
|
||||
return base::FilePath();
|
||||
}
|
||||
GURL GetCustomBackgroundImageURL() const override {
|
||||
return GURL(std::string(kCustomWallpaperURL) + "foo.jpg");
|
||||
}
|
||||
|
||||
bool IsColorBackgroundEnabled() const override { return color_enabled_; }
|
||||
std::string GetColor() const override { return "#ff0000"; }
|
||||
@@ -412,7 +416,8 @@ TEST_F(NTPBackgroundImagesViewCounterTest, GetCurrentWallpaperTest) {
|
||||
delegate_->image_enabled_ = true;
|
||||
background = view_counter_->GetCurrentWallpaper();
|
||||
bg_url = background->FindString(kWallpaperImageURLKey);
|
||||
EXPECT_EQ("chrome://custom-wallpaper/background.jpg", *bg_url);
|
||||
EXPECT_TRUE(base::StartsWith(*bg_url, kCustomWallpaperURL))
|
||||
<< "actual url " << *bg_url;
|
||||
|
||||
// Disable custom image background.
|
||||
delegate_->image_enabled_ = false;
|
||||
|
||||
Reference in New Issue
Block a user