Source code

Revision control

Copy as Markdown

Other Tools

/* 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;
}