tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FlightStatusSuggestions.sys.mjs (7567B)


      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
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { RealtimeSuggestProvider } from "moz-src:///browser/components/urlbar/private/RealtimeSuggestProvider.sys.mjs";
      6 
      7 /**
      8 * A feature that supports flight status suggestions.
      9 */
     10 export class FlightStatusSuggestions extends RealtimeSuggestProvider {
     11  get realtimeType() {
     12    return "flightStatus";
     13  }
     14 
     15  get isSponsored() {
     16    return false;
     17  }
     18 
     19  get merinoProvider() {
     20    return "flightaware";
     21  }
     22 
     23  get baseTelemetryType() {
     24    return "flights";
     25  }
     26 
     27  getViewTemplateForDescriptionTop(_item, index) {
     28    return [
     29      {
     30        name: `departure_time_${index}`,
     31        tag: "span",
     32        classList: ["urlbarView-flightStatus-time"],
     33      },
     34      {
     35        name: `origin_airport_${index}`,
     36        tag: "span",
     37        classList: ["urlbarView-flightStatus-airport"],
     38      },
     39      {
     40        tag: "span",
     41        classList: ["urlbarView-realtime-description-separator-dash"],
     42      },
     43      {
     44        name: `arrival_time_${index}`,
     45        tag: "span",
     46        classList: ["urlbarView-flightStatus-time"],
     47      },
     48      {
     49        name: `destination_airport_${index}`,
     50        tag: "span",
     51        classList: ["urlbarView-flightStatus-airport"],
     52      },
     53    ];
     54  }
     55 
     56  getViewTemplateForDescriptionBottom(_item, index) {
     57    return [
     58      {
     59        name: `departure_date_${index}`,
     60        tag: "span",
     61        classList: ["urlbarView-flightStatus-departure-date"],
     62      },
     63      {
     64        tag: "span",
     65        classList: ["urlbarView-realtime-description-separator-dot"],
     66      },
     67      {
     68        name: `flight_number_${index}`,
     69        tag: "span",
     70        classList: ["urlbarView-flightStatus-flight-number"],
     71      },
     72      {
     73        tag: "span",
     74        classList: ["urlbarView-realtime-description-separator-dot"],
     75      },
     76      {
     77        name: `status_${index}`,
     78        tag: "span",
     79        classList: ["urlbarView-flightStatus-status"],
     80      },
     81      {
     82        tag: "span",
     83        classList: ["urlbarView-realtime-description-separator-dot"],
     84      },
     85      {
     86        name: `time_left_${index}`,
     87        tag: "span",
     88        classList: ["urlbarView-flightStatus-time-left"],
     89      },
     90    ];
     91  }
     92 
     93  getViewUpdateForPayloadItem(item, index) {
     94    let status;
     95    switch (item.status) {
     96      case "Scheduled": {
     97        status = "ontime";
     98        break;
     99      }
    100      case "En Route": {
    101        status = "inflight";
    102        break;
    103      }
    104      case "Arrived": {
    105        status = "arrived";
    106        break;
    107      }
    108      case "Cancelled": {
    109        status = "cancelled";
    110        break;
    111      }
    112      case "Delayed": {
    113        status = "delayed";
    114        break;
    115      }
    116    }
    117 
    118    let departureTime;
    119    let departureTimeZone;
    120    let arrivalTime;
    121    let arrivalTimeZone;
    122    if (status == "delayed" || !item.delayed) {
    123      departureTime = new Date(item.departure.scheduled_time);
    124      departureTimeZone = getTimeZone(item.departure.scheduled_time);
    125      arrivalTime = new Date(item.arrival.scheduled_time);
    126      arrivalTimeZone = getTimeZone(item.arrival.scheduled_time);
    127    } else {
    128      departureTime = new Date(item.departure.estimated_time);
    129      departureTimeZone = getTimeZone(item.departure.estimated_time);
    130      arrivalTime = new Date(item.arrival.estimated_time);
    131      arrivalTimeZone = getTimeZone(item.arrival.estimated_time);
    132    }
    133 
    134    let statusL10nId = `urlbar-result-flight-status-status-${status}`;
    135    let statusL10nArgs;
    136    if (status == "delayed") {
    137      statusL10nArgs = {
    138        departureEstimatedTime: new Intl.DateTimeFormat(undefined, {
    139          hour: "numeric",
    140          minute: "numeric",
    141          timeZone: getTimeZone(item.departure.estimated_time),
    142        }).format(new Date(item.departure.estimated_time)),
    143      };
    144    }
    145 
    146    let foregroundImage;
    147    let backgroundImage;
    148    if (status == "inflight") {
    149      let backgroundImageId =
    150        item.progress_percent == 100
    151          ? 4
    152          : Math.floor(item.progress_percent / 20);
    153      backgroundImage = {
    154        style: {
    155          "--airline-color": item.airline.color,
    156        },
    157        attributes: {
    158          backgroundImageId,
    159          hasForegroundImage: !!item.airline.icon,
    160        },
    161      };
    162      foregroundImage = {
    163        attributes: {
    164          src: item.airline.icon,
    165        },
    166      };
    167    } else {
    168      foregroundImage = {
    169        attributes: {
    170          src:
    171            item.airline.icon ??
    172            "chrome://browser/skin/urlbar/flight-airline.svg",
    173          fallback: !item.airline.icon,
    174        },
    175      };
    176    }
    177 
    178    let timeLeft;
    179    if (typeof item.time_left_minutes == "number") {
    180      let hours = Math.floor(item.time_left_minutes / 60);
    181      let minutes = item.time_left_minutes % 60;
    182      // TODO Bug 1997547: TypeScript support for `Intl.DurationFormat`
    183      // @ts-ignore
    184      timeLeft = new Intl.DurationFormat(undefined, {
    185        style: "short",
    186      }).format({
    187        // If hours is zero, pass `undefined` to not show it at all.
    188        hours: hours || undefined,
    189        minutes,
    190      });
    191    }
    192 
    193    return {
    194      [`item_${index}`]: {
    195        attributes: {
    196          status,
    197        },
    198      },
    199      [`image_container_${index}`]: backgroundImage,
    200      [`image_${index}`]: foregroundImage,
    201      [`departure_time_${index}`]: {
    202        textContent: new Intl.DateTimeFormat(undefined, {
    203          hour: "numeric",
    204          minute: "numeric",
    205          timeZone: departureTimeZone,
    206        }).format(departureTime),
    207      },
    208      [`departure_date_${index}`]: {
    209        textContent: new Intl.DateTimeFormat(undefined, {
    210          month: "short",
    211          day: "numeric",
    212          weekday: "short",
    213          timeZone: departureTimeZone,
    214        }).format(departureTime),
    215      },
    216      [`arrival_time_${index}`]: {
    217        textContent: new Intl.DateTimeFormat(undefined, {
    218          hour: "numeric",
    219          minute: "numeric",
    220          timeZone: arrivalTimeZone,
    221        }).format(arrivalTime),
    222      },
    223      [`origin_airport_${index}`]: {
    224        l10n: {
    225          id: "urlbar-result-flight-status-airport",
    226          args: {
    227            city: item.origin.city,
    228            code: item.origin.code,
    229          },
    230        },
    231      },
    232      [`destination_airport_${index}`]: {
    233        l10n: {
    234          id: "urlbar-result-flight-status-airport",
    235          args: {
    236            city: item.destination.city,
    237            code: item.destination.code,
    238          },
    239        },
    240      },
    241      [`flight_number_${index}`]: item.airline.name
    242        ? {
    243            l10n: {
    244              id: "urlbar-result-flight-status-flight-number-with-airline",
    245              args: {
    246                flightNumber: item.flight_number,
    247                airlineName: item.airline.name,
    248              },
    249            },
    250          }
    251        : {
    252            textContent: item.flight_number,
    253          },
    254      [`status_${index}`]: {
    255        l10n: {
    256          id: statusL10nId,
    257          args: statusL10nArgs,
    258        },
    259      },
    260      [`time_left_${index}`]: timeLeft
    261        ? {
    262            l10n: {
    263              id: "urlbar-result-flight-status-time-left",
    264              args: {
    265                timeLeft,
    266              },
    267            },
    268          }
    269        : null,
    270    };
    271  }
    272 }
    273 
    274 function getTimeZone(isoTimeString) {
    275  let match = isoTimeString.match(/([+-]\d{2}:?\d{2}|Z)$/);
    276  if (!match) {
    277    return undefined;
    278  }
    279 
    280  let timeZone = match[1];
    281  return timeZone == "Z" ? "UTC" : timeZone;
    282 }