Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions frontend/src/styles/popups.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1863,15 +1863,17 @@ body.darkMode {
}
}
.notificationHistory .list .item {
grid-template-areas: "indicator title" "indicator body";
grid-template-columns: 0.25rem calc(100% - 0.25rem);
grid-template-areas: "indicator title buttons" "indicator body buttons";
.title {
font-size: 0.75rem;
color: var(--sub-color);
}
.body {
opacity: 1;
}
.highlight {
color: var(--main-color) !important;
}
}
.accountAlerts {
.title {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/styles/settings.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
.settingsGroup {
display: grid;
gap: 2rem;
overflow: hidden;
&.quickNav {
justify-content: center;
.links {
Expand Down
9 changes: 3 additions & 6 deletions frontend/src/ts/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,11 @@ async function sendVerificationEmail(): Promise<void> {

Loader.show();
qs(".sendVerificationEmail")?.disable();
const result = await Ape.users.verificationEmail();
const response = await Ape.users.verificationEmail();
qs(".sendVerificationEmail")?.enable();
if (result.status !== 200) {
if (response.status !== 200) {
Loader.hide();
Notifications.add(
"Failed to request verification email: " + result.body.message,
-1,
);
Notifications.add("Failed to request verification email", -1, { response });
} else {
Loader.hide();
Notifications.add("Verification email sent", 1);
Expand Down
26 changes: 7 additions & 19 deletions frontend/src/ts/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ export async function getUserResults(offset?: number): Promise<boolean> {
const response = await Ape.results.get({ query: { offset } });

if (response.status !== 200) {
Notifications.add("Error getting results: " + response.body.message, -1);
Notifications.add("Error getting results", -1, { response });
return false;
}

Expand Down Expand Up @@ -357,10 +357,7 @@ export async function addCustomTheme(

const response = await Ape.users.addCustomTheme({ body: { ...theme } });
if (response.status !== 200) {
Notifications.add(
"Error adding custom theme: " + response.body.message,
-1,
);
Notifications.add("Error adding custom theme", -1, { response });
return false;
}

Expand Down Expand Up @@ -400,10 +397,7 @@ export async function editCustomTheme(
body: { themeId, theme: newTheme },
});
if (response.status !== 200) {
Notifications.add(
"Error editing custom theme: " + response.body.message,
-1,
);
Notifications.add("Error editing custom theme", -1, { response });
return false;
}

Expand All @@ -427,10 +421,7 @@ export async function deleteCustomTheme(themeId: string): Promise<boolean> {

const response = await Ape.users.deleteCustomTheme({ body: { themeId } });
if (response.status !== 200) {
Notifications.add(
"Error deleting custom theme: " + response.body.message,
-1,
);
Notifications.add("Error deleting custom theme", -1, { response });
return false;
}

Expand Down Expand Up @@ -923,7 +914,7 @@ export async function saveConfig(config: Partial<Config>): Promise<void> {
if (isAuthenticated()) {
const response = await Ape.configs.save({ body: config });
if (response.status !== 200) {
Notifications.add("Failed to save config: " + response.body.message, -1);
Notifications.add("Failed to save config", -1, { response });
}
}
}
Expand All @@ -932,7 +923,7 @@ export async function resetConfig(): Promise<void> {
if (isAuthenticated()) {
const response = await Ape.configs.delete();
if (response.status !== 200) {
Notifications.add("Failed to reset config: " + response.body.message, -1);
Notifications.add("Failed to reset config", -1, { response });
}
}
}
Expand Down Expand Up @@ -1055,10 +1046,7 @@ export async function getTestActivityCalendar(
Loader.show();
const response = await Ape.users.getTestActivity();
if (response.status !== 200) {
Notifications.add(
"Error getting test activities: " + response.body.message,
-1,
);
Notifications.add("Error getting test activities", -1, { response });
Loader.hide();
return undefined;
}
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/ts/elements/account-settings/ape-key-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const editApeKey = new SimpleModal({
if (response.status !== 200) {
return {
status: -1,
message: "Failed to update key: " + response.body.message,
message: "Failed to update key",
notificationOptions: { response },
};
}
return {
Expand All @@ -53,7 +54,8 @@ const deleteApeKeyModal = new SimpleModal({
if (response.status !== 200) {
return {
status: -1,
message: "Failed to delete key: " + response.body.message,
message: "Failed to delete key",
notificationOptions: { response },
};
}

Expand Down Expand Up @@ -128,7 +130,8 @@ const generateApeKey = new SimpleModal({
if (response.status !== 200) {
return {
status: -1,
message: "Failed to generate key: " + response.body.message,
message: "Failed to generate key",
notificationOptions: { response },
};
}

Expand Down Expand Up @@ -174,7 +177,7 @@ async function getData(): Promise<boolean> {
void update();
return false;
}
Notifications.add("Error getting ape keys: " + response.body.message, -1);
Notifications.add("Error getting ape keys", -1, { response });
return false;
}

Expand Down Expand Up @@ -261,7 +264,7 @@ async function toggleActiveKey(keyId: string): Promise<void> {
});
Loader.hide();
if (response.status !== 200) {
Notifications.add("Failed to update key: " + response.body.message, -1);
Notifications.add("Failed to update key", -1, { response });
return;
}
key.enabled = !key.enabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ async function getData(): Promise<boolean> {

if (response.status !== 200) {
blockedUsers = [];
Notifications.add(
"Error getting blocked users: " + response.body.message,
-1,
);
Notifications.add("Error getting blocked users", -1, { response });
return false;
}

Expand Down
119 changes: 106 additions & 13 deletions frontend/src/ts/elements/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import * as NotificationEvent from "../observables/notification-event";
import * as BadgeController from "../controllers/badge-controller";
import * as Notifications from "../elements/notifications";
import * as ConnectionState from "../states/connection";
import { escapeHTML } from "../utils/misc";
import {
applyReducedMotion,
createErrorMessage,
escapeHTML,
promiseAnimate,
} from "../utils/misc";
import AnimatedModal from "../utils/animated-modal";
import { updateXp as accountPageUpdateProfile } from "./profile";
import { MonkeyMail } from "@monkeytype/schemas/users";
Expand All @@ -29,10 +34,17 @@ let mailToMarkRead: string[] = [];
let mailToDelete: string[] = [];

type State = {
notifications: { message: string; level: number; customTitle?: string }[];
notifications: {
id: string;
title: string;
message: string;
level: number;
details?: string | object;
}[];
psas: { message: string; level: number }[];
};

let notificationId = 0;
const state: State = {
notifications: [],
psas: [],
Expand Down Expand Up @@ -289,28 +301,29 @@ function fillNotifications(): void {
} else {
notificationHistoryListEl.empty();
for (const n of state.notifications) {
const { message, level, customTitle } = n;
let title = "Notice";
const { message, level, title } = n;

let levelClass = "sub";
if (level === -1) {
levelClass = "error";
title = "Error";
} else if (level === 1) {
levelClass = "main";
title = "Success";
}

if (customTitle !== undefined) {
title = customTitle;
}

notificationHistoryListEl.prependHtml(`
<div class="item">
<div class="item" data-id="${n.id}">
<div class="indicator ${levelClass}"></div>
<div class="title">${title}</div>
<div class="body">
${escapeHTML(message)}
</div>
<div class="buttons">
${
n.details !== undefined
? `<button class="copyNotification textButton" aria-label="Copy details to clipboard" data-balloon-pos="left"><i class="fas fa-fw fa-clipboard"></i></button>`
: ``
}
</div>
</div>
`);
}
Expand Down Expand Up @@ -396,15 +409,89 @@ function updateClaimDeleteAllButton(): void {
}
}

async function copyNotificationToClipboard(target: HTMLElement): Promise<void> {
const id = (target as HTMLElement | null)
?.closest(".item")
?.getAttribute("data-id")
?.toString();

if (id === undefined) {
throw new Error("Notification ID is undefined");
}
const notification = state.notifications.find((it) => it.id === id);
if (notification === undefined) return;

const icon = target.querySelector("i") as HTMLElement;

try {
await navigator.clipboard.writeText(
JSON.stringify(
{
title: notification.title,
message: notification.message,
details: notification.details,
},
null,
4,
),
);

const duration = applyReducedMotion(100);

await promiseAnimate(icon, {
scale: [1, 0.8],
opacity: [1, 0],
duration,
});
icon.classList.remove("fa-clipboard");
icon.classList.add("fa-check", "highlight");
await promiseAnimate(icon, {
scale: [0.8, 1],
opacity: [0, 1],
duration,
});

await promiseAnimate(icon, {
scale: [1, 0.8],
opacity: [1, 0],
delay: 3000,
duration,
});
icon.classList.remove("fa-check", "highlight");
icon.classList.add("fa-clipboard");

await promiseAnimate(icon, {
scale: [0.8, 1],
opacity: [0, 1],
duration,
});
} catch (e: unknown) {
const msg = createErrorMessage(e, "Could not copy to clipboard");
Notifications.add(msg, -1);
}
}

qs("header nav .showAlerts")?.on("click", () => {
void show();
});

NotificationEvent.subscribe((message, level, customTitle) => {
NotificationEvent.subscribe((message, level, options) => {
let title = "Notice";
if (level === -1) {
title = "Error";
} else if (level === 1) {
title = "Success";
}
if (options.customTitle !== undefined) {
title = options.customTitle;
}

state.notifications.push({
id: (notificationId++).toString(),
title,
message,
level,
customTitle,
details: options.details,
});
if (state.notifications.length > 25) {
state.notifications.shift();
Expand Down Expand Up @@ -495,5 +582,11 @@ const modal = new AnimatedModal({

markReadAlert(id);
});

alertsPopupEl
.qs(".notificationHistory .list")
?.onChild("click", ".item .buttons .copyNotification", (e) => {
void copyNotificationToClipboard(e.target as HTMLElement);
});
},
});
Loading
Loading