/* 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 { RealtimeSuggestProvider } from "moz-src:///browser/components/urlbar/private/RealtimeSuggestProvider.sys.mjs"; /** * A feature that supports flight status suggestions. */ export class FlightStatusSuggestions extends RealtimeSuggestProvider { get realtimeType() { return "flightStatus"; } get isSponsored() { return false; } get merinoProvider() { return "flightaware"; } get baseTelemetryType() { return "flights"; } getViewTemplateForDescriptionTop(_item, index) { return [ { name: `departure_time_${index}`, tag: "span", classList: ["urlbarView-flightStatus-time"], }, { name: `origin_airport_${index}`, tag: "span", classList: ["urlbarView-flightStatus-airport"], }, { tag: "span", classList: ["urlbarView-realtime-description-separator-dash"], }, { name: `arrival_time_${index}`, tag: "span", classList: ["urlbarView-flightStatus-time"], }, { name: `destination_airport_${index}`, tag: "span", classList: ["urlbarView-flightStatus-airport"], }, ]; } getViewTemplateForDescriptionBottom(_item, index) { return [ { name: `departure_date_${index}`, tag: "span", classList: ["urlbarView-flightStatus-departure-date"], }, { tag: "span", classList: ["urlbarView-realtime-description-separator-dot"], }, { name: `flight_number_${index}`, tag: "span", classList: ["urlbarView-flightStatus-flight-number"], }, { tag: "span", classList: ["urlbarView-realtime-description-separator-dot"], }, { name: `status_${index}`, tag: "span", classList: ["urlbarView-flightStatus-status"], }, { tag: "span", classList: ["urlbarView-realtime-description-separator-dot"], }, { name: `time_left_${index}`, tag: "span", classList: ["urlbarView-flightStatus-time-left"], }, ]; } getViewUpdateForPayloadItem(item, index) { let status; switch (item.status) { case "Scheduled": { status = "ontime"; break; } case "En Route": { status = "inflight"; break; } case "Arrived": { status = "arrived"; break; } case "Cancelled": { status = "cancelled"; break; } case "Delayed": { status = "delayed"; break; } } let departureTime; let departureTimeZone; let arrivalTime; let arrivalTimeZone; if (status == "delayed" || !item.delayed) { departureTime = new Date(item.departure.scheduled_time); departureTimeZone = getTimeZone(item.departure.scheduled_time); arrivalTime = new Date(item.arrival.scheduled_time); arrivalTimeZone = getTimeZone(item.arrival.scheduled_time); } else { departureTime = new Date(item.departure.estimated_time); departureTimeZone = getTimeZone(item.departure.estimated_time); arrivalTime = new Date(item.arrival.estimated_time); arrivalTimeZone = getTimeZone(item.arrival.estimated_time); } let statusL10nId = `urlbar-result-flight-status-status-${status}`; let statusL10nArgs; if (status == "delayed") { statusL10nArgs = { departureEstimatedTime: new Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric", timeZone: getTimeZone(item.departure.estimated_time), }).format(new Date(item.departure.estimated_time)), }; } let foregroundImage; let backgroundImage; if (status == "inflight") { let backgroundImageId = item.progress_percent == 100 ? 4 : Math.floor(item.progress_percent / 20); backgroundImage = { style: { "--airline-color": item.airline.color, }, attributes: { backgroundImageId, hasForegroundImage: !!item.airline.icon, }, }; foregroundImage = { attributes: { src: item.airline.icon, }, }; } else { foregroundImage = { attributes: { src: item.airline.icon ?? "chrome://browser/skin/urlbar/flight-airline.svg", fallback: !item.airline.icon, }, }; } let timeLeft; if (typeof item.time_left_minutes == "number") { let hours = Math.floor(item.time_left_minutes / 60); let minutes = item.time_left_minutes % 60; // TODO Bug 1997547: TypeScript support for `Intl.DurationFormat` // @ts-ignore timeLeft = new Intl.DurationFormat(undefined, { style: "short", }).format({ // If hours is zero, pass `undefined` to not show it at all. hours: hours || undefined, minutes, }); } return { [`item_${index}`]: { attributes: { status, }, }, [`image_container_${index}`]: backgroundImage, [`image_${index}`]: foregroundImage, [`departure_time_${index}`]: { textContent: new Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric", timeZone: departureTimeZone, }).format(departureTime), }, [`departure_date_${index}`]: { textContent: new Intl.DateTimeFormat(undefined, { month: "short", day: "numeric", weekday: "short", timeZone: departureTimeZone, }).format(departureTime), }, [`arrival_time_${index}`]: { textContent: new Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric", timeZone: arrivalTimeZone, }).format(arrivalTime), }, [`origin_airport_${index}`]: { l10n: { id: "urlbar-result-flight-status-airport", args: { city: item.origin.city, code: item.origin.code, }, }, }, [`destination_airport_${index}`]: { l10n: { id: "urlbar-result-flight-status-airport", args: { city: item.destination.city, code: item.destination.code, }, }, }, [`flight_number_${index}`]: item.airline.name ? { l10n: { id: "urlbar-result-flight-status-flight-number-with-airline", args: { flightNumber: item.flight_number, airlineName: item.airline.name, }, }, } : { textContent: item.flight_number, }, [`status_${index}`]: { l10n: { id: statusL10nId, args: statusL10nArgs, }, }, [`time_left_${index}`]: timeLeft ? { l10n: { id: "urlbar-result-flight-status-time-left", args: { timeLeft, }, }, } : null, }; } } function getTimeZone(isoTimeString) { let match = isoTimeString.match(/([+-]\d{2}:?\d{2}|Z)$/); if (!match) { return undefined; } let timeZone = match[1]; return timeZone == "Z" ? "UTC" : timeZone; }