mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2024-11-10 17:04:49 +08:00
refactor(ape keys modal): use new modal system
This commit is contained in:
parent
7f5e3aa73e
commit
6f75abef70
6 changed files with 180 additions and 196 deletions
|
@ -689,14 +689,14 @@
|
|||
<button>submit</button>
|
||||
</div>
|
||||
</dialog>
|
||||
<div id="apeKeysPopupWrapper" class="hidden popupWrapper">
|
||||
<div id="apeKeysPopup" mode="">
|
||||
<dialog id="apeKeysModal" class="hidden modalWrapper">
|
||||
<div class="modal">
|
||||
<div class="top">
|
||||
<div class="title">Ape Keys</div>
|
||||
<div class="button generateApeKey">
|
||||
<button class="generateApeKey">
|
||||
<i class="fas fa-plus"></i>
|
||||
Generate new key
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -712,7 +712,7 @@
|
|||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
<dialog id="quoteReportModal" class="modalWrapper hidden">
|
||||
<div class="modal">
|
||||
<div class="title">Report a quote</div>
|
||||
|
|
|
@ -932,108 +932,99 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
#apeKeysModal {
|
||||
.modal {
|
||||
max-width: 1000px;
|
||||
|
||||
#apeKeysPopup {
|
||||
background: var(--bg-color);
|
||||
border-radius: var(--roundness);
|
||||
padding: 2rem;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
width: 1000px;
|
||||
max-width: calc(100vw - 4rem);
|
||||
// height: 100%;
|
||||
// max-height: 40rem;
|
||||
min-height: 18rem;
|
||||
overflow-y: scroll;
|
||||
grid-template-rows: max-content auto;
|
||||
align-items: baseline;
|
||||
gap: 1rem;
|
||||
grid-template-rows: max-content auto;
|
||||
align-items: baseline;
|
||||
|
||||
.top {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
color: var(--sub-color);
|
||||
}
|
||||
.button {
|
||||
padding: 0.4rem 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.textButton {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.keyButtons {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
gap: 1rem;
|
||||
.button {
|
||||
width: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
|
||||
tr td:first-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
tr.me {
|
||||
td {
|
||||
color: var(--main-color);
|
||||
// font-weight: 900;
|
||||
.top {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
color: var(--sub-color);
|
||||
}
|
||||
button {
|
||||
padding: 0.5em 2em;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.5rem 0.5rem;
|
||||
.textButton {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
thead {
|
||||
color: var(--sub-color);
|
||||
font-size: 0.75rem;
|
||||
.keyButtons {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
gap: 1rem;
|
||||
.button {
|
||||
width: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
|
||||
tr td:first-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
tr.me {
|
||||
td {
|
||||
color: var(--main-color);
|
||||
// font-weight: 900;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.5rem;
|
||||
background: var(--bg-color);
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
padding: 0.5rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
color: var(--text-color);
|
||||
thead {
|
||||
color: var(--sub-color);
|
||||
font-size: 0.75rem;
|
||||
|
||||
tr:nth-child(odd) td {
|
||||
background: var(--sub-alt-color);
|
||||
td {
|
||||
padding: 0.5rem;
|
||||
background: var(--bg-color);
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tfoot {
|
||||
td {
|
||||
padding: 1rem 0.5rem;
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
bottom: -5px;
|
||||
background: var(--bg-color);
|
||||
color: var(--main-color);
|
||||
z-index: 4;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
color: var(--text-color);
|
||||
|
||||
tr {
|
||||
td:first-child {
|
||||
padding-left: 1rem;
|
||||
tr:nth-child(odd) td {
|
||||
background: var(--sub-alt-color);
|
||||
}
|
||||
}
|
||||
td:last-child {
|
||||
padding-right: 1rem;
|
||||
|
||||
tfoot {
|
||||
td {
|
||||
padding: 1rem 0.5rem;
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
bottom: -5px;
|
||||
background: var(--bg-color);
|
||||
color: var(--main-color);
|
||||
z-index: 4;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
td:first-child {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
td:last-child {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as CookiesModal from "../modals/cookies";
|
|||
import * as StreakHourOffsetModal from "../modals/streak-hour-offset";
|
||||
import * as EditPresetPopup from "../modals/edit-preset";
|
||||
import * as EditTagPopup from "../modals/edit-tag";
|
||||
import * as ApeKeysModal from "../modals/ape-keys";
|
||||
|
||||
import * as Notifications from "../elements/notifications";
|
||||
|
||||
|
@ -131,3 +132,9 @@ settingsPage?.querySelector(".section.tags")?.addEventListener("click", (e) => {
|
|||
EditTagPopup.show("remove", tagid, name);
|
||||
}
|
||||
});
|
||||
|
||||
settingsPage
|
||||
?.querySelector(".section.apeKeys #showApeKeysPopup")
|
||||
?.addEventListener("click", () => {
|
||||
void ApeKeysModal.show();
|
||||
});
|
||||
|
|
|
@ -3,13 +3,11 @@ import * as Loader from "../elements/loader";
|
|||
import * as Notifications from "../elements/notifications";
|
||||
import format from "date-fns/format";
|
||||
import * as ConnectionState from "../states/connection";
|
||||
import * as Skeleton from "../utils/skeleton";
|
||||
import { isPopupVisible } from "../utils/misc";
|
||||
import AnimatedModal, { ShowOptions } from "../utils/animated-modal";
|
||||
import { showPopup } from "../popups/simple-popups";
|
||||
|
||||
let apeKeys: Ape.ApeKeys.GetApeKeys | null = {};
|
||||
|
||||
const wrapperId = "apeKeysPopupWrapper";
|
||||
|
||||
async function getData(): Promise<void> {
|
||||
Loader.show();
|
||||
const response = await Ape.apeKeys.get();
|
||||
|
@ -26,7 +24,7 @@ async function getData(): Promise<void> {
|
|||
function refreshList(): void {
|
||||
const data = apeKeys;
|
||||
if (data === undefined || data === null) return;
|
||||
const table = $("#apeKeysPopupWrapper table tbody");
|
||||
const table = $("#apeKeysModal table tbody");
|
||||
table.empty();
|
||||
const apeKeyIds = Object.keys(data);
|
||||
if (apeKeyIds.length === 0) {
|
||||
|
@ -40,15 +38,15 @@ function refreshList(): void {
|
|||
table.append(`
|
||||
<tr keyId="${apeKeyId}">
|
||||
<td>
|
||||
<div class="textButton">
|
||||
<button class="textButton toggleActive">
|
||||
${
|
||||
key.enabled
|
||||
? `<i class="fas fa-fw fa-check-square"></i>`
|
||||
: `<i class="far fa-fw fa-square"></i>`
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
</td>
|
||||
<td>${key.name}</td>
|
||||
<td onClick=${console.log(key)}>${key.name}</td>
|
||||
<td>${format(new Date(key.createdOn), "dd MMM yyyy HH:mm")}</td>
|
||||
<td>${format(new Date(key.modifiedOn), "dd MMM yyyy HH:mm")}</td>
|
||||
<td>${
|
||||
|
@ -58,81 +56,59 @@ function refreshList(): void {
|
|||
}</td>
|
||||
<td>
|
||||
<div class="keyButtons">
|
||||
<div class="button edit">
|
||||
<button class="editButton">
|
||||
<i class="fas fa-fw fa-pen"></i>
|
||||
</div>
|
||||
<div class="button delete">
|
||||
</button>
|
||||
<button class="deleteButton">
|
||||
<i class="fas fa-fw fa-trash-alt"></i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
function hide(): void {
|
||||
if (isPopupVisible(wrapperId)) {
|
||||
$("#apeKeysPopupWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 1)
|
||||
.animate(
|
||||
{
|
||||
opacity: 0,
|
||||
},
|
||||
125,
|
||||
() => {
|
||||
$("#apeKeysPopupWrapper").addClass("hidden");
|
||||
Skeleton.remove(wrapperId);
|
||||
}
|
||||
);
|
||||
for (const tr of table.find("tr")) {
|
||||
const keyid = tr.getAttribute("keyid") as string;
|
||||
tr.querySelector("button.deleteButton")?.addEventListener("click", (e) => {
|
||||
showPopup("deleteApeKey", [keyid], {
|
||||
modalChain: modal,
|
||||
});
|
||||
});
|
||||
tr.querySelector("button.editButton")?.addEventListener("click", (e) => {
|
||||
showPopup("editApeKey", [keyid], {
|
||||
modalChain: modal,
|
||||
});
|
||||
});
|
||||
tr.querySelector("button.toggleActive")?.addEventListener("click", (e) => {
|
||||
void toggleActiveKey(keyid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// function hide(clearModalChain = false): void {
|
||||
// void modal.hide({
|
||||
// clearModalChain,
|
||||
// });
|
||||
// }
|
||||
|
||||
//show the popup
|
||||
export async function show(): Promise<void> {
|
||||
export async function show(showOptions?: ShowOptions): Promise<void> {
|
||||
if (!ConnectionState.get()) {
|
||||
Notifications.add("You are offline", 0, {
|
||||
duration: 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
Skeleton.append(wrapperId, "popups");
|
||||
if (!isPopupVisible(wrapperId)) {
|
||||
await getData();
|
||||
refreshList();
|
||||
$("#apeKeysPopupWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 0)
|
||||
.removeClass("hidden")
|
||||
.animate(
|
||||
{
|
||||
opacity: 1,
|
||||
},
|
||||
125,
|
||||
() => {
|
||||
$("#apeKeysPopup textarea").trigger("focus").trigger("select");
|
||||
}
|
||||
);
|
||||
}
|
||||
void modal.show({
|
||||
...showOptions,
|
||||
beforeAnimation: async () => {
|
||||
await getData();
|
||||
refreshList();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
$("#apeKeysPopupWrapper").on("mousedown", (e) => {
|
||||
if ($(e.target).attr("id") === "apeKeysPopupWrapper") {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
$("#apeKeysPopup .generateApeKey").on("click", () => {
|
||||
hide();
|
||||
});
|
||||
|
||||
$("#popups").on("click", "#apeKeysPopup table .keyButtons .button", () => {
|
||||
hide();
|
||||
});
|
||||
|
||||
$("#popups").on("click", "#apeKeysPopup table .textButton", async (e) => {
|
||||
const keyId = $(e.target).closest("tr").attr("keyId") as string;
|
||||
async function toggleActiveKey(keyId: string): Promise<void> {
|
||||
const key = apeKeys?.[keyId];
|
||||
if (!key || apeKeys === undefined) return;
|
||||
Loader.show();
|
||||
|
@ -148,13 +124,19 @@ $("#popups").on("click", "#apeKeysPopup table .textButton", async (e) => {
|
|||
} else {
|
||||
Notifications.add("Key inactive", 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on("keydown", (event) => {
|
||||
if (event.key === "Escape" && isPopupVisible(wrapperId)) {
|
||||
hide();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
async function setup(modalEl: HTMLElement): Promise<void> {
|
||||
modalEl
|
||||
.querySelector(".generateApeKey")
|
||||
?.addEventListener("click", async () => {
|
||||
showPopup("generateApeKey", [], {
|
||||
modalChain: modal,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Skeleton.save(wrapperId);
|
||||
const modal = new AnimatedModal({
|
||||
dialogId: "apeKeysModal",
|
||||
setup,
|
||||
});
|
|
@ -11,7 +11,6 @@ import * as Notifications from "../elements/notifications";
|
|||
import * as ImportExportSettingsModal from "../modals/import-export-settings";
|
||||
import * as ConfigEvent from "../observables/config-event";
|
||||
import * as ActivePage from "../states/active-page";
|
||||
import * as ApeKeysPopup from "../popups/ape-keys-popup";
|
||||
import Page from "./page";
|
||||
import { getAuthenticatedUser, isAuthenticated } from "../firebase";
|
||||
import Ape from "../ape";
|
||||
|
@ -1141,10 +1140,6 @@ $(".pageSettings .sectionGroupTitle").on("click", (e) => {
|
|||
toggleSettingsGroup($(e.currentTarget).attr("group") as string);
|
||||
});
|
||||
|
||||
$(".pageSettings .section.apeKeys #showApeKeysPopup").on("click", () => {
|
||||
void ApeKeysPopup.show();
|
||||
});
|
||||
|
||||
$(
|
||||
".pageSettings .section[data-config-name='customBackgroundSize'] .inputAndButton button.save"
|
||||
).on("click", () => {
|
||||
|
|
|
@ -5,7 +5,6 @@ import * as UpdateConfig from "../config";
|
|||
import * as Loader from "../elements/loader";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
import * as Settings from "../pages/settings";
|
||||
import * as ApeKeysPopup from "../popups/ape-keys-popup";
|
||||
import * as ThemePicker from "../settings/theme-picker";
|
||||
import * as CustomText from "../test/custom-text";
|
||||
import * as SavedTextsPopup from "./saved-texts-popup";
|
||||
|
@ -30,7 +29,10 @@ import {
|
|||
} from "../utils/misc";
|
||||
import * as CustomTextState from "../states/custom-text-name";
|
||||
import * as ThemeController from "../controllers/theme-controller";
|
||||
import AnimatedModal, { ShowOptions } from "../utils/animated-modal";
|
||||
import AnimatedModal, {
|
||||
HideOptions,
|
||||
ShowOptions,
|
||||
} from "../utils/animated-modal";
|
||||
|
||||
type Input = {
|
||||
placeholder?: string;
|
||||
|
@ -48,6 +50,7 @@ type ExecReturn = {
|
|||
message: string;
|
||||
showNotification?: false;
|
||||
notificationOptions?: MonkeyTypes.AddNotificationOptions;
|
||||
hideOptions?: HideOptions;
|
||||
afterHide?: () => void;
|
||||
};
|
||||
|
||||
|
@ -117,6 +120,7 @@ type SimplePopupOptions = {
|
|||
beforeShowFn?: (thisPopup: SimplePopup) => void;
|
||||
canClose?: boolean;
|
||||
onlineOnly?: boolean;
|
||||
hideCallsExec?: boolean;
|
||||
};
|
||||
|
||||
const modal = new AnimatedModal({
|
||||
|
@ -150,6 +154,7 @@ class SimplePopup {
|
|||
beforeShowFn: ((thisPopup: SimplePopup) => void) | undefined;
|
||||
canClose: boolean;
|
||||
onlineOnly: boolean;
|
||||
hideCallsExec: boolean;
|
||||
constructor(options: SimplePopupOptions) {
|
||||
this.parameters = [];
|
||||
this.id = options.id;
|
||||
|
@ -165,6 +170,7 @@ class SimplePopup {
|
|||
this.beforeShowFn = options.beforeShowFn;
|
||||
this.canClose = options.canClose ?? true;
|
||||
this.onlineOnly = options.onlineOnly ?? false;
|
||||
this.hideCallsExec = options.hideCallsExec ?? false;
|
||||
}
|
||||
reset(): void {
|
||||
this.element.innerHTML = `
|
||||
|
@ -306,7 +312,7 @@ class SimplePopup {
|
|||
Notifications.add(res.message, res.status, res.notificationOptions);
|
||||
}
|
||||
if (res.status === 1) {
|
||||
void this.hide().then(() => {
|
||||
void this.hide(true, res.hideOptions).then(() => {
|
||||
if (res.afterHide) {
|
||||
res.afterHide();
|
||||
}
|
||||
|
@ -348,10 +354,14 @@ class SimplePopup {
|
|||
});
|
||||
}
|
||||
|
||||
async hide(): Promise<void> {
|
||||
async hide(callerIsExec?: boolean, hideOptions?: HideOptions): Promise<void> {
|
||||
if (!this.canClose) return;
|
||||
activePopup = null;
|
||||
await modal.hide();
|
||||
if (this.hideCallsExec && !callerIsExec) {
|
||||
this.exec();
|
||||
} else {
|
||||
activePopup = null;
|
||||
await modal.hide(hideOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1211,11 +1221,19 @@ list.generateApeKey = new SimplePopup({
|
|||
//if response is 200 data is guaranteed to not be null
|
||||
const data = response.data as Ape.ApeKeys.GenerateApeKey;
|
||||
|
||||
const modalChain = modal.getPreviousModalInChain();
|
||||
return {
|
||||
status: 1,
|
||||
message: "Key generated",
|
||||
hideOptions: {
|
||||
clearModalChain: true,
|
||||
animationMode: "modalOnly",
|
||||
},
|
||||
afterHide: (): void => {
|
||||
showPopup("viewApeKey", [data.apeKey]);
|
||||
showPopup("viewApeKey", [data.apeKey], {
|
||||
modalChain,
|
||||
animationMode: "modalOnly",
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -1235,11 +1253,15 @@ list.viewApeKey = new SimplePopup({
|
|||
],
|
||||
text: "This is your new Ape Key. Please keep it safe. You will only see it once!",
|
||||
buttonText: "close",
|
||||
hideCallsExec: true,
|
||||
execFn: async (_thisPopup): Promise<ExecReturn> => {
|
||||
void ApeKeysPopup.show();
|
||||
return {
|
||||
status: 1,
|
||||
message: "Key generated",
|
||||
showNotification: false,
|
||||
hideOptions: {
|
||||
clearModalChain: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
beforeInitFn: (_thisPopup): void => {
|
||||
|
@ -1273,11 +1295,12 @@ list.deleteApeKey = new SimplePopup({
|
|||
};
|
||||
}
|
||||
|
||||
void ApeKeysPopup.show();
|
||||
|
||||
return {
|
||||
status: 1,
|
||||
message: "Key deleted",
|
||||
hideOptions: {
|
||||
clearModalChain: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -1304,12 +1327,12 @@ list.editApeKey = new SimplePopup({
|
|||
message: "Failed to update key: " + response.message,
|
||||
};
|
||||
}
|
||||
|
||||
void ApeKeysPopup.show();
|
||||
|
||||
return {
|
||||
status: 1,
|
||||
message: "Key updated",
|
||||
hideOptions: {
|
||||
clearModalChain: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -1526,7 +1549,7 @@ function isUsingPasswordAuthentication(): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function showPopup(
|
||||
export function showPopup(
|
||||
key: PopupKey,
|
||||
showParams = [] as string[],
|
||||
showOptions: ShowOptions = {}
|
||||
|
@ -1603,10 +1626,6 @@ $(".pageSettings #optOutOfLeaderboardsButton").on("click", () => {
|
|||
showPopup("optOutOfLeaderboards");
|
||||
});
|
||||
|
||||
$("#popups").on("click", "#apeKeysPopup .generateApeKey", () => {
|
||||
showPopup("generateApeKey");
|
||||
});
|
||||
|
||||
$(".pageSettings").on(
|
||||
"click",
|
||||
".section.themes .customTheme .delButton",
|
||||
|
@ -1654,16 +1673,6 @@ $("#popups").on(
|
|||
}
|
||||
);
|
||||
|
||||
$("#popups").on("click", "#apeKeysPopup table tbody tr .button.delete", (e) => {
|
||||
const keyId = $(e.target).closest("tr").attr("keyId") as string;
|
||||
showPopup("deleteApeKey", [keyId]);
|
||||
});
|
||||
|
||||
$("#popups").on("click", "#apeKeysPopup table tbody tr .button.edit", (e) => {
|
||||
const keyId = $(e.target).closest("tr").attr("keyId") as string;
|
||||
showPopup("editApeKey", [keyId]);
|
||||
});
|
||||
|
||||
$(".pageSettings").on(
|
||||
"click",
|
||||
".section[data-config-name='fontFamily'] button[data-config-value='custom']",
|
||||
|
|
Loading…
Reference in a new issue