/* 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 { useDispatch, useSelector } from "react-redux";
import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
import {
getNotificationIdsForUrl,
originFromUrl,
} from "content-src/lib/web-notification-match.mjs";
import { proxiedIconUrl } from "content-src/lib/web-notification-icon.mjs";
import React from "react";
// Origins whose notification icon just repeats the site's own shortcut icon, so
// listing it is visual noise. Hand-curated; grown as needed.
const ICON_SUPPRESS_ORIGINS = new Set(["https://apnews.com"]);
// Biggest units first, so the loop returns the coarsest one that fits.
// Anything under a minute falls through to the "just now" string.
const RELATIVE_TIME_UNITS = [
["year", 365 * 24 * 60 * 60 * 1000],
["month", 30 * 24 * 60 * 60 * 1000],
["week", 7 * 24 * 60 * 60 * 1000],
["day", 24 * 60 * 60 * 1000],
["hour", 60 * 60 * 1000],
["minute", 60 * 1000],
];
/**
* Picks the largest relative-time unit that fits ("2 hours ago", "5 days ago").
* Returns null when the delta is under a minute, so the caller can show
* "just now" instead.
*
* @param {number} timestamp ms epoch the notification was posted.
* @param {string} [locale] BCP-47 locale; falls back to the runtime default.
* @param {number} now ms epoch to measure against.
* @returns {?string}
*/
function formatRelativeTime(timestamp, locale, now) {
const delta = timestamp - now;
const abs = Math.abs(delta);
for (const [unit, ms] of RELATIVE_TIME_UNITS) {
if (abs >= ms) {
return new Intl.RelativeTimeFormat(locale || undefined, {
numeric: "auto",
}).format(Math.round(delta / ms), unit);
}
}
return null;
}
function NotificationTime({ timestamp, locale, now }) {
if (!timestamp) {
return null;
}
const relative = formatRelativeTime(timestamp, locale, now);
const dateTime = new Date(timestamp).toISOString();
// A null relative string means it's under a minute, so show "just now".
if (relative === null) {
return (
);
}
return (
);
}
/**
* A notification's icon, proxied. Renders nothing when the icon cannot be
* proxied or the proxy fails to serve it — there is deliberately no fallback to
* the origin URL, which is the load the proxy exists to avoid.
*/
function NotificationIcon({ notification }) {
const [failed, setFailed] = React.useState(false);
if (ICON_SUPPRESS_ORIGINS.has(notification.origin)) {
return null;
}
const src = proxiedIconUrl(notification.icon);
if (!src || failed) {
return null;
}
return (
setFailed(true)}
/>
);
}
function NotificationList({
notifications,
locale,
now,
onActivate,
onDismiss,
}) {
return (