diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /browser/components/urlbar/private/YelpSuggestions.sys.mjs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/urlbar/private/YelpSuggestions.sys.mjs')
-rw-r--r-- | browser/components/urlbar/private/YelpSuggestions.sys.mjs | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/browser/components/urlbar/private/YelpSuggestions.sys.mjs b/browser/components/urlbar/private/YelpSuggestions.sys.mjs new file mode 100644 index 0000000000..546c7ce216 --- /dev/null +++ b/browser/components/urlbar/private/YelpSuggestions.sys.mjs @@ -0,0 +1,264 @@ +/* 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 { BaseFeature } from "resource:///modules/urlbar/private/BaseFeature.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs", + MerinoClient: "resource:///modules/MerinoClient.sys.mjs", + UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs", + UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs", + UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs", +}); + +const RESULT_MENU_COMMAND = { + HELP: "help", + INACCURATE_LOCATION: "inaccurate_location", + NOT_INTERESTED: "not_interested", + NOT_RELEVANT: "not_relevant", + SHOW_LESS_FREQUENTLY: "show_less_frequently", +}; + +/** + * A feature for Yelp suggestions. + */ +export class YelpSuggestions extends BaseFeature { + get shouldEnable() { + return ( + lazy.UrlbarPrefs.get("suggest.quicksuggest.sponsored") && + lazy.UrlbarPrefs.get("yelpFeatureGate") && + lazy.UrlbarPrefs.get("suggest.yelp") + ); + } + + get enablingPreferences() { + return ["suggest.quicksuggest.sponsored", "suggest.yelp"]; + } + + get rustSuggestionTypes() { + return ["Yelp"]; + } + + get showLessFrequentlyCount() { + const count = lazy.UrlbarPrefs.get("yelp.showLessFrequentlyCount") || 0; + return Math.max(count, 0); + } + + get canShowLessFrequently() { + const cap = + lazy.UrlbarPrefs.get("yelpShowLessFrequentlyCap") || + lazy.QuickSuggest.backend.config?.showLessFrequentlyCap || + 0; + return !cap || this.showLessFrequentlyCount < cap; + } + + getSuggestionTelemetryType(suggestion) { + return "yelp"; + } + + enable(enabled) { + if (!enabled) { + this.#merino = null; + } + } + + async makeResult(queryContext, suggestion, searchString) { + // If the user clicked "Show less frequently" at least once or if the + // subject wasn't typed in full, then apply the min length threshold and + // return null if the entire search string is too short. + if ( + (this.showLessFrequentlyCount || !suggestion.subjectExactMatch) && + searchString.length < this.#minKeywordLength + ) { + return null; + } + + suggestion.is_top_pick = lazy.UrlbarPrefs.get("yelpSuggestPriority"); + + let url = new URL(suggestion.url); + let title = suggestion.title; + if (!url.searchParams.has(suggestion.locationParam)) { + let city = await this.#fetchCity(); + + // If we can't get city from Merino, rely on Yelp own. + if (city) { + url.searchParams.set(suggestion.locationParam, city); + + if (!suggestion.hasLocationSign) { + title += " in"; + } + + title += ` ${city}`; + } + } + + url.searchParams.set("utm_medium", "partner"); + url.searchParams.set("utm_source", "mozilla"); + + let resultProperties = { + isRichSuggestion: true, + richSuggestionIconSize: 38, + showFeedbackMenu: true, + }; + if (!suggestion.is_top_pick) { + resultProperties.richSuggestionIconSize = 16; + resultProperties.isSuggestedIndexRelativeToGroup = true; + resultProperties.suggestedIndex = lazy.UrlbarPrefs.get( + "yelpSuggestNonPriorityIndex" + ); + } + + return Object.assign( + new lazy.UrlbarResult( + lazy.UrlbarUtils.RESULT_TYPE.URL, + lazy.UrlbarUtils.RESULT_SOURCE.SEARCH, + ...lazy.UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, { + url: url.toString(), + originalUrl: suggestion.url, + title: [title, lazy.UrlbarUtils.HIGHLIGHT.TYPED], + shouldShowUrl: true, + bottomTextL10n: { id: "firefox-suggest-yelp-bottom-text" }, + }) + ), + resultProperties + ); + } + + getResultCommands(result) { + let commands = [ + { + name: RESULT_MENU_COMMAND.INACCURATE_LOCATION, + l10n: { + id: "firefox-suggest-weather-command-inaccurate-location", + }, + }, + ]; + + if (this.canShowLessFrequently) { + commands.push({ + name: RESULT_MENU_COMMAND.SHOW_LESS_FREQUENTLY, + l10n: { + id: "firefox-suggest-command-show-less-frequently", + }, + }); + } + + commands.push( + { + l10n: { + id: "firefox-suggest-command-dont-show-this", + }, + children: [ + { + name: RESULT_MENU_COMMAND.NOT_RELEVANT, + l10n: { + id: "firefox-suggest-command-not-relevant", + }, + }, + { + name: RESULT_MENU_COMMAND.NOT_INTERESTED, + l10n: { + id: "firefox-suggest-command-not-interested", + }, + }, + ], + }, + { name: "separator" }, + { + name: RESULT_MENU_COMMAND.HELP, + l10n: { + id: "urlbar-result-menu-learn-more-about-firefox-suggest", + }, + } + ); + + return commands; + } + + handleCommand(view, result, selType, searchString) { + switch (selType) { + case RESULT_MENU_COMMAND.HELP: + // "help" is handled by UrlbarInput, no need to do anything here. + break; + case RESULT_MENU_COMMAND.INACCURATE_LOCATION: + // Currently the only way we record this feedback is in the Glean + // engagement event. As with all commands, it will be recorded with an + // `engagement_type` value that is the command's name, in this case + // `inaccurate_location`. + view.acknowledgeFeedback(result); + break; + // selType == "dismiss" when the user presses the dismiss key shortcut. + case "dismiss": + case RESULT_MENU_COMMAND.NOT_RELEVANT: + lazy.QuickSuggest.blockedSuggestions.add(result.payload.originalUrl); + result.acknowledgeDismissalL10n = { + id: "firefox-suggest-dismissal-acknowledgment-one-yelp", + }; + view.controller.removeResult(result); + break; + case RESULT_MENU_COMMAND.NOT_INTERESTED: + lazy.UrlbarPrefs.set("suggest.yelp", false); + result.acknowledgeDismissalL10n = { + id: "firefox-suggest-dismissal-acknowledgment-all-yelp", + }; + view.controller.removeResult(result); + break; + case RESULT_MENU_COMMAND.SHOW_LESS_FREQUENTLY: + view.acknowledgeFeedback(result); + this.incrementShowLessFrequentlyCount(); + if (!this.canShowLessFrequently) { + view.invalidateResultMenuCommands(); + } + lazy.UrlbarPrefs.set("yelp.minKeywordLength", searchString.length + 1); + break; + } + } + + incrementShowLessFrequentlyCount() { + if (this.canShowLessFrequently) { + lazy.UrlbarPrefs.set( + "yelp.showLessFrequentlyCount", + this.showLessFrequentlyCount + 1 + ); + } + } + + get #minKeywordLength() { + // It's unusual to get both a Nimbus variable and its fallback pref at the + // same time, but we have a good reason. To recap, if a variable doesn't + // have a value, then the value of its fallback will be returned; otherwise + // the variable value will be returned. That's usually what we want, but for + // Yelp, we set the pref each time the user clicks "show less frequently", + // and we want the variable to act only as an initial min length. In other + // words, if the pref has a user value (because we set it), use it; + // otherwise use the initial value defined by the variable. + return Math.max( + lazy.UrlbarPrefs.get("yelpMinKeywordLength") || 0, + lazy.UrlbarPrefs.get("yelp.minKeywordLength") || 0, + 0 + ); + } + + async #fetchCity() { + if (!this.#merino) { + this.#merino = new lazy.MerinoClient(this.constructor.name); + } + + let results = await this.#merino.fetch({ + providers: ["geolocation"], + query: "", + }); + + if (!results.length) { + return null; + } + + let { city, region } = results[0].custom_details.geolocation; + return [city, region].filter(loc => !!loc).join(", "); + } + + #merino = null; +} |