Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ntp): store last NTP search engine as a pref #26137

Merged
merged 2 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 52 additions & 49 deletions browser/ui/webui/new_tab_page/brave_new_tab_message_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,14 @@
#include <utility>

#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "base/json/values_util.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "brave/browser/brave_ads/ads_service_factory.h"
#include "brave/browser/ntp_background/view_counter_service_factory.h"
#include "brave/browser/profiles/profile_util.h"
#include "brave/browser/search_engines/pref_names.h"
#include "brave/browser/ui/webui/new_tab_page/brave_new_tab_ui.h"
#include "brave/components/brave_ads/core/public/ads_util.h"
#include "brave/components/brave_news/common/pref_names.h"
#include "brave/components/brave_perf_predictor/common/pref_names.h"
Expand All @@ -32,13 +28,11 @@
#include "brave/components/ntp_background_images/browser/view_counter_service.h"
#include "brave/components/ntp_background_images/common/pref_names.h"
#include "brave/components/p3a/utils.h"
#include "brave/components/services/bat_ads/public/interfaces/bat_ads.mojom.h"
#include "brave/components/time_period_storage/weekly_storage.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/plural_string_handler.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/grit/brave_components_strings.h"
#include "components/prefs/pref_change_registrar.h"
Expand Down Expand Up @@ -95,6 +89,9 @@ base::Value::Dict GetPreferencesDictionary(PrefService* prefs) {
pref_data.Set(
"showSearchBox",
prefs->GetBoolean(brave_search_conversion::prefs::kShowNTPSearchBox));
pref_data.Set("lastUsedNtpSearchEngine",
prefs->GetString(
brave_search_conversion::prefs::kLastUsedNTPSearchEngine));
pref_data.Set("promptEnableSearchSuggestions",
prefs->GetBoolean(
brave_search_conversion::prefs::kPromptEnableSuggestions));
Expand Down Expand Up @@ -292,6 +289,10 @@ void BraveNewTabMessageHandler::OnJavascriptAllowed() {
brave_search_conversion::prefs::kShowNTPSearchBox,
base::BindRepeating(&BraveNewTabMessageHandler::OnPreferencesChanged,
base::Unretained(this)));
pref_change_registrar_.Add(
brave_search_conversion::prefs::kLastUsedNTPSearchEngine,
base::BindRepeating(&BraveNewTabMessageHandler::OnPreferencesChanged,
base::Unretained(this)));
pref_change_registrar_.Add(
brave_search_conversion::prefs::kPromptEnableSuggestions,
base::BindRepeating(&BraveNewTabMessageHandler::OnPreferencesChanged,
Expand Down Expand Up @@ -393,73 +394,75 @@ void BraveNewTabMessageHandler::HandleSaveNewTabPagePref(
}
PrefService* prefs = profile_->GetPrefs();
// Collect args
std::string settingsKeyInput = args[0].GetString();
auto settingsValue = args[1].Clone();
std::string settingsKey;
std::string settings_key_input = args[0].GetString();
auto settings_value = args[1].Clone();
std::string settings_key;

// Prevent News onboarding below NTP and sponsored NTP notification
// state from triggering the "shown & changed" answer for the
// customize dialog metric.
if (settingsKeyInput != "showToday" &&
settingsKeyInput != "isBraveNewsOptedIn" &&
settingsKeyInput != "isBrandedWallpaperNotificationDismissed") {
if (settings_key_input != "showToday" &&
settings_key_input != "isBraveNewsOptedIn" &&
settings_key_input != "isBrandedWallpaperNotificationDismissed") {
p3a::RecordValueIfGreater<NTPCustomizeUsage>(
NTPCustomizeUsage::kOpenedAndEdited, kCustomizeUsageHistogramName,
kNTPCustomizeUsageStatus, g_browser_process->local_state());
}

// Handle string settings
if (settingsValue.is_string()) {
const auto settingsValueString = settingsValue.GetString();
if (settingsKeyInput == "clockFormat") {
settingsKey = kNewTabPageClockFormat;
if (settings_value.is_string()) {
const auto settings_value_string = settings_value.GetString();
if (settings_key_input == "clockFormat") {
settings_key = kNewTabPageClockFormat;
} else if (settings_key_input == "lastUsedNtpSearchEngine") {
settings_key = brave_search_conversion::prefs::kLastUsedNTPSearchEngine;
} else {
LOG(ERROR) << "Invalid setting key";
return;
}
prefs->SetString(settingsKey, settingsValueString);
prefs->SetString(settings_key, settings_value_string);
return;
}

// Handle bool settings
if (!settingsValue.is_bool()) {
if (!settings_value.is_bool()) {
LOG(ERROR) << "Invalid value type";
return;
}
const auto settingsValueBool = settingsValue.GetBool();
if (settingsKeyInput == "showBackgroundImage") {
settingsKey = kNewTabPageShowBackgroundImage;
} else if (settingsKeyInput == "brandedWallpaperOptIn") {
const auto settings_value_bool = settings_value.GetBool();
if (settings_key_input == "showBackgroundImage") {
settings_key = kNewTabPageShowBackgroundImage;
} else if (settings_key_input == "brandedWallpaperOptIn") {
// TODO(simonhong): I think above |brandedWallpaperOptIn| should be changed
// to |sponsoredImagesWallpaperOptIn|.
settingsKey = kNewTabPageShowSponsoredImagesBackgroundImage;
} else if (settingsKeyInput == "showClock") {
settingsKey = kNewTabPageShowClock;
} else if (settingsKeyInput == "showStats") {
settingsKey = kNewTabPageShowStats;
} else if (settingsKeyInput == "showToday") {
settingsKey = brave_news::prefs::kNewTabPageShowToday;
} else if (settingsKeyInput == "isBraveNewsOptedIn") {
settingsKey = brave_news::prefs::kBraveNewsOptedIn;
} else if (settingsKeyInput == "showRewards") {
settingsKey = kNewTabPageShowRewards;
} else if (settingsKeyInput == "isBrandedWallpaperNotificationDismissed") {
settingsKey = kBrandedWallpaperNotificationDismissed;
} else if (settingsKeyInput == "hideAllWidgets") {
settingsKey = kNewTabPageHideAllWidgets;
} else if (settingsKeyInput == "showBraveTalk") {
settingsKey = kNewTabPageShowBraveTalk;
} else if (settingsKeyInput == "showSearchBox") {
settingsKey = brave_search_conversion::prefs::kShowNTPSearchBox;
} else if (settingsKeyInput == "promptEnableSearchSuggestions") {
settingsKey = brave_search_conversion::prefs::kPromptEnableSuggestions;
} else if (settingsKeyInput == "searchSuggestionsEnabled") {
settingsKey = prefs::kSearchSuggestEnabled;
settings_key = kNewTabPageShowSponsoredImagesBackgroundImage;
} else if (settings_key_input == "showClock") {
settings_key = kNewTabPageShowClock;
} else if (settings_key_input == "showStats") {
settings_key = kNewTabPageShowStats;
} else if (settings_key_input == "showToday") {
settings_key = brave_news::prefs::kNewTabPageShowToday;
} else if (settings_key_input == "isBraveNewsOptedIn") {
settings_key = brave_news::prefs::kBraveNewsOptedIn;
} else if (settings_key_input == "showRewards") {
settings_key = kNewTabPageShowRewards;
} else if (settings_key_input == "isBrandedWallpaperNotificationDismissed") {
settings_key = kBrandedWallpaperNotificationDismissed;
} else if (settings_key_input == "hideAllWidgets") {
settings_key = kNewTabPageHideAllWidgets;
} else if (settings_key_input == "showBraveTalk") {
settings_key = kNewTabPageShowBraveTalk;
} else if (settings_key_input == "showSearchBox") {
settings_key = brave_search_conversion::prefs::kShowNTPSearchBox;
} else if (settings_key_input == "promptEnableSearchSuggestions") {
settings_key = brave_search_conversion::prefs::kPromptEnableSuggestions;
} else if (settings_key_input == "searchSuggestionsEnabled") {
settings_key = prefs::kSearchSuggestEnabled;
} else {
LOG(ERROR) << "Invalid setting key";
return;
}
prefs->SetBoolean(settingsKey, settingsValueBool);
prefs->SetBoolean(settings_key, settings_value_bool);
}

void BraveNewTabMessageHandler::HandleRegisterNewTabPageView(
Expand Down Expand Up @@ -541,10 +544,10 @@ void BraveNewTabMessageHandler::HandleGetWallpaperData(
// Even though we show sponsored image, we should pass "Background wallpaper"
// data so that NTP customization menu can know which wallpaper is selected by
// users.
auto backgroundWallpaper = service->GetCurrentWallpaper();
auto background_wallpaper = service->GetCurrentWallpaper();
wallpaper.Set(kBackgroundWallpaperKey,
backgroundWallpaper
? base::Value(std::move(*backgroundWallpaper))
background_wallpaper
? base::Value(std::move(*background_wallpaper))
: base::Value());

const std::string* creative_instance_id =
Expand Down
85 changes: 85 additions & 0 deletions components/brave_new_tab_ui/components/search/EngineContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) 2024 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 https://mozilla.org/MPL/2.0/.
import { SearchEngineInfo } from '../../api/background'
import { useNewTabPref } from '../../hooks/usePref'
import * as React from 'react';
import { ENABLED_SEARCH_ENGINES_KEY, LAST_SEARCH_ENGINE_KEY, braveSearchHost } from './config';

interface Engine {
setLastSearchEngine: (engine: SearchEngineInfo) => void
lastSearchEngine: string,
engineConfig: Record<string, boolean>,
setEngineConfig: (engine: string, enabled: boolean) => void,
}

const EngineContext = React.createContext<Engine>({
setLastSearchEngine: () => { },
lastSearchEngine: '',
engineConfig: {},
setEngineConfig: () => { },
})

const searchEngineConfig = () => {
const localStorageValue = localStorage.getItem(ENABLED_SEARCH_ENGINES_KEY);
if (localStorageValue) {
return JSON.parse(localStorageValue);
}
return {
// Default to enabling Brave Search
[braveSearchHost]: true
};
}

export function EngineContextProvider(props: React.PropsWithChildren<{}>) {
const [lastUsed, setLastUsed] = useNewTabPref('lastUsedNtpSearchEngine')
const lastLocalStorage = localStorage.getItem(LAST_SEARCH_ENGINE_KEY)
const [config, setConfig] = React.useState<Record<string, boolean>>(searchEngineConfig);

const setLastNtpSearchEngine = React.useCallback((engine: SearchEngineInfo) => {
setLastUsed(engine.host);
}, [setLastUsed])

const lastNtpSearchEngine = React.useMemo(() => {
let last = lastUsed;

// Migrates the existing local storage value to the new pref value, letting the local storage value take precedence.
if (lastLocalStorage && config[lastLocalStorage] && lastUsed !== lastLocalStorage) {
setLastUsed(lastLocalStorage);
last = lastLocalStorage;
localStorage.removeItem(LAST_SEARCH_ENGINE_KEY)
}
IanKrieger marked this conversation as resolved.
Show resolved Hide resolved

// If the last search engine we used has been disabled or doesn't exist in the config, return the first enabled
// one, or Brave Search.
if (!last || !config[last]) {
return Object.keys(config).find(key => config[key]) ?? braveSearchHost
}

return last
}, [lastUsed, lastLocalStorage, config]);

const setEngineEnabled = React.useCallback(((engine: string, enabled: boolean) => {
setConfig(prevConfig => {
const newState = { ...prevConfig, [engine]: enabled };
localStorage.setItem(ENABLED_SEARCH_ENGINES_KEY, JSON.stringify(newState));
return newState;
});
}), [])

return (
<EngineContext.Provider value={{
setLastSearchEngine: setLastNtpSearchEngine,
lastSearchEngine: lastNtpSearchEngine,
engineConfig: config,
setEngineConfig: setEngineEnabled,
}} >
{props.children}
</EngineContext.Provider>
)
}

export function useEngineContext() {
return React.useContext(EngineContext)
}
15 changes: 8 additions & 7 deletions components/brave_new_tab_ui/components/search/SearchContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AutocompleteResult, OmniboxPopupSelection, PageHandler, PageHandlerRemo
import { stringToMojoString16 } from 'chrome://resources/js/mojo_type_util.js';
import * as React from 'react';
import getNTPBrowserAPI, { SearchEngineInfo } from '../../api/background';
import { getDefaultSearchEngine, isSearchEngineEnabled, setDefaultSearchEngine } from './config';
import { useEngineContext } from './EngineContext';

interface Context {
open: boolean,
Expand All @@ -23,7 +23,7 @@ interface Context {

const Context = React.createContext<Context>({
open: false,
setOpen: () => {},
setOpen: () => { },
query: '',
setQuery: () => { },
searchEngine: undefined,
Expand Down Expand Up @@ -73,18 +73,19 @@ class SearchPage implements PageInterface {
for (const listener of this.selectionListeners) listener(selection)
}

setInputText(inputText: string) {}
setThumbnail(thumbnailUrl: string) {}
setInputText(inputText: string) { }
setThumbnail(thumbnailUrl: string) { }
}

export const search = new SearchPage()

export function SearchContext(props: React.PropsWithChildren<{}>) {
const { engineConfig, lastSearchEngine, setLastSearchEngine } = useEngineContext()
const [open, setOpen] = React.useState(false)
const [searchEngine, setSearchEngineInternal] = React.useState<SearchEngineInfo>()
const [query, setQuery] = React.useState('')
const { result: searchEngines = [] } = usePromise(() => searchEnginesPromise, [])
const filteredSearchEngines = searchEngines.filter(isSearchEngineEnabled)
const filteredSearchEngines = searchEngines.filter(s => engineConfig[s.host])

const setSearchEngine = React.useCallback((engine: SearchEngineInfo | string) => {
if (typeof engine === 'string') {
Expand All @@ -93,7 +94,7 @@ export function SearchContext(props: React.PropsWithChildren<{}>) {

if (!engine) return

setDefaultSearchEngine(engine)
setLastSearchEngine(engine)
getNTPBrowserAPI().newTabMetrics.reportNTPSearchDefaultEngine(engine.prepopulateId)
setSearchEngineInternal(engine)
}, [searchEngines]);
Expand All @@ -102,7 +103,7 @@ export function SearchContext(props: React.PropsWithChildren<{}>) {
React.useEffect(() => {
if (!searchEngines.length) return

const match = filteredSearchEngines.find(s => s.host === getDefaultSearchEngine())
const match = filteredSearchEngines.find(s => s.host === lastSearchEngine)
?? searchEngines[0]
getNTPBrowserAPI().newTabMetrics.reportNTPSearchDefaultEngine(match.prepopulateId)
setSearchEngine(match)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function MaybeImage(props: React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLI
export function SearchEngineIcon(props: { engine?: SearchEngineInfo, className?: string }) {
if (!props.engine) return null

const nalaIcon = icons[props.engine.host]
const nalaIcon = icons[props.engine.host as keyof typeof icons]

return nalaIcon
? <Icon name={nalaIcon} className={props.className} />
Expand Down
Loading
Loading