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 }