This is the relevant branch config (non-top-pick) from [the final Yelp v3 experiment](https://experimenter.services.mozilla.com/nimbus/yelp-suggestions-pilot-v3/summary) that we should now make the default: ```json { "yelpFeatureGate": true, "yelpSuggestPriority": false, "yelpShowLessFrequentlyCap": 3, "yelpMinKeywordLength": 4, "exposureResults": "rust_yelp", "showExposureResults": true } ``` Ideally we might define `minKeywordLength` as part of the Yelp data in remote settings and then modify the Rust component to ingest it as Yelp config data, similar to weather suggestions. But that would require modifying the Rust component, and Product wants Yelp for 129, so that means we would need to uplift a vendoring of application-services to Beta, which I think we should avoid. So instead I just set the default value of the `minKeywordLength` pref to 4. I kept the Nimbus variable and it will override the pref if the pref still has its default value. That way we can still use Nimbus to set it if there's some emergency. I also kept the `yelpSuggestNonPriorityIndex` and `yelpSuggestPriority` variables, in case we need those too. Eventually we should clean up all the variables and prefs we're not using anymore. Differential Revision: https://phabricator.services.mozilla.com/D215709
263 lines
7.6 KiB
JavaScript
263 lines
7.6 KiB
JavaScript
/* 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 = {
|
|
INACCURATE_LOCATION: "inaccurate_location",
|
|
MANAGE: "manage",
|
|
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() {
|
|
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,
|
|
showFeedbackMenu: true,
|
|
};
|
|
if (!suggestion.is_top_pick) {
|
|
let suggestedIndex = lazy.UrlbarPrefs.get("yelpSuggestNonPriorityIndex");
|
|
if (suggestedIndex !== null) {
|
|
resultProperties.isSuggestedIndexRelativeToGroup = true;
|
|
resultProperties.suggestedIndex = suggestedIndex;
|
|
}
|
|
}
|
|
|
|
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],
|
|
bottomTextL10n: { id: "firefox-suggest-yelp-bottom-text" },
|
|
})
|
|
),
|
|
resultProperties
|
|
);
|
|
}
|
|
|
|
getResultCommands() {
|
|
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.MANAGE,
|
|
l10n: {
|
|
id: "urlbar-result-menu-manage-firefox-suggest",
|
|
},
|
|
}
|
|
);
|
|
|
|
return commands;
|
|
}
|
|
|
|
handleCommand(view, result, selType, searchString) {
|
|
switch (selType) {
|
|
case RESULT_MENU_COMMAND.MANAGE:
|
|
// "manage" 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() {
|
|
// Use the pref value if it has a user value (which means the user clicked
|
|
// "Show less frequently") or if there's no Nimbus value. Otherwise use the
|
|
// Nimbus value. This lets us override the pref's default value using Nimbus
|
|
// if necessary.
|
|
let hasUserValue = Services.prefs.prefHasUserValue(
|
|
"browser.urlbar.yelp.minKeywordLength"
|
|
);
|
|
let nimbusValue = lazy.UrlbarPrefs.get("yelpMinKeywordLength");
|
|
let minLength =
|
|
hasUserValue || nimbusValue === null
|
|
? lazy.UrlbarPrefs.get("yelp.minKeywordLength")
|
|
: nimbusValue;
|
|
return Math.max(minLength, 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;
|
|
}
|