chore(client/ts): port some dialogs

This commit is contained in:
Elian Doran 2025-03-15 22:14:21 +02:00
parent 1bf16bfa22
commit 182bccad39
No known key found for this signature in database
5 changed files with 62 additions and 27 deletions

View File

@ -6,15 +6,15 @@ import appContext from "../components/app_context.js";
import { t } from "./i18n.js"; import { t } from "./i18n.js";
interface UploadFilesOptions { interface UploadFilesOptions {
safeImport: boolean; safeImport?: boolean;
shrinkImages: boolean; shrinkImages: "true" | "false";
textImportedAsText: boolean; textImportedAsText?: boolean;
codeImportedAsCode: boolean; codeImportedAsCode?: boolean;
explodeArchives: boolean; explodeArchives?: boolean;
replaceUnderscoresWithSpaces: boolean; replaceUnderscoresWithSpaces?: boolean;
} }
export async function uploadFiles(entityType: string, parentNoteId: string, files: string[], options: UploadFilesOptions) { export async function uploadFiles(entityType: string, parentNoteId: string, files: string[] | File[], options: UploadFilesOptions) {
if (!["notes", "attachments"].includes(entityType)) { if (!["notes", "attachments"].includes(entityType)) {
throw new Error(`Unrecognized import entity type '${entityType}'.`); throw new Error(`Unrecognized import entity type '${entityType}'.`);
} }

View File

@ -27,14 +27,19 @@ const TPL = `
</div>`; </div>`;
export default class ProtectedSessionPasswordDialog extends BasicWidget { export default class ProtectedSessionPasswordDialog extends BasicWidget {
private modal!: bootstrap.Modal;
private $passwordForm!: JQuery<HTMLElement>;
private $passwordInput!: JQuery<HTMLElement>;
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.modal = Modal.getOrCreateInstance(this.$widget); this.modal = Modal.getOrCreateInstance(this.$widget[0]);
this.$passwordForm = this.$widget.find(".protected-session-password-form"); this.$passwordForm = this.$widget.find(".protected-session-password-form");
this.$passwordInput = this.$widget.find(".protected-session-password"); this.$passwordInput = this.$widget.find(".protected-session-password");
this.$passwordForm.on("submit", () => { this.$passwordForm.on("submit", () => {
const password = this.$passwordInput.val(); const password = String(this.$passwordInput.val());
this.$passwordInput.val(""); this.$passwordInput.val("");
protectedSessionService.setupProtectedSession(password); protectedSessionService.setupProtectedSession(password);

View File

@ -1,6 +1,6 @@
import { formatDateTime } from "../../utils/formatters.js"; import { formatDateTime } from "../../utils/formatters.js";
import { t } from "../../services/i18n.js"; import { t } from "../../services/i18n.js";
import appContext from "../../components/app_context.js"; import appContext, { type EventData } from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import dialogService from "../../services/dialog.js"; import dialogService from "../../services/dialog.js";
import froca from "../../services/froca.js"; import froca from "../../services/froca.js";
@ -28,10 +28,23 @@ const TPL = `
</div> </div>
</div>`; </div>`;
// TODO: Deduplicate with server.
interface RecentChangesRow {
noteId: string;
date: string;
}
export default class RecentChangesDialog extends BasicWidget { export default class RecentChangesDialog extends BasicWidget {
private ancestorNoteId?: string;
private modal!: bootstrap.Modal;
private $content!: JQuery<HTMLElement>;
private $eraseDeletedNotesNow!: JQuery<HTMLElement>;
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.modal = Modal.getOrCreateInstance(this.$widget); this.modal = Modal.getOrCreateInstance(this.$widget[0]);
this.$content = this.$widget.find(".recent-changes-content"); this.$content = this.$widget.find(".recent-changes-content");
this.$eraseDeletedNotesNow = this.$widget.find(".erase-deleted-notes-now-button"); this.$eraseDeletedNotesNow = this.$widget.find(".erase-deleted-notes-now-button");
@ -44,7 +57,7 @@ export default class RecentChangesDialog extends BasicWidget {
}); });
} }
async showRecentChangesEvent({ ancestorNoteId }) { async showRecentChangesEvent({ ancestorNoteId }: EventData<"showRecentChanges">) {
this.ancestorNoteId = ancestorNoteId; this.ancestorNoteId = ancestorNoteId;
await this.refresh(); await this.refresh();
@ -57,7 +70,7 @@ export default class RecentChangesDialog extends BasicWidget {
this.ancestorNoteId = hoistedNoteService.getHoistedNoteId(); this.ancestorNoteId = hoistedNoteService.getHoistedNoteId();
} }
const recentChangesRows = await server.get(`recent-changes/${this.ancestorNoteId}`); const recentChangesRows = await server.get<RecentChangesRow[]>(`recent-changes/${this.ancestorNoteId}`);
// preload all notes into cache // preload all notes into cache
await froca.getNotes( await froca.getNotes(
@ -110,7 +123,7 @@ export default class RecentChangesDialog extends BasicWidget {
} }
} else { } else {
const note = await froca.getNote(change.noteId); const note = await froca.getNote(change.noteId);
const notePath = note.getBestNotePathString(); const notePath = note?.getBestNotePathString();
if (notePath) { if (notePath) {
$noteLink = await linkService.createLink(notePath, { $noteLink = await linkService.createLink(notePath, {
@ -118,7 +131,7 @@ export default class RecentChangesDialog extends BasicWidget {
showNotePath: true showNotePath: true
}); });
} else { } else {
$noteLink = $("<span>").text(note.title); $noteLink = $("<span>").text(note?.title ?? "");
} }
} }
@ -131,9 +144,7 @@ export default class RecentChangesDialog extends BasicWidget {
appContext.tabManager.getActiveContext().setNote(change.noteId); appContext.tabManager.getActiveContext().setNote(change.noteId);
} }
}) })
.addClass(() => { .toggleClass("deleted-note", !!change.current_isDeleted)
if (change.current_isDeleted) return "deleted-note";
})
.append($("<span>").text(formattedTime).attr("title", change.date)) .append($("<span>").text(formattedTime).attr("title", change.date))
.append($noteLink.addClass("note-title")) .append($noteLink.addClass("note-title"))
); );
@ -143,7 +154,7 @@ export default class RecentChangesDialog extends BasicWidget {
} }
} }
groupByDate(rows) { groupByDate(rows: RecentChangesRow[]) {
const groupedByDate = new Map(); const groupedByDate = new Map();
for (const row of rows) { for (const row of rows) {

View File

@ -1,3 +1,4 @@
import type { EventData } from "../../components/app_context.js";
import { t } from "../../services/i18n.js"; import { t } from "../../services/i18n.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import utils from "../../services/utils.js"; import utils from "../../services/utils.js";
@ -79,6 +80,10 @@ const TPL = `<div class="sort-child-notes-dialog modal mx-auto" tabindex="-1" ro
</div>`; </div>`;
export default class SortChildNotesDialog extends BasicWidget { export default class SortChildNotesDialog extends BasicWidget {
private parentNoteId?: string;
private $form!: JQuery<HTMLElement>;
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$form = this.$widget.find(".sort-child-notes-form"); this.$form = this.$widget.find(".sort-child-notes-form");
@ -96,7 +101,7 @@ export default class SortChildNotesDialog extends BasicWidget {
}); });
} }
async sortChildNotesEvent({ node }) { async sortChildNotesEvent({ node }: EventData<"sortChildNotes">) {
this.parentNoteId = node.data.noteId; this.parentNoteId = node.data.noteId;
utils.openDialog(this.$widget); utils.openDialog(this.$widget);

View File

@ -5,6 +5,7 @@ import importService from "../../services/import.js";
import options from "../../services/options.js"; import options from "../../services/options.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
import { Modal, Tooltip } from "bootstrap"; import { Modal, Tooltip } from "bootstrap";
import type { EventData } from "../../components/app_context.js";
const TPL = ` const TPL = `
<div class="upload-attachments-dialog modal fade mx-auto" tabindex="-1" role="dialog"> <div class="upload-attachments-dialog modal fade mx-auto" tabindex="-1" role="dialog">
@ -42,6 +43,15 @@ const TPL = `
</div>`; </div>`;
export default class UploadAttachmentsDialog extends BasicWidget { export default class UploadAttachmentsDialog extends BasicWidget {
private parentNoteId: string | null;
private modal!: bootstrap.Modal;
private $form!: JQuery<HTMLElement>;
private $noteTitle!: JQuery<HTMLElement>;
private $fileUploadInput!: JQuery<HTMLInputElement>;
private $uploadButton!: JQuery<HTMLElement>;
private $shrinkImagesCheckbox!: JQuery<HTMLElement>;
constructor() { constructor() {
super(); super();
@ -50,7 +60,7 @@ export default class UploadAttachmentsDialog extends BasicWidget {
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.modal = Modal.getOrCreateInstance(this.$widget); this.modal = Modal.getOrCreateInstance(this.$widget[0]);
this.$form = this.$widget.find(".upload-attachment-form"); this.$form = this.$widget.find(".upload-attachment-form");
this.$noteTitle = this.$widget.find(".upload-attachment-note-title"); this.$noteTitle = this.$widget.find(".upload-attachment-note-title");
@ -61,7 +71,9 @@ export default class UploadAttachmentsDialog extends BasicWidget {
this.$form.on("submit", () => { this.$form.on("submit", () => {
// disabling so that import is not triggered again. // disabling so that import is not triggered again.
this.$uploadButton.attr("disabled", "disabled"); this.$uploadButton.attr("disabled", "disabled");
if (this.parentNoteId) {
this.uploadAttachments(this.parentNoteId); this.uploadAttachments(this.parentNoteId);
}
return false; return false;
}); });
@ -73,12 +85,12 @@ export default class UploadAttachmentsDialog extends BasicWidget {
} }
}); });
Tooltip.getOrCreateInstance(this.$widget.find('[data-bs-toggle="tooltip"]'), { Tooltip.getOrCreateInstance(this.$widget.find('[data-bs-toggle="tooltip"]')[0], {
html: true html: true
}); });
} }
async showUploadAttachmentsDialogEvent({ noteId }) { async showUploadAttachmentsDialogEvent({ noteId }: EventData<"showUploadAttachmentsDialog">) {
this.parentNoteId = noteId; this.parentNoteId = noteId;
this.$fileUploadInput.val("").trigger("change"); // to trigger upload button disabling listener below this.$fileUploadInput.val("").trigger("change"); // to trigger upload button disabling listener below
@ -89,10 +101,12 @@ export default class UploadAttachmentsDialog extends BasicWidget {
utils.openDialog(this.$widget); utils.openDialog(this.$widget);
} }
async uploadAttachments(parentNoteId) { async uploadAttachments(parentNoteId: string) {
const files = Array.from(this.$fileUploadInput[0].files); // shallow copy since we're resetting the upload button below const files = Array.from(this.$fileUploadInput[0].files ?? []); // shallow copy since we're resetting the upload button below
const boolToString = ($el) => ($el.is(":checked") ? "true" : "false"); function boolToString($el: JQuery<HTMLElement>): "true" | "false" {
return ($el.is(":checked") ? "true" : "false");
}
const options = { const options = {
shrinkImages: boolToString(this.$shrinkImagesCheckbox) shrinkImages: boolToString(this.$shrinkImagesCheckbox)