diff --git a/web/.prettierignore b/web/.prettierignore
index d0097d35..d50a46ce 100644
--- a/web/.prettierignore
+++ b/web/.prettierignore
@@ -1,2 +1,2 @@
build/
-public/static/langs/
\ No newline at end of file
+public/static/langs/
diff --git a/web/package.json b/web/package.json
index 1ca2da7d..10c198dc 100644
--- a/web/package.json
+++ b/web/package.json
@@ -43,5 +43,8 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "prettier": {
+ "printWidth": 160
}
}
diff --git a/web/public/config.js b/web/public/config.js
index a748dd84..2f46d65c 100644
--- a/web/public/config.js
+++ b/web/public/config.js
@@ -15,15 +15,5 @@ var config = {
enable_emails: true,
enable_calls: true,
billing_contact: "",
- disallowed_topics: [
- "docs",
- "static",
- "file",
- "app",
- "account",
- "settings",
- "signup",
- "login",
- "v1",
- ],
+ disallowed_topics: ["docs", "static", "file", "app", "account", "settings", "signup", "login", "v1"],
};
diff --git a/web/public/index.html b/web/public/index.html
index 31dd280e..e59a62e3 100644
--- a/web/public/index.html
+++ b/web/public/index.html
@@ -5,10 +5,7 @@
ntfy web
-
+
@@ -18,11 +15,7 @@
-
+
@@ -40,23 +33,13 @@
-
-
+
+
diff --git a/web/src/app/AccountApi.js b/web/src/app/AccountApi.js
index 3f116114..9af220a0 100644
--- a/web/src/app/AccountApi.js
+++ b/web/src/app/AccountApi.js
@@ -56,9 +56,7 @@ class AccountApi {
async logout() {
const url = accountTokenUrl(config.base_url);
- console.log(
- `[AccountApi] Logging out from ${url} using token ${session.token()}`
- );
+ console.log(`[AccountApi] Logging out from ${url} using token ${session.token()}`);
await fetchOrThrow(url, {
method: "DELETE",
headers: withBearerAuth({}, session.token()),
@@ -227,9 +225,7 @@ class AccountApi {
async upsertReservation(topic, everyone) {
const url = accountReservationUrl(config.base_url);
- console.log(
- `[AccountApi] Upserting user access to topic ${topic}, everyone=${everyone}`
- );
+ console.log(`[AccountApi] Upserting user access to topic ${topic}, everyone=${everyone}`);
await fetchOrThrow(url, {
method: "POST",
headers: withBearerAuth({}, session.token()),
@@ -264,16 +260,12 @@ class AccountApi {
}
async createBillingSubscription(tier, interval) {
- console.log(
- `[AccountApi] Creating billing subscription with ${tier} and interval ${interval}`
- );
+ console.log(`[AccountApi] Creating billing subscription with ${tier} and interval ${interval}`);
return await this.upsertBillingSubscription("POST", tier, interval);
}
async updateBillingSubscription(tier, interval) {
- console.log(
- `[AccountApi] Updating billing subscription with ${tier} and interval ${interval}`
- );
+ console.log(`[AccountApi] Updating billing subscription with ${tier} and interval ${interval}`);
return await this.upsertBillingSubscription("PUT", tier, interval);
}
@@ -324,9 +316,7 @@ class AccountApi {
async addPhoneNumber(phoneNumber, code) {
const url = accountPhoneUrl(config.base_url);
- console.log(
- `[AccountApi] Adding phone number with verification code ${url}`
- );
+ console.log(`[AccountApi] Adding phone number with verification code ${url}`);
await fetchOrThrow(url, {
method: "PUT",
headers: withBearerAuth({}, session.token()),
@@ -371,10 +361,7 @@ class AccountApi {
}
}
if (account.subscriptions) {
- await subscriptionManager.syncFromRemote(
- account.subscriptions,
- account.reservations
- );
+ await subscriptionManager.syncFromRemote(account.subscriptions, account.reservations);
}
return account;
} catch (e) {
diff --git a/web/src/app/Api.js b/web/src/app/Api.js
index 345b0f22..4d7ce822 100644
--- a/web/src/app/Api.js
+++ b/web/src/app/Api.js
@@ -1,12 +1,4 @@
-import {
- fetchLinesIterator,
- maybeWithAuth,
- topicShortUrl,
- topicUrl,
- topicUrlAuth,
- topicUrlJsonPoll,
- topicUrlJsonPollWithSince,
-} from "./utils";
+import { fetchLinesIterator, maybeWithAuth, topicShortUrl, topicUrl, topicUrlAuth, topicUrlJsonPoll, topicUrlJsonPollWithSince } from "./utils";
import userManager from "./UserManager";
import { fetchOrThrow } from "./errors";
@@ -14,9 +6,7 @@ class Api {
async poll(baseUrl, topic, since) {
const user = await userManager.get(baseUrl);
const shortUrl = topicShortUrl(baseUrl, topic);
- const url = since
- ? topicUrlJsonPollWithSince(baseUrl, topic, since)
- : topicUrlJsonPoll(baseUrl, topic);
+ const url = since ? topicUrlJsonPollWithSince(baseUrl, topic, since) : topicUrlJsonPoll(baseUrl, topic);
const messages = [];
const headers = maybeWithAuth({}, user);
console.log(`[Api] Polling ${url}`);
@@ -73,17 +63,11 @@ class Api {
xhr.upload.addEventListener("progress", onProgress);
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status <= 299) {
- console.log(
- `[Api] Publish successful (HTTP ${xhr.status})`,
- xhr.response
- );
+ console.log(`[Api] Publish successful (HTTP ${xhr.status})`, xhr.response);
resolve(xhr.response);
} else if (xhr.readyState === 4) {
// Firefox bug; see description above!
- console.log(
- `[Api] Publish failed (HTTP ${xhr.status})`,
- xhr.responseText
- );
+ console.log(`[Api] Publish failed (HTTP ${xhr.status})`, xhr.responseText);
let errorText;
try {
const error = JSON.parse(xhr.responseText);
diff --git a/web/src/app/Connection.js b/web/src/app/Connection.js
index 5dfc41ba..23416787 100644
--- a/web/src/app/Connection.js
+++ b/web/src/app/Connection.js
@@ -1,10 +1,4 @@
-import {
- basicAuth,
- bearerAuth,
- encodeBase64Url,
- topicShortUrl,
- topicUrlWs,
-} from "./utils";
+import { basicAuth, bearerAuth, encodeBase64Url, topicShortUrl, topicUrlWs } from "./utils";
const retryBackoffSeconds = [5, 10, 20, 30, 60, 120];
@@ -15,16 +9,7 @@ const retryBackoffSeconds = [5, 10, 20, 30, 60, 120];
* Incoming messages and state changes are forwarded via listeners.
*/
class Connection {
- constructor(
- connectionId,
- subscriptionId,
- baseUrl,
- topic,
- user,
- since,
- onNotification,
- onStateChanged
- ) {
+ constructor(connectionId, subscriptionId, baseUrl, topic, user, since, onNotification, onStateChanged) {
this.connectionId = connectionId;
this.subscriptionId = subscriptionId;
this.baseUrl = baseUrl;
@@ -44,78 +29,51 @@ class Connection {
// we don't want to re-trigger the main view re-render potentially hundreds of times.
const wsUrl = this.wsUrl();
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Opening connection to ${wsUrl}`
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Opening connection to ${wsUrl}`);
this.ws = new WebSocket(wsUrl);
this.ws.onopen = (event) => {
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Connection established`,
- event
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Connection established`, event);
this.retryCount = 0;
this.onStateChanged(this.subscriptionId, ConnectionState.Connected);
};
this.ws.onmessage = (event) => {
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Message received from server: ${event.data}`
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Message received from server: ${event.data}`);
try {
const data = JSON.parse(event.data);
if (data.event === "open") {
return;
}
- const relevantAndValid =
- data.event === "message" &&
- "id" in data &&
- "time" in data &&
- "message" in data;
+ const relevantAndValid = data.event === "message" && "id" in data && "time" in data && "message" in data;
if (!relevantAndValid) {
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Unexpected message. Ignoring.`
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Unexpected message. Ignoring.`);
return;
}
this.since = data.id;
this.onNotification(this.subscriptionId, data);
} catch (e) {
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Error handling message: ${e}`
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Error handling message: ${e}`);
}
};
this.ws.onclose = (event) => {
if (event.wasClean) {
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Connection closed cleanly, code=${event.code} reason=${event.reason}`
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
this.ws = null;
} else {
- const retrySeconds =
- retryBackoffSeconds[
- Math.min(this.retryCount, retryBackoffSeconds.length - 1)
- ];
+ const retrySeconds = retryBackoffSeconds[Math.min(this.retryCount, retryBackoffSeconds.length - 1)];
this.retryCount++;
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Connection died, retrying in ${retrySeconds} seconds`
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Connection died, retrying in ${retrySeconds} seconds`);
this.retryTimeout = setTimeout(() => this.start(), retrySeconds * 1000);
this.onStateChanged(this.subscriptionId, ConnectionState.Connecting);
}
};
this.ws.onerror = (event) => {
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Error occurred: ${event}`,
- event
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Error occurred: ${event}`, event);
};
}
close() {
- console.log(
- `[Connection, ${this.shortUrl}, ${this.connectionId}] Closing connection`
- );
+ console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Closing connection`);
const socket = this.ws;
const retryTimeout = this.retryTimeout;
if (socket !== null) {
diff --git a/web/src/app/ConnectionManager.js b/web/src/app/ConnectionManager.js
index ced32d5a..15b94cd7 100644
--- a/web/src/app/ConnectionManager.js
+++ b/web/src/app/ConnectionManager.js
@@ -49,12 +49,8 @@ class ConnectionManager {
return { ...s, user, connectionId };
})
);
- const targetIds = subscriptionsWithUsersAndConnectionId.map(
- (s) => s.connectionId
- );
- const deletedIds = Array.from(this.connections.keys()).filter(
- (id) => !targetIds.includes(id)
- );
+ const targetIds = subscriptionsWithUsersAndConnectionId.map((s) => s.connectionId);
+ const deletedIds = Array.from(this.connections.keys()).filter((id) => !targetIds.includes(id));
// Create and add new connections
subscriptionsWithUsersAndConnectionId.forEach((subscription) => {
@@ -73,15 +69,12 @@ class ConnectionManager {
topic,
user,
since,
- (subscriptionId, notification) =>
- this.notificationReceived(subscriptionId, notification),
+ (subscriptionId, notification) => this.notificationReceived(subscriptionId, notification),
(subscriptionId, state) => this.stateChanged(subscriptionId, state)
);
this.connections.set(connectionId, connection);
console.log(
- `[ConnectionManager] Starting new connection ${connectionId} (subscription ${subscriptionId} with user ${
- user ? user.username : "anonymous"
- })`
+ `[ConnectionManager] Starting new connection ${connectionId} (subscription ${subscriptionId} with user ${user ? user.username : "anonymous"})`
);
connection.start();
}
@@ -101,10 +94,7 @@ class ConnectionManager {
try {
this.stateListener(subscriptionId, state);
} catch (e) {
- console.error(
- `[ConnectionManager] Error updating state of ${subscriptionId} to ${state}`,
- e
- );
+ console.error(`[ConnectionManager] Error updating state of ${subscriptionId} to ${state}`, e);
}
}
}
@@ -114,23 +104,14 @@ class ConnectionManager {
try {
this.messageListener(subscriptionId, notification);
} catch (e) {
- console.error(
- `[ConnectionManager] Error handling notification for ${subscriptionId}`,
- e
- );
+ console.error(`[ConnectionManager] Error handling notification for ${subscriptionId}`, e);
}
}
}
}
const makeConnectionId = async (subscription, user) => {
- return user
- ? hashCode(
- `${subscription.id}|${user.username}|${user.password ?? ""}|${
- user.token ?? ""
- }`
- )
- : hashCode(`${subscription.id}`);
+ return user ? hashCode(`${subscription.id}|${user.username}|${user.password ?? ""}|${user.token ?? ""}`) : hashCode(`${subscription.id}`);
};
const connectionManager = new ConnectionManager();
diff --git a/web/src/app/Notifier.js b/web/src/app/Notifier.js
index e4396d25..2d00dea9 100644
--- a/web/src/app/Notifier.js
+++ b/web/src/app/Notifier.js
@@ -1,11 +1,4 @@
-import {
- formatMessage,
- formatTitleWithDefault,
- openUrl,
- playSound,
- topicDisplayName,
- topicShortUrl,
-} from "./utils";
+import { formatMessage, formatTitleWithDefault, openUrl, playSound, topicDisplayName, topicShortUrl } from "./utils";
import prefs from "./Prefs";
import subscriptionManager from "./SubscriptionManager";
import logo from "../img/ntfy.png";
@@ -30,9 +23,7 @@ class Notifier {
const title = formatTitleWithDefault(notification, displayName);
// Show notification
- console.log(
- `[Notifier, ${shortUrl}] Displaying notification ${notification.id}: ${message}`
- );
+ console.log(`[Notifier, ${shortUrl}] Displaying notification ${notification.id}: ${message}`);
const n = new Notification(title, {
body: message,
icon: logo,
@@ -96,11 +87,7 @@ class Notifier {
* is not supported, see https://developer.mozilla.org/en-US/docs/Web/API/notification
*/
contextSupported() {
- return (
- location.protocol === "https:" ||
- location.hostname.match("^127.") ||
- location.hostname === "localhost"
- );
+ return location.protocol === "https:" || location.hostname.match("^127.") || location.hostname === "localhost";
}
}
diff --git a/web/src/app/Poller.js b/web/src/app/Poller.js
index d2bf6965..402e36b4 100644
--- a/web/src/app/Poller.js
+++ b/web/src/app/Poller.js
@@ -34,18 +34,12 @@ class Poller {
console.log(`[Poller] Polling ${subscription.id}`);
const since = subscription.last;
- const notifications = await api.poll(
- subscription.baseUrl,
- subscription.topic,
- since
- );
+ const notifications = await api.poll(subscription.baseUrl, subscription.topic, since);
if (!notifications || notifications.length === 0) {
console.log(`[Poller] No new notifications found for ${subscription.id}`);
return;
}
- console.log(
- `[Poller] Adding ${notifications.length} notification(s) for ${subscription.id}`
- );
+ console.log(`[Poller] Adding ${notifications.length} notification(s) for ${subscription.id}`);
await subscriptionManager.addNotifications(subscription.id, notifications);
}
diff --git a/web/src/app/Pruner.js b/web/src/app/Pruner.js
index 84853b62..498c1566 100644
--- a/web/src/app/Pruner.js
+++ b/web/src/app/Pruner.js
@@ -20,15 +20,12 @@ class Pruner {
async prune() {
const deleteAfterSeconds = await prefs.deleteAfter();
- const pruneThresholdTimestamp =
- Math.round(Date.now() / 1000) - deleteAfterSeconds;
+ const pruneThresholdTimestamp = Math.round(Date.now() / 1000) - deleteAfterSeconds;
if (deleteAfterSeconds === 0) {
console.log(`[Pruner] Pruning is disabled. Skipping.`);
return;
}
- console.log(
- `[Pruner] Pruning notifications older than ${deleteAfterSeconds}s (timestamp ${pruneThresholdTimestamp})`
- );
+ console.log(`[Pruner] Pruning notifications older than ${deleteAfterSeconds}s (timestamp ${pruneThresholdTimestamp})`);
try {
await subscriptionManager.pruneNotifications(pruneThresholdTimestamp);
} catch (e) {
diff --git a/web/src/app/SubscriptionManager.js b/web/src/app/SubscriptionManager.js
index 25d08309..a539362c 100644
--- a/web/src/app/SubscriptionManager.js
+++ b/web/src/app/SubscriptionManager.js
@@ -7,9 +7,7 @@ class SubscriptionManager {
const subscriptions = await db.subscriptions.toArray();
await Promise.all(
subscriptions.map(async (s) => {
- s.new = await db.notifications
- .where({ subscriptionId: s.id, new: 1 })
- .count();
+ s.new = await db.notifications.where({ subscriptionId: s.id, new: 1 }).count();
})
);
return subscriptions;
@@ -38,20 +36,14 @@ class SubscriptionManager {
}
async syncFromRemote(remoteSubscriptions, remoteReservations) {
- console.log(
- `[SubscriptionManager] Syncing subscriptions from remote`,
- remoteSubscriptions
- );
+ console.log(`[SubscriptionManager] Syncing subscriptions from remote`, remoteSubscriptions);
// Add remote subscriptions
let remoteIds = []; // = topicUrl(baseUrl, topic)
for (let i = 0; i < remoteSubscriptions.length; i++) {
const remote = remoteSubscriptions[i];
const local = await this.add(remote.base_url, remote.topic, false);
- const reservation =
- remoteReservations?.find(
- (r) => remote.base_url === config.base_url && remote.topic === r.topic
- ) || null;
+ const reservation = remoteReservations?.find((r) => remote.base_url === config.base_url && remote.topic === r.topic) || null;
await this.update(local.id, {
displayName: remote.display_name, // May be undefined
reservation: reservation, // May be null!
@@ -122,9 +114,7 @@ class SubscriptionManager {
/** Adds/replaces notifications, will not throw if they exist */
async addNotifications(subscriptionId, notifications) {
- const notificationsWithSubscriptionId = notifications.map(
- (notification) => ({ ...notification, subscriptionId })
- );
+ const notificationsWithSubscriptionId = notifications.map((notification) => ({ ...notification, subscriptionId }));
const lastNotificationId = notifications.at(-1).id;
await db.notifications.bulkPut(notificationsWithSubscriptionId);
await db.subscriptions.update(subscriptionId, {
@@ -158,9 +148,7 @@ class SubscriptionManager {
}
async markNotificationsRead(subscriptionId) {
- await db.notifications
- .where({ subscriptionId: subscriptionId, new: 1 })
- .modify({ new: 0 });
+ await db.notifications.where({ subscriptionId: subscriptionId, new: 1 }).modify({ new: 0 });
}
async setMutedUntil(subscriptionId, mutedUntil) {
diff --git a/web/src/app/errors.js b/web/src/app/errors.js
index 96aaf86f..e31949d2 100644
--- a/web/src/app/errors.js
+++ b/web/src/app/errors.js
@@ -15,12 +15,7 @@ export const throwAppError = async (response) => {
}
const error = await maybeToJson(response);
if (error?.code) {
- console.log(
- `[Error] HTTP ${response.status}, ntfy error ${error.code}: ${
- error.error || ""
- }`,
- response
- );
+ console.log(`[Error] HTTP ${response.status}, ntfy error ${error.code}: ${error.error || ""}`, response);
if (error.code === UserExistsError.CODE) {
throw new UserExistsError();
} else if (error.code === TopicReservedError.CODE) {
diff --git a/web/src/app/utils.js b/web/src/app/utils.js
index f67c2d4b..d6bb02d8 100644
--- a/web/src/app/utils.js
+++ b/web/src/app/utils.js
@@ -10,37 +10,23 @@ import config from "./config";
import { Base64 } from "js-base64";
export const topicUrl = (baseUrl, topic) => `${baseUrl}/${topic}`;
-export const topicUrlWs = (baseUrl, topic) =>
- `${topicUrl(baseUrl, topic)}/ws`
- .replaceAll("https://", "wss://")
- .replaceAll("http://", "ws://");
-export const topicUrlJson = (baseUrl, topic) =>
- `${topicUrl(baseUrl, topic)}/json`;
-export const topicUrlJsonPoll = (baseUrl, topic) =>
- `${topicUrlJson(baseUrl, topic)}?poll=1`;
-export const topicUrlJsonPollWithSince = (baseUrl, topic, since) =>
- `${topicUrlJson(baseUrl, topic)}?poll=1&since=${since}`;
-export const topicUrlAuth = (baseUrl, topic) =>
- `${topicUrl(baseUrl, topic)}/auth`;
-export const topicShortUrl = (baseUrl, topic) =>
- shortUrl(topicUrl(baseUrl, topic));
+export const topicUrlWs = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/ws`.replaceAll("https://", "wss://").replaceAll("http://", "ws://");
+export const topicUrlJson = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/json`;
+export const topicUrlJsonPoll = (baseUrl, topic) => `${topicUrlJson(baseUrl, topic)}?poll=1`;
+export const topicUrlJsonPollWithSince = (baseUrl, topic, since) => `${topicUrlJson(baseUrl, topic)}?poll=1&since=${since}`;
+export const topicUrlAuth = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/auth`;
+export const topicShortUrl = (baseUrl, topic) => shortUrl(topicUrl(baseUrl, topic));
export const accountUrl = (baseUrl) => `${baseUrl}/v1/account`;
export const accountPasswordUrl = (baseUrl) => `${baseUrl}/v1/account/password`;
export const accountTokenUrl = (baseUrl) => `${baseUrl}/v1/account/token`;
export const accountSettingsUrl = (baseUrl) => `${baseUrl}/v1/account/settings`;
-export const accountSubscriptionUrl = (baseUrl) =>
- `${baseUrl}/v1/account/subscription`;
-export const accountReservationUrl = (baseUrl) =>
- `${baseUrl}/v1/account/reservation`;
-export const accountReservationSingleUrl = (baseUrl, topic) =>
- `${baseUrl}/v1/account/reservation/${topic}`;
-export const accountBillingSubscriptionUrl = (baseUrl) =>
- `${baseUrl}/v1/account/billing/subscription`;
-export const accountBillingPortalUrl = (baseUrl) =>
- `${baseUrl}/v1/account/billing/portal`;
+export const accountSubscriptionUrl = (baseUrl) => `${baseUrl}/v1/account/subscription`;
+export const accountReservationUrl = (baseUrl) => `${baseUrl}/v1/account/reservation`;
+export const accountReservationSingleUrl = (baseUrl, topic) => `${baseUrl}/v1/account/reservation/${topic}`;
+export const accountBillingSubscriptionUrl = (baseUrl) => `${baseUrl}/v1/account/billing/subscription`;
+export const accountBillingPortalUrl = (baseUrl) => `${baseUrl}/v1/account/billing/portal`;
export const accountPhoneUrl = (baseUrl) => `${baseUrl}/v1/account/phone`;
-export const accountPhoneVerifyUrl = (baseUrl) =>
- `${baseUrl}/v1/account/phone/verify`;
+export const accountPhoneVerifyUrl = (baseUrl) => `${baseUrl}/v1/account/phone/verify`;
export const tiersUrl = (baseUrl) => `${baseUrl}/v1/tiers`;
export const shortUrl = (url) => url.replaceAll(/https?:\/\//g, "");
export const expandUrl = (url) => [`https://${url}`, `http://${url}`];
@@ -208,9 +194,7 @@ export const formatShortDateTime = (timestamp) => {
};
export const formatShortDate = (timestamp) => {
- return new Intl.DateTimeFormat("default", { dateStyle: "short" }).format(
- new Date(timestamp * 1000)
- );
+ return new Intl.DateTimeFormat("default", { dateStyle: "short" }).format(new Date(timestamp * 1000));
};
export const formatBytes = (bytes, decimals = 2) => {
@@ -312,8 +296,7 @@ export async function* fetchLinesIterator(fileURL, headers) {
}
export const randomAlphanumericString = (len) => {
- const alphabet =
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let id = "";
for (let i = 0; i < len; i++) {
id += alphabet[(Math.random() * alphabet.length) | 0];
diff --git a/web/src/components/Account.js b/web/src/components/Account.js
index bb8e7a74..b6710c6c 100644
--- a/web/src/components/Account.js
+++ b/web/src/components/Account.js
@@ -38,18 +38,8 @@ import DialogContent from "@mui/material/DialogContent";
import TextField from "@mui/material/TextField";
import routes from "./routes";
import IconButton from "@mui/material/IconButton";
-import {
- formatBytes,
- formatShortDate,
- formatShortDateTime,
- openUrl,
-} from "../app/utils";
-import accountApi, {
- LimitBasis,
- Role,
- SubscriptionInterval,
- SubscriptionStatus,
-} from "../app/AccountApi";
+import { formatBytes, formatShortDate, formatShortDateTime, openUrl } from "../app/utils";
+import accountApi, { LimitBasis, Role, SubscriptionInterval, SubscriptionStatus } from "../app/AccountApi";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import { Pref, PrefGroup } from "./Pref";
import db from "../app/db";
@@ -108,11 +98,7 @@ const Username = () => {
const labelId = "prefUsername";
return (
-
+
{session.username()}
{account?.role === Role.ADMIN ? (
@@ -146,30 +132,16 @@ const ChangePassword = () => {
};
return (
-
+
-
+
⬤⬤⬤⬤⬤⬤⬤⬤⬤⬤
-
+
-
+
);
};
@@ -190,9 +162,7 @@ const ChangePasswordDialog = (props) => {
} catch (e) {
console.log(`[Account] Error changing password`, e);
if (e instanceof IncorrectPasswordError) {
- setError(
- t("account_basics_password_dialog_current_password_incorrect")
- );
+ setError(t("account_basics_password_dialog_current_password_incorrect"));
} else if (e instanceof UnauthorizedError) {
session.resetAndRedirect(routes.login);
} else {
@@ -209,9 +179,7 @@ const ChangePasswordDialog = (props) => {
margin="dense"
id="current-password"
label={t("account_basics_password_dialog_current_password_label")}
- aria-label={t(
- "account_basics_password_dialog_current_password_label"
- )}
+ aria-label={t("account_basics_password_dialog_current_password_label")}
type="password"
value={currentPassword}
onChange={(ev) => setCurrentPassword(ev.target.value)}
@@ -233,9 +201,7 @@ const ChangePasswordDialog = (props) => {
margin="dense"
id="confirm"
label={t("account_basics_password_dialog_confirm_password_label")}
- aria-label={t(
- "account_basics_password_dialog_confirm_password_label"
- )}
+ aria-label={t("account_basics_password_dialog_confirm_password_label")}
type="password"
value={confirmPassword}
onChange={(ev) => setConfirmPassword(ev.target.value)}
@@ -245,14 +211,7 @@ const ChangePasswordDialog = (props) => {
-
@@ -299,9 +258,7 @@ const AccountType = () => {
: t("account_basics_tier_admin_suffix_no_tier");
accountType = `${t("account_basics_tier_admin")} ${tierSuffix}`;
} else if (!account.tier) {
- accountType = config.enable_payments
- ? t("account_basics_tier_free")
- : t("account_basics_tier_basic");
+ accountType = config.enable_payments ? t("account_basics_tier_free") : t("account_basics_tier_basic");
} else {
accountType = account.tier.name;
if (account.billing?.interval === SubscriptionInterval.MONTH) {
@@ -313,10 +270,7 @@ const AccountType = () => {
return (
0
- }
+ alignTop={account.billing?.status === SubscriptionStatus.PAST_DUE || account.billing?.cancel_at > 0}
title={t("account_basics_tier_title")}
description={t("account_basics_tier_description")}
>
@@ -333,49 +287,23 @@ const AccountType = () => {
)}
- {config.enable_payments &&
- account.role === Role.USER &&
- !account.billing?.subscription && (
- }
- onClick={handleUpgradeClick}
- sx={{ ml: 1 }}
- >
- {t("account_basics_tier_upgrade_button")}
-
- )}
- {config.enable_payments &&
- account.role === Role.USER &&
- account.billing?.subscription && (
-
- {t("account_basics_tier_change_button")}
-
- )}
- {config.enable_payments &&
- account.role === Role.USER &&
- account.billing?.customer && (
-
- {t("account_basics_tier_manage_billing_button")}
-
- )}
+ {config.enable_payments && account.role === Role.USER && !account.billing?.subscription && (
+ } onClick={handleUpgradeClick} sx={{ ml: 1 }}>
+ {t("account_basics_tier_upgrade_button")}
+
+ )}
+ {config.enable_payments && account.role === Role.USER && account.billing?.subscription && (
+
+ {t("account_basics_tier_change_button")}
+
+ )}
+ {config.enable_payments && account.role === Role.USER && account.billing?.customer && (
+
+ {t("account_basics_tier_manage_billing_button")}
+
+ )}
{config.enable_payments && (
- setUpgradeDialogOpen(false)}
- />
+ setUpgradeDialogOpen(false)} />
)}
{account.billing?.status === SubscriptionStatus.PAST_DUE && (
@@ -456,11 +384,7 @@ const PhoneNumbers = () => {
}
return (
-
+
{account?.phone_numbers?.map((phoneNumber) => (
{
onDelete={() => handleDelete(phoneNumber)}
/>
))}
- {!account?.phone_numbers && (
- {t("account_basics_phone_numbers_no_phone_numbers_yet")}
- )}
+ {!account?.phone_numbers && {t("account_basics_phone_numbers_no_phone_numbers_yet")}}
-
+
{
return (
)}
@@ -722,14 +602,7 @@ const Stats = () => {
: t("account_usage_unlimited")}
-
+
{config.enable_emails && (
{
: t("account_usage_unlimited")}
+
+
+ )}
+ {config.enable_calls && (account.role === Role.ADMIN || account.limits.calls > 0) && (
+
+ {t("account_usage_calls_title")}
+
+
+
+
+
+ >
+ }
+ >
+
+
+ {account.stats.calls.toLocaleString()}
+
+
+ {account.role === Role.USER
+ ? t("account_usage_of_limit", {
+ limit: account.limits.calls.toLocaleString(),
+ })
+ : t("account_usage_unlimited")}
+
+
0 ? normalize(account.stats.calls, account.limits.calls) : 100}
/>
)}
- {config.enable_calls &&
- (account.role === Role.ADMIN || account.limits.calls > 0) && (
-
- {t("account_usage_calls_title")}
-
-
-
-
-
- >
- }
- >
-
-
- {account.stats.calls.toLocaleString()}
-
-
- {account.role === Role.USER
- ? t("account_usage_of_limit", {
- limit: account.limits.calls.toLocaleString(),
- })
- : t("account_usage_unlimited")}
-
-
- 0
- ? normalize(account.stats.calls, account.limits.calls)
- : 100
- }
- />
-
- )}
@@ -830,49 +688,36 @@ const Stats = () => {
- {config.enable_reservations &&
- account.role === Role.USER &&
- account.limits.reservations === 0 && (
-
- {t("account_usage_reservations_title")}
- {config.enable_payments && }
- >
- }
- >
- {t("account_usage_reservations_none")}
-
- )}
- {config.enable_calls &&
- account.role === Role.USER &&
- account.limits.calls === 0 && (
-
- {t("account_usage_calls_title")}
- {config.enable_payments && }
- >
- }
- >
- {t("account_usage_calls_none")}
-
- )}
+ {config.enable_reservations && account.role === Role.USER && account.limits.reservations === 0 && (
+
+ {t("account_usage_reservations_title")}
+ {config.enable_payments && }
+ >
+ }
+ >
+ {t("account_usage_reservations_none")}
+
+ )}
+ {config.enable_calls && account.role === Role.USER && account.limits.calls === 0 && (
+
+ {t("account_usage_calls_title")}
+ {config.enable_payments && }
+ >
+ }
+ >
+ {t("account_usage_calls_none")}
+
+ )}
{account.role === Role.USER && account.limits.basis === LimitBasis.IP && (
-
- {t("account_usage_basis_ip_description")}
-
+ {t("account_usage_basis_ip_description")}
)}
);
@@ -928,15 +773,9 @@ const Tokens = () => {
{tokens?.length > 0 && }
-
- {t("account_tokens_table_create_token_button")}
-
+ {t("account_tokens_table_create_token_button")}
-
+
);
};
@@ -984,9 +823,7 @@ const TokensTable = (props) => {
-
- {t("account_tokens_table_token_header")}
-
+ {t("account_tokens_table_token_header")}
{t("account_tokens_table_label_header")}
{t("account_tokens_table_expires_header")}
{t("account_tokens_table_last_access_header")}
@@ -995,25 +832,12 @@ const TokensTable = (props) => {
{tokens.map((token) => (
-
-
+
+
-
- {token.token.slice(0, 12)}
-
+ {token.token.slice(0, 12)}
...
-
+
handleCopy(token.token)}>
@@ -1021,25 +845,13 @@ const TokensTable = (props) => {
- {token.token === session.token() && (
- {t("account_tokens_table_current_session")}
- )}
+ {token.token === session.token() && {t("account_tokens_table_current_session")}}
{token.token !== session.token() && (token.label || "-")}
-
- {token.expires ? (
- formatShortDateTime(token.expires)
- ) : (
- {t("account_tokens_table_never_expires")}
- )}
+
+ {token.expires ? formatShortDateTime(token.expires) : {t("account_tokens_table_never_expires")}}
-
+
{formatShortDateTime(token.last_access)}
{
ip: token.last_origin,
})}
>
-
- openUrl(
- `https://whatismyipaddress.com/ip/${token.last_origin}`
- )
- }
- >
+ openUrl(`https://whatismyipaddress.com/ip/${token.last_origin}`)}>
@@ -1062,24 +868,16 @@ const TokensTable = (props) => {
{token.token !== session.token() && (
<>
- handleEditClick(token)}
- aria-label={t("account_tokens_dialog_title_edit")}
- >
+ handleEditClick(token)} aria-label={t("account_tokens_dialog_title_edit")}>
- handleDeleteClick(token)}
- aria-label={t("account_tokens_dialog_title_delete")}
- >
+ handleDeleteClick(token)} aria-label={t("account_tokens_dialog_title_delete")}>
>
)}
{token.token === session.token() && (
-
+
@@ -1095,24 +893,10 @@ const TokensTable = (props) => {
))}
- setSnackOpen(false)}
- message={t("account_tokens_table_copied_to_clipboard")}
- />
+ setSnackOpen(false)} message={t("account_tokens_table_copied_to_clipboard")} />
-
-
+
+
);
};
@@ -1144,18 +928,8 @@ const TokenDialog = (props) => {
};
return (
-