link-menu-options.mjs (15305B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { 6 actionCreators as ac, 7 actionTypes as at, 8 } from "../../common/Actions.mjs"; 9 10 const _OpenInPrivateWindow = site => ({ 11 id: "newtab-menu-open-new-private-window", 12 icon: "new-window-private", 13 action: ac.OnlyToMain({ 14 type: at.OPEN_PRIVATE_WINDOW, 15 data: { 16 url: site.url, 17 referrer: site.referrer, 18 event_source: "CONTEXT_MENU", 19 }, 20 }), 21 userEvent: "OPEN_PRIVATE_WINDOW", 22 }); 23 24 /** 25 * List of functions that return items that can be included as menu options in a 26 * LinkMenu. All functions take the site as the first parameter, and optionally 27 * the index of the site. 28 */ 29 export const LinkMenuOptions = { 30 Separator: () => ({ type: "separator" }), 31 EmptyItem: () => ({ type: "empty" }), 32 ShowPrivacyInfo: () => ({ 33 id: "newtab-menu-show-privacy-info", 34 icon: "info", 35 action: { 36 type: at.SHOW_PRIVACY_INFO, 37 }, 38 userEvent: "SHOW_PRIVACY_INFO", 39 }), 40 AboutSponsored: site => ({ 41 id: "newtab-menu-show-privacy-info", 42 icon: "info", 43 action: ac.AlsoToMain({ 44 type: at.ABOUT_SPONSORED_TOP_SITES, 45 data: { 46 advertiser_name: (site.label || site.hostname).toLocaleLowerCase(), 47 position: site.sponsored_position, 48 tile_id: site.sponsored_tile_id, 49 block_key: site.block_key, 50 }, 51 }), 52 userEvent: "TOPSITE_SPONSOR_INFO", 53 }), 54 RemoveBookmark: site => ({ 55 id: "newtab-menu-remove-bookmark", 56 icon: "bookmark-added", 57 action: ac.AlsoToMain({ 58 type: at.DELETE_BOOKMARK_BY_ID, 59 data: site.bookmarkGuid, 60 }), 61 userEvent: "BOOKMARK_DELETE", 62 }), 63 AddBookmark: site => ({ 64 id: "newtab-menu-bookmark", 65 icon: "bookmark-hollow", 66 action: ac.AlsoToMain({ 67 type: at.BOOKMARK_URL, 68 data: { url: site.url, title: site.title, type: site.type }, 69 }), 70 userEvent: "BOOKMARK_ADD", 71 }), 72 OpenInNewWindow: site => ({ 73 id: "newtab-menu-open-new-window", 74 icon: "new-window", 75 action: ac.AlsoToMain({ 76 type: at.OPEN_NEW_WINDOW, 77 data: { 78 card_type: site.card_type, 79 referrer: site.referrer, 80 typedBonus: site.typedBonus, 81 url: site.url, 82 is_sponsored: !!site.sponsored_tile_id, 83 event_source: "CONTEXT_MENU", 84 topic: site.topic, 85 firstVisibleTimestamp: site.firstVisibleTimestamp, 86 tile_id: site.tile_id, 87 recommendation_id: site.recommendation_id, 88 scheduled_corpus_item_id: site.scheduled_corpus_item_id, 89 corpus_item_id: site.corpus_item_id, 90 received_rank: site.received_rank, 91 recommended_at: site.recommended_at, 92 format: site.format, 93 ...(site.flight_id ? { flight_id: site.flight_id } : {}), 94 is_pocket_card: site.type === "CardGrid", 95 ...(site.section 96 ? { 97 section: site.section, 98 section_position: site.section_position, 99 is_section_followed: site.is_section_followed, 100 } 101 : {}), 102 }, 103 }), 104 userEvent: "OPEN_NEW_WINDOW", 105 }), 106 107 // This blocks the url for regular stories, 108 // but also sends a message to DiscoveryStream with flight_id. 109 // If DiscoveryStream sees this message for a flight_id 110 // it also blocks it on the flight_id. 111 BlockUrl: (site, index, eventSource) => { 112 return LinkMenuOptions.BlockUrls([site], index, eventSource); 113 }, 114 // Same as BlockUrl, except can work on an array of sites. 115 BlockUrls: (tiles, pos, eventSource) => ({ 116 id: "newtab-menu-dismiss", 117 icon: "dismiss", 118 action: ac.AlsoToMain({ 119 type: at.BLOCK_URL, 120 source: eventSource, 121 data: tiles.map(site => ({ 122 url: site.original_url || site.open_url || site.url, 123 // pocket_id is only for pocket stories being in highlights, and then dismissed. 124 pocket_id: site.pocket_id, 125 tile_id: site.tile_id, 126 ...(site.block_key ? { block_key: site.block_key } : {}), 127 recommendation_id: site.recommendation_id, 128 scheduled_corpus_item_id: site.scheduled_corpus_item_id, 129 corpus_item_id: site.corpus_item_id, 130 received_rank: site.received_rank, 131 recommended_at: site.recommended_at, 132 // used by PlacesFeed and TopSitesFeed for sponsored top sites blocking. 133 isSponsoredTopSite: site.sponsored_position, 134 type: site.type, 135 card_type: site.card_type, 136 ...(site.shim && site.shim.delete ? { shim: site.shim.delete } : {}), 137 ...(site.flight_id ? { flight_id: site.flight_id } : {}), 138 // If not sponsored, hostname could be anything (Cat3 Data!). 139 // So only put in advertiser_name for sponsored topsites. 140 ...(site.sponsored_position 141 ? { 142 advertiser_name: ( 143 site.label || site.hostname 144 )?.toLocaleLowerCase(), 145 } 146 : {}), 147 position: pos, 148 ...(site.sponsored_tile_id ? { tile_id: site.sponsored_tile_id } : {}), 149 is_pocket_card: site.type === "CardGrid", 150 ...(site.format ? { format: site.format } : {}), 151 ...(site.section 152 ? { 153 section: site.section, 154 section_position: site.section_position, 155 is_section_followed: site.is_section_followed, 156 } 157 : {}), 158 })), 159 }), 160 impression: ac.ImpressionStats({ 161 source: eventSource, 162 block: 0, 163 tiles: tiles.map((site, index) => ({ 164 id: site.guid, 165 pos: pos + index, 166 ...(site.shim && site.shim.delete ? { shim: site.shim.delete } : {}), 167 })), 168 }), 169 userEvent: "BLOCK", 170 }), 171 172 // This is the "Dismiss" action for leaderboard/billboard ads. 173 BlockAdUrl: (site, pos, eventSource) => ({ 174 id: "newtab-menu-dismiss", 175 icon: "dismiss", 176 action: ac.AlsoToMain({ 177 type: at.BLOCK_URL, 178 data: [site], 179 }), 180 impression: ac.ImpressionStats({ 181 source: eventSource, 182 block: 0, 183 tiles: [ 184 { 185 id: site.guid, 186 pos, 187 ...(site.shim && site.shim.save ? { shim: site.shim.save } : {}), 188 }, 189 ], 190 }), 191 userEvent: "BLOCK", 192 }), 193 194 // This is an option for web extentions which will result in remove items from 195 // memory and notify the web extenion, rather than using the built-in block list. 196 WebExtDismiss: (site, index, eventSource) => ({ 197 id: "menu_action_webext_dismiss", 198 string_id: "newtab-menu-dismiss", 199 icon: "dismiss", 200 action: ac.WebExtEvent(at.WEBEXT_DISMISS, { 201 source: eventSource, 202 url: site.url, 203 action_position: index, 204 }), 205 }), 206 DeleteUrl: (site, index, eventSource, isEnabled, siteInfo) => ({ 207 id: "newtab-menu-delete-history", 208 icon: "delete", 209 action: { 210 type: at.DIALOG_OPEN, 211 data: { 212 onConfirm: [ 213 ac.AlsoToMain({ 214 type: at.DELETE_HISTORY_URL, 215 data: { 216 url: site.url, 217 pocket_id: site.pocket_id, 218 forceBlock: site.bookmarkGuid, 219 }, 220 }), 221 ac.UserEvent( 222 Object.assign( 223 { event: "DELETE", source: eventSource, action_position: index }, 224 siteInfo 225 ) 226 ), 227 // Also broadcast that this url has been deleted so that 228 // the confirmation dialog knows it needs to disappear now. 229 ac.AlsoToMain({ 230 type: at.DIALOG_CLOSE, 231 }), 232 ], 233 eventSource, 234 body_string_id: [ 235 "newtab-confirm-delete-history-p1", 236 "newtab-confirm-delete-history-p2", 237 ], 238 confirm_button_string_id: "newtab-topsites-delete-history-button", 239 cancel_button_string_id: "newtab-topsites-cancel-button", 240 icon: "modal-delete", 241 }, 242 }, 243 userEvent: "DIALOG_OPEN", 244 }), 245 ShowFile: site => ({ 246 id: "newtab-menu-show-file", 247 icon: "search", 248 action: ac.OnlyToMain({ 249 type: at.SHOW_DOWNLOAD_FILE, 250 data: { url: site.url }, 251 }), 252 }), 253 OpenFile: site => ({ 254 id: "newtab-menu-open-file", 255 icon: "open-file", 256 action: ac.OnlyToMain({ 257 type: at.OPEN_DOWNLOAD_FILE, 258 data: { url: site.url }, 259 }), 260 }), 261 CopyDownloadLink: site => ({ 262 id: "newtab-menu-copy-download-link", 263 icon: "copy", 264 action: ac.OnlyToMain({ 265 type: at.COPY_DOWNLOAD_LINK, 266 data: { url: site.url }, 267 }), 268 }), 269 GoToDownloadPage: site => ({ 270 id: "newtab-menu-go-to-download-page", 271 icon: "download", 272 action: ac.OnlyToMain({ 273 type: at.OPEN_LINK, 274 data: { url: site.referrer }, 275 }), 276 disabled: !site.referrer, 277 }), 278 RemoveDownload: site => ({ 279 id: "newtab-menu-remove-download", 280 icon: "delete", 281 action: ac.OnlyToMain({ 282 type: at.REMOVE_DOWNLOAD_FILE, 283 data: { url: site.url }, 284 }), 285 }), 286 PinTopSite: (site, index) => ({ 287 id: "newtab-menu-pin", 288 icon: "pin", 289 action: ac.AlsoToMain({ 290 type: at.TOP_SITES_PIN, 291 data: { 292 site, 293 index, 294 }, 295 }), 296 userEvent: "PIN", 297 }), 298 UnpinTopSite: site => ({ 299 id: "newtab-menu-unpin", 300 icon: "unpin", 301 action: ac.AlsoToMain({ 302 type: at.TOP_SITES_UNPIN, 303 data: { site: { url: site.url } }, 304 }), 305 userEvent: "UNPIN", 306 }), 307 EditTopSite: (site, index) => ({ 308 id: "newtab-menu-edit-topsites", 309 icon: "edit", 310 action: { 311 type: at.TOP_SITES_EDIT, 312 data: { index }, 313 }, 314 }), 315 CheckBookmark: site => 316 site.bookmarkGuid 317 ? LinkMenuOptions.RemoveBookmark(site) 318 : LinkMenuOptions.AddBookmark(site), 319 CheckPinTopSite: (site, index) => 320 site.isPinned 321 ? LinkMenuOptions.UnpinTopSite(site) 322 : LinkMenuOptions.PinTopSite(site, index), 323 OpenInPrivateWindow: (site, index, eventSource, isEnabled) => 324 isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem(), 325 ChangeWeatherLocation: () => ({ 326 id: "newtab-weather-menu-change-location", 327 action: ac.BroadcastToContent({ 328 type: at.WEATHER_SEARCH_ACTIVE, 329 data: true, 330 }), 331 }), 332 DetectLocation: () => ({ 333 id: "newtab-weather-menu-detect-my-location", 334 action: ac.AlsoToMain({ 335 type: at.WEATHER_USER_OPT_IN_LOCATION, 336 }), 337 userEvent: "WEATHER_DETECT_LOCATION", 338 }), 339 ChangeWeatherDisplaySimple: () => ({ 340 id: "newtab-weather-menu-change-weather-display-simple", 341 action: ac.OnlyToMain({ 342 type: at.SET_PREF, 343 data: { 344 name: "weather.display", 345 value: "simple", 346 }, 347 }), 348 }), 349 ChangeWeatherDisplayDetailed: () => ({ 350 id: "newtab-weather-menu-change-weather-display-detailed", 351 action: ac.OnlyToMain({ 352 type: at.SET_PREF, 353 data: { 354 name: "weather.display", 355 value: "detailed", 356 }, 357 }), 358 }), 359 ChangeTempUnitFahrenheit: () => ({ 360 id: "newtab-weather-menu-change-temperature-units-fahrenheit", 361 action: ac.OnlyToMain({ 362 type: at.SET_PREF, 363 data: { 364 name: "weather.temperatureUnits", 365 value: "f", 366 }, 367 }), 368 }), 369 ChangeTempUnitCelsius: () => ({ 370 id: "newtab-weather-menu-change-temperature-units-celsius", 371 action: ac.OnlyToMain({ 372 type: at.SET_PREF, 373 data: { 374 name: "weather.temperatureUnits", 375 value: "c", 376 }, 377 }), 378 }), 379 HideWeather: () => ({ 380 id: "newtab-weather-menu-hide-weather", 381 action: ac.OnlyToMain({ 382 type: at.SET_PREF, 383 data: { 384 name: "showWeather", 385 value: false, 386 }, 387 }), 388 }), 389 OpenLearnMoreURL: site => ({ 390 id: "newtab-weather-menu-learn-more", 391 action: ac.OnlyToMain({ 392 type: at.OPEN_LINK, 393 data: { url: site.url }, 394 }), 395 }), 396 SectionBlock: ({ 397 sectionPersonalization, 398 sectionKey, 399 sectionPosition, 400 title, 401 }) => ({ 402 id: "newtab-menu-section-block", 403 icon: "delete", 404 action: { 405 // Open the confirmation dialog to block a section. 406 type: at.DIALOG_OPEN, 407 data: { 408 onConfirm: [ 409 // Once the user confirmed their intention to block this section, 410 // update their preferences. 411 ac.AlsoToMain({ 412 type: at.SECTION_PERSONALIZATION_SET, 413 data: { 414 ...sectionPersonalization, 415 [sectionKey]: { 416 isBlocked: true, 417 isFollowed: false, 418 }, 419 }, 420 }), 421 // Telemetry 422 ac.OnlyToMain({ 423 type: at.BLOCK_SECTION, 424 data: { 425 section: sectionKey, 426 section_position: sectionPosition, 427 event_source: "CONTEXT_MENU", 428 }, 429 }), 430 // Also broadcast that this section has been blocked so that 431 // the confirmation dialog knows it needs to disappear now. 432 ac.AlsoToMain({ 433 type: at.DIALOG_CLOSE, 434 }), 435 ], 436 // Pass Fluent strings to ConfirmDialog component for the copy 437 // of the prompt to block sections. 438 body_string_id: [ 439 "newtab-section-confirm-block-topic-p1", 440 "newtab-section-confirm-block-topic-p2", 441 ], 442 confirm_button_string_id: "newtab-section-block-topic-button", 443 confirm_button_string_args: { topic: title }, 444 cancel_button_string_id: "newtab-section-cancel-button", 445 }, 446 }, 447 userEvent: "DIALOG_OPEN", 448 }), 449 SectionUnfollow: ({ 450 sectionPersonalization, 451 sectionKey, 452 sectionPosition, 453 }) => ({ 454 id: "newtab-menu-section-unfollow", 455 action: ac.AlsoToMain({ 456 type: at.SECTION_PERSONALIZATION_SET, 457 data: (({ [sectionKey]: _sectionKey, ...remaining }) => remaining)( 458 sectionPersonalization 459 ), 460 }), 461 impression: ac.OnlyToMain({ 462 type: at.UNFOLLOW_SECTION, 463 data: { 464 section: sectionKey, 465 section_position: sectionPosition, 466 event_source: "CONTEXT_MENU", 467 }, 468 }), 469 }), 470 ManageSponsoredContent: () => ({ 471 id: "newtab-menu-manage-sponsored-content", 472 action: ac.OnlyToMain({ type: at.SETTINGS_OPEN }), 473 userEvent: "OPEN_NEWTAB_PREFS", 474 }), 475 OurSponsorsAndYourPrivacy: () => ({ 476 id: "newtab-menu-our-sponsors-and-your-privacy", 477 action: ac.OnlyToMain({ 478 type: at.OPEN_LINK, 479 data: { 480 url: "https://support.mozilla.org/kb/pocket-sponsored-stories-new-tabs", 481 }, 482 }), 483 userEvent: "CLICK_PRIVACY_INFO", 484 }), 485 ReportAd: site => { 486 return { 487 id: "newtab-menu-report-this-ad", 488 action: ac.AlsoToMain({ 489 type: at.REPORT_AD_OPEN, 490 data: { 491 card_type: site.card_type, 492 position: site.position, 493 reporting_url: site.shim.report, 494 url: site.url, 495 }, 496 }), 497 }; 498 }, 499 500 ReportContent: site => { 501 return { 502 id: "newtab-menu-report", 503 action: ac.AlsoToMain({ 504 type: at.REPORT_CONTENT_OPEN, 505 data: { 506 card_type: site.card_type, 507 corpus_item_id: site.corpus_item_id, 508 scheduled_corpus_item_id: site.scheduled_corpus_item_id, 509 section_position: site.section_position, 510 section: site.section, 511 title: site.title, 512 topic: site.topic, 513 url: site.url, 514 }, 515 }), 516 }; 517 }, 518 };