mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
feat(react/dialogs): port delete_notes
This commit is contained in:
parent
a4e6a964c9
commit
87d9ea06f3
@ -87,9 +87,9 @@
|
||||
"delete_all_clones_description": "同时删除所有克隆(可以在最近修改中撤消)",
|
||||
"erase_notes_description": "通常(软)删除仅标记笔记为已删除,可以在一段时间内通过最近修改对话框撤消。选中此选项将立即擦除笔记,不可撤销。",
|
||||
"erase_notes_warning": "永久擦除笔记(无法撤销),包括所有克隆。这将强制应用程序重载。",
|
||||
"notes_to_be_deleted": "将删除以下笔记 ({{- noteCount}})",
|
||||
"notes_to_be_deleted": "将删除以下笔记 ({{notesCount}})",
|
||||
"no_note_to_delete": "没有笔记将被删除(仅克隆)。",
|
||||
"broken_relations_to_be_deleted": "将删除以下关系并断开连接 ({{- relationCount}})",
|
||||
"broken_relations_to_be_deleted": "将删除以下关系并断开连接 ({{ relationCount}})",
|
||||
"cancel": "取消",
|
||||
"ok": "确定",
|
||||
"deleted_relation_text": "笔记 {{- note}} (将被删除的笔记) 被以下关系 {{- relation}} 引用, 来自 {{- source}}。"
|
||||
|
@ -87,9 +87,9 @@
|
||||
"delete_all_clones_description": "auch alle Klone löschen (kann bei letzte Änderungen rückgängig gemacht werden)",
|
||||
"erase_notes_description": "Beim normalen (vorläufigen) Löschen werden die Notizen nur als gelöscht markiert und sie können innerhalb eines bestimmten Zeitraums (im Dialogfeld „Letzte Änderungen“) wiederhergestellt werden. Wenn du diese Option aktivierst, werden die Notizen sofort gelöscht und es ist nicht möglich, die Notizen wiederherzustellen.",
|
||||
"erase_notes_warning": "Notizen dauerhaft löschen (kann nicht rückgängig gemacht werden), einschließlich aller Klone. Dadurch wird ein Neuladen der Anwendung erzwungen.",
|
||||
"notes_to_be_deleted": "Folgende Notizen werden gelöscht (<span class=\"deleted-notes-count\"></span>)",
|
||||
"notes_to_be_deleted": "Folgende Notizen werden gelöscht ({{notesCount}})",
|
||||
"no_note_to_delete": "Es werden keine Notizen gelöscht (nur Klone).",
|
||||
"broken_relations_to_be_deleted": "Folgende Beziehungen werden gelöst und gelöscht (<span class=\"broke-relations-count\"></span>)",
|
||||
"broken_relations_to_be_deleted": "Folgende Beziehungen werden gelöst und gelöscht ({{ relationCount}})",
|
||||
"cancel": "Abbrechen",
|
||||
"ok": "OK",
|
||||
"deleted_relation_text": "Notiz {{- note}} (soll gelöscht werden) wird von Beziehung {{- relation}} ausgehend von {{- source}} referenziert."
|
||||
|
@ -87,9 +87,9 @@
|
||||
"delete_all_clones_description": "Delete also all clones (can be undone in recent changes)",
|
||||
"erase_notes_description": "Normal (soft) deletion only marks the notes as deleted and they can be undeleted (in recent changes dialog) within a period of time. Checking this option will erase the notes immediately and it won't be possible to undelete the notes.",
|
||||
"erase_notes_warning": "Erase notes permanently (can't be undone), including all clones. This will force application reload.",
|
||||
"notes_to_be_deleted": "Following notes will be deleted ({{- noteCount}})",
|
||||
"notes_to_be_deleted": "Following notes will be deleted ({{notesCount}})",
|
||||
"no_note_to_delete": "No note will be deleted (only clones).",
|
||||
"broken_relations_to_be_deleted": "Following relations will be broken and deleted ({{- relationCount}})",
|
||||
"broken_relations_to_be_deleted": "Following relations will be broken and deleted ({{ relationCount}})",
|
||||
"cancel": "Cancel",
|
||||
"ok": "OK",
|
||||
"deleted_relation_text": "Note {{- note}} (to be deleted) is referenced by relation {{- relation}} originating from {{- source}}."
|
||||
|
@ -87,9 +87,9 @@
|
||||
"delete_all_clones_description": "Eliminar también todos los clones (se puede deshacer en cambios recientes)",
|
||||
"erase_notes_description": "La eliminación normal (suave) solo marca las notas como eliminadas y se pueden recuperar (en el cuadro de diálogo de cambios recientes) dentro de un periodo de tiempo. Al marcar esta opción se borrarán las notas inmediatamente y no será posible recuperarlas.",
|
||||
"erase_notes_warning": "Eliminar notas permanentemente (no se puede deshacer), incluidos todos los clones. Esto forzará la recarga de la aplicación.",
|
||||
"notes_to_be_deleted": "Las siguientes notas serán eliminadas ({{- noteCount}})",
|
||||
"notes_to_be_deleted": "Las siguientes notas serán eliminadas ({{notesCount}})",
|
||||
"no_note_to_delete": "No se eliminará ninguna nota (solo clones).",
|
||||
"broken_relations_to_be_deleted": "Las siguientes relaciones se romperán y serán eliminadas ({{- relationCount}})",
|
||||
"broken_relations_to_be_deleted": "Las siguientes relaciones se romperán y serán eliminadas ({{ relationCount}})",
|
||||
"cancel": "Cancelar",
|
||||
"ok": "Aceptar",
|
||||
"deleted_relation_text": "Nota {{- note}} (para ser eliminada) está referenciado por la relación {{- relation}} que se origina en {{- source}}."
|
||||
|
@ -87,9 +87,9 @@
|
||||
"delete_all_clones_description": "Supprimer aussi les clones (peut être annulé dans des modifications récentes)",
|
||||
"erase_notes_description": "La suppression normale (douce) marque uniquement les notes comme supprimées et elles peuvent être restaurées (dans la boîte de dialogue des Modifications récentes) dans un délai donné. Cocher cette option effacera les notes immédiatement et il ne sera pas possible de les restaurer.",
|
||||
"erase_notes_warning": "Efface les notes de manière permanente (ne peut pas être annulée), y compris les clones. L'application va être rechargée.",
|
||||
"notes_to_be_deleted": "Les notes suivantes seront supprimées ({{- noteCount}})",
|
||||
"notes_to_be_deleted": "Les notes suivantes seront supprimées ({{notesCount}})",
|
||||
"no_note_to_delete": "Aucune note ne sera supprimée (uniquement les clones).",
|
||||
"broken_relations_to_be_deleted": "Les relations suivantes seront rompues et supprimées ({{- relationCount}})",
|
||||
"broken_relations_to_be_deleted": "Les relations suivantes seront rompues et supprimées ({{ relationCount}})",
|
||||
"cancel": "Annuler",
|
||||
"ok": "OK",
|
||||
"deleted_relation_text": "Note {{- note}} (à supprimer) est référencée dans la relation {{- relation}} provenant de {{- source}}."
|
||||
|
@ -436,14 +436,14 @@
|
||||
"undelete_notes_instruction": "După ștergere, se pot recupera din ecranul Schimbări recente."
|
||||
},
|
||||
"delete_notes": {
|
||||
"broken_relations_to_be_deleted": "Următoarele relații vor fi întrerupte și șterse ({{- relationCount}})",
|
||||
"broken_relations_to_be_deleted": "Următoarele relații vor fi întrerupte și șterse ({{ relationCount}})",
|
||||
"cancel": "Anulează",
|
||||
"delete_all_clones_description": "Șterge și toate clonele (se pot recupera în ecranul Schimbări recente)",
|
||||
"delete_notes_preview": "Previzualizare ștergerea notițelor",
|
||||
"erase_notes_description": "Ștergerea obișnuită doar marchează notițele ca fiind șterse și pot fi recuperate (în ecranul Schimbări recente) pentru o perioadă de timp. Dacă se bifează această opțiune, notițele vor fi șterse imediat fără posibilitatea de a le recupera.",
|
||||
"erase_notes_warning": "Șterge notițele permanent (nu se mai pot recupera), incluzând toate clonele. Va forța reîncărcarea aplicației.",
|
||||
"no_note_to_delete": "Nicio notiță nu va fi ștearsă (doar clonele).",
|
||||
"notes_to_be_deleted": "Următoarele notițe vor fi șterse ({{- noteCount}})",
|
||||
"notes_to_be_deleted": "Următoarele notițe vor fi șterse ({{notesCount}})",
|
||||
"ok": "OK",
|
||||
"deleted_relation_text": "Notița {{- note}} ce va fi ștearsă este referențiată de relația {{- relation}}, originând din {{- source}}.",
|
||||
"close": "Închide"
|
||||
|
@ -83,9 +83,9 @@
|
||||
"delete_all_clones_description": "同時刪除所有複製(可以在最近修改中撤消)",
|
||||
"erase_notes_description": "通常(軟)刪除僅標記筆記為已刪除,可以在一段時間內通過最近修改對話框撤消。選中此選項將立即擦除筆記,無法撤銷。",
|
||||
"erase_notes_warning": "永久擦除筆記(無法撤銷),包括所有複製。這將強制應用程式重新加載。",
|
||||
"notes_to_be_deleted": "將刪除以下筆記 ({{- noteCount}})",
|
||||
"notes_to_be_deleted": "將刪除以下筆記 ({{notesCount}})",
|
||||
"no_note_to_delete": "沒有筆記將被刪除(僅複製)。",
|
||||
"broken_relations_to_be_deleted": "將刪除以下關係並斷開連接 ({{- relationCount}})",
|
||||
"broken_relations_to_be_deleted": "將刪除以下關係並斷開連接 ({{ relationCount}})",
|
||||
"cancel": "取消",
|
||||
"ok": "確定",
|
||||
"deleted_relation_text": "筆記 {{- note}} (將被刪除的筆記) 被以下關係 {{- relation}} 引用, 來自 {{- source}}。"
|
||||
|
@ -1,198 +0,0 @@
|
||||
import server from "../../services/server.js";
|
||||
import froca from "../../services/froca.js";
|
||||
import linkService from "../../services/link.js";
|
||||
import BasicWidget from "../basic_widget.js";
|
||||
import { t } from "../../services/i18n.js";
|
||||
import type { FAttributeRow } from "../../entities/fattribute.js";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
|
||||
|
||||
// TODO: Use common with server.
|
||||
interface Response {
|
||||
noteIdsToBeDeleted: string[];
|
||||
brokenRelations: FAttributeRow[];
|
||||
}
|
||||
|
||||
export interface ResolveOptions {
|
||||
proceed: boolean;
|
||||
deleteAllClones?: boolean;
|
||||
eraseNotes?: boolean;
|
||||
}
|
||||
|
||||
interface ShowDeleteNotesDialogOpts {
|
||||
branchIdsToDelete: string[];
|
||||
callback: (opts: ResolveOptions) => void;
|
||||
forceDeleteAllClones: boolean;
|
||||
}
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="delete-notes-dialog modal mx-auto" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-xl" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">${t("delete_notes.delete_notes_preview")}</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("delete_notes.close")}"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-checkbox">
|
||||
<label for="delete-all-clones" class="form-check-label tn-checkbox">
|
||||
<input id="delete-all-clones" class="delete-all-clones form-check-input" value="1" type="checkbox">
|
||||
${t("delete_notes.delete_all_clones_description")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-checkbox" style="margin-bottom: 1rem">
|
||||
<label for="erase-notes" class="form-check-label tn-checkbox">
|
||||
<input id="erase-notes" class="erase-notes form-check-input" value="1" type="checkbox">
|
||||
${t("delete_notes.erase_notes_warning")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="delete-notes-list-wrapper">
|
||||
<h4>${t("delete_notes.notes_to_be_deleted", { noteCount: '<span class="deleted-notes-count"></span>' })}</h4>
|
||||
|
||||
<ul class="delete-notes-list" style="max-height: 200px; overflow: auto;"></ul>
|
||||
</div>
|
||||
|
||||
<div class="no-note-to-delete-wrapper alert alert-info">
|
||||
${t("delete_notes.no_note_to_delete")}
|
||||
</div>
|
||||
|
||||
<div class="broken-relations-wrapper">
|
||||
<div class="alert alert-danger">
|
||||
<h4>${t("delete_notes.broken_relations_to_be_deleted", { relationCount: '<span class="broke-relations-count"></span>' })}</h4>
|
||||
|
||||
<ul class="broken-relations-list" style="max-height: 200px; overflow: auto;"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="delete-notes-dialog-cancel-button btn btn-sm">${t("delete_notes.cancel")}</button>
|
||||
|
||||
|
||||
|
||||
<button class="delete-notes-dialog-ok-button btn btn-primary btn-sm">${t("delete_notes.ok")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
export default class DeleteNotesDialog extends BasicWidget {
|
||||
private branchIds: string[] | null;
|
||||
private resolve!: (options: ResolveOptions) => void;
|
||||
|
||||
private $content!: JQuery<HTMLElement>;
|
||||
private $okButton!: JQuery<HTMLElement>;
|
||||
private $cancelButton!: JQuery<HTMLElement>;
|
||||
private $deleteNotesList!: JQuery<HTMLElement>;
|
||||
private $brokenRelationsList!: JQuery<HTMLElement>;
|
||||
private $deletedNotesCount!: JQuery<HTMLElement>;
|
||||
private $noNoteToDeleteWrapper!: JQuery<HTMLElement>;
|
||||
private $deleteNotesListWrapper!: JQuery<HTMLElement>;
|
||||
private $brokenRelationsListWrapper!: JQuery<HTMLElement>;
|
||||
private $brokenRelationsCount!: JQuery<HTMLElement>;
|
||||
private $deleteAllClones!: JQuery<HTMLElement>;
|
||||
private $eraseNotes!: JQuery<HTMLElement>;
|
||||
|
||||
private forceDeleteAllClones?: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.branchIds = null;
|
||||
}
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.$content = this.$widget.find(".recent-changes-content");
|
||||
this.$okButton = this.$widget.find(".delete-notes-dialog-ok-button");
|
||||
this.$cancelButton = this.$widget.find(".delete-notes-dialog-cancel-button");
|
||||
this.$deleteNotesList = this.$widget.find(".delete-notes-list");
|
||||
this.$brokenRelationsList = this.$widget.find(".broken-relations-list");
|
||||
this.$deletedNotesCount = this.$widget.find(".deleted-notes-count");
|
||||
this.$noNoteToDeleteWrapper = this.$widget.find(".no-note-to-delete-wrapper");
|
||||
this.$deleteNotesListWrapper = this.$widget.find(".delete-notes-list-wrapper");
|
||||
this.$brokenRelationsListWrapper = this.$widget.find(".broken-relations-wrapper");
|
||||
this.$brokenRelationsCount = this.$widget.find(".broke-relations-count");
|
||||
this.$deleteAllClones = this.$widget.find(".delete-all-clones");
|
||||
this.$eraseNotes = this.$widget.find(".erase-notes");
|
||||
|
||||
this.$widget.on("shown.bs.modal", () => this.$okButton.trigger("focus"));
|
||||
|
||||
this.$cancelButton.on("click", () => {
|
||||
closeActiveDialog();
|
||||
|
||||
this.resolve({ proceed: false });
|
||||
});
|
||||
|
||||
this.$okButton.on("click", () => {
|
||||
closeActiveDialog();
|
||||
|
||||
this.resolve({
|
||||
proceed: true,
|
||||
deleteAllClones: this.forceDeleteAllClones || this.isDeleteAllClonesChecked(),
|
||||
eraseNotes: this.isEraseNotesChecked()
|
||||
});
|
||||
});
|
||||
|
||||
this.$deleteAllClones.on("click", () => this.renderDeletePreview());
|
||||
}
|
||||
|
||||
async renderDeletePreview() {
|
||||
const response = await server.post<Response>("delete-notes-preview", {
|
||||
branchIdsToDelete: this.branchIds,
|
||||
deleteAllClones: this.forceDeleteAllClones || this.isDeleteAllClonesChecked()
|
||||
});
|
||||
|
||||
this.$deleteNotesList.empty();
|
||||
this.$brokenRelationsList.empty();
|
||||
|
||||
this.$deleteNotesListWrapper.toggle(response.noteIdsToBeDeleted.length > 0);
|
||||
this.$noNoteToDeleteWrapper.toggle(response.noteIdsToBeDeleted.length === 0);
|
||||
|
||||
for (const note of await froca.getNotes(response.noteIdsToBeDeleted)) {
|
||||
this.$deleteNotesList.append($("<li>").append(await linkService.createLink(note.noteId, { showNotePath: true })));
|
||||
}
|
||||
|
||||
this.$deletedNotesCount.text(response.noteIdsToBeDeleted.length);
|
||||
|
||||
this.$brokenRelationsListWrapper.toggle(response.brokenRelations.length > 0);
|
||||
this.$brokenRelationsCount.text(response.brokenRelations.length);
|
||||
|
||||
await froca.getNotes(response.brokenRelations.map((br) => br.noteId));
|
||||
|
||||
for (const attr of response.brokenRelations) {
|
||||
this.$brokenRelationsList.append(
|
||||
$("<li>").html(
|
||||
t("delete_notes.deleted_relation_text", {
|
||||
note: (await linkService.createLink(attr.value)).html(),
|
||||
relation: `<code>${attr.name}</code>`,
|
||||
source: (await linkService.createLink(attr.noteId)).html()
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async showDeleteNotesDialogEvent({ branchIdsToDelete, callback, forceDeleteAllClones }: ShowDeleteNotesDialogOpts) {
|
||||
this.branchIds = branchIdsToDelete;
|
||||
this.forceDeleteAllClones = forceDeleteAllClones;
|
||||
|
||||
await this.renderDeletePreview();
|
||||
|
||||
openDialog(this.$widget);
|
||||
|
||||
this.$deleteAllClones.prop("checked", !!forceDeleteAllClones).prop("disabled", !!forceDeleteAllClones);
|
||||
|
||||
this.$eraseNotes.prop("checked", false);
|
||||
|
||||
this.resolve = callback;
|
||||
}
|
||||
|
||||
isDeleteAllClonesChecked() {
|
||||
return this.$deleteAllClones.is(":checked");
|
||||
}
|
||||
|
||||
isEraseNotesChecked() {
|
||||
return this.$eraseNotes.is(":checked");
|
||||
}
|
||||
}
|
182
apps/client/src/widgets/dialogs/delete_notes.tsx
Normal file
182
apps/client/src/widgets/dialogs/delete_notes.tsx
Normal file
@ -0,0 +1,182 @@
|
||||
import { useRef, useState } from "preact/hooks";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
|
||||
import { t } from "../../services/i18n.js";
|
||||
import FormCheckbox from "../react/FormCheckbox.js";
|
||||
import Modal from "../react/Modal.js";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget.js";
|
||||
import { useEffect } from "react";
|
||||
import type { DeleteNotesPreview } from "@triliumnext/commons";
|
||||
import server from "../../services/server.js";
|
||||
import froca from "../../services/froca.js";
|
||||
import FNote from "../../entities/fnote.js";
|
||||
import link from "../../services/link.js";
|
||||
import Button from "../react/Button.jsx";
|
||||
|
||||
export interface ResolveOptions {
|
||||
proceed: boolean;
|
||||
deleteAllClones?: boolean;
|
||||
eraseNotes?: boolean;
|
||||
}
|
||||
|
||||
interface ShowDeleteNotesDialogOpts {
|
||||
branchIdsToDelete?: string[];
|
||||
callback?: (opts: ResolveOptions) => void;
|
||||
forceDeleteAllClones?: boolean;
|
||||
}
|
||||
|
||||
interface ShowDeleteNotesDialogProps extends ShowDeleteNotesDialogOpts { }
|
||||
|
||||
interface BrokenRelationData {
|
||||
note: string;
|
||||
relation: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
function DeleteNotesDialogComponent({ forceDeleteAllClones, branchIdsToDelete, callback }: ShowDeleteNotesDialogProps) {
|
||||
const [ deleteAllClones, setDeleteAllClones ] = useState(false);
|
||||
const [ eraseNotes, setEraseNotes ] = useState(!!forceDeleteAllClones);
|
||||
const [ brokenRelations, setBrokenRelations ] = useState<DeleteNotesPreview["brokenRelations"]>([]);
|
||||
const [ noteIdsToBeDeleted, setNoteIdsToBeDeleted ] = useState<DeleteNotesPreview["noteIdsToBeDeleted"]>([]);
|
||||
const [ proceed, setProceed ] = useState(false);
|
||||
const okButtonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!branchIdsToDelete || branchIdsToDelete.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
server.post<DeleteNotesPreview>("delete-notes-preview", {
|
||||
branchIdsToDelete,
|
||||
deleteAllClones: forceDeleteAllClones || deleteAllClones
|
||||
}).then(response => {
|
||||
setBrokenRelations(response.brokenRelations);
|
||||
setNoteIdsToBeDeleted(response.noteIdsToBeDeleted);
|
||||
});
|
||||
}, [ branchIdsToDelete, deleteAllClones, forceDeleteAllClones ]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="delete-notes-dialog"
|
||||
size="xl"
|
||||
scrollable
|
||||
title={t("delete_notes.delete_notes_preview")}
|
||||
onShown={() => okButtonRef.current?.focus()}
|
||||
onHidden={() => callback?.({ proceed, deleteAllClones, eraseNotes })}
|
||||
footer={<>
|
||||
<Button text={t("delete_notes.cancel")}
|
||||
onClick={() => closeActiveDialog()} />
|
||||
<Button text={t("delete_notes.ok")} primary
|
||||
buttonRef={okButtonRef}
|
||||
onClick={() => {
|
||||
setProceed(true);
|
||||
closeActiveDialog();
|
||||
}} />
|
||||
</>}
|
||||
>
|
||||
<FormCheckbox name="delete-all-clones" label={t("delete_notes.delete_all_clones_description")}
|
||||
currentValue={deleteAllClones} onChange={setDeleteAllClones}
|
||||
/>
|
||||
<FormCheckbox
|
||||
name="erase-notes" label={t("delete_notes.erase_notes_warning")}
|
||||
disabled={forceDeleteAllClones}
|
||||
currentValue={eraseNotes} onChange={setEraseNotes}
|
||||
/>
|
||||
|
||||
<DeletedNotes noteIdsToBeDeleted={noteIdsToBeDeleted} />
|
||||
<BrokenRelations brokenRelations={brokenRelations} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function DeletedNotes({ noteIdsToBeDeleted }: { noteIdsToBeDeleted: DeleteNotesPreview["noteIdsToBeDeleted"] }) {
|
||||
const [ noteLinks, setNoteLinks ] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
froca.getNotes(noteIdsToBeDeleted).then(async (notes: FNote[]) => {
|
||||
const noteLinks = [];
|
||||
|
||||
for (const note of notes) {
|
||||
noteLinks.push((await link.createLink(note.noteId, { showNotePath: true })).html());
|
||||
}
|
||||
|
||||
setNoteLinks(noteLinks);
|
||||
});
|
||||
}, [noteIdsToBeDeleted]);
|
||||
|
||||
return (
|
||||
<div className="delete-notes-list-wrapper">
|
||||
<h4>{t("delete_notes.notes_to_be_deleted", { notesCount: noteIdsToBeDeleted.length })}</h4>
|
||||
|
||||
<ul className="delete-notes-list" style={{ maxHeight: "200px", overflow: "auto" }}>
|
||||
{noteLinks.map((link, index) => (
|
||||
<li key={index} dangerouslySetInnerHTML={{ __html: link }} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BrokenRelations({ brokenRelations }: { brokenRelations: DeleteNotesPreview["brokenRelations"] }) {
|
||||
const [ notesWithBrokenRelations, setNotesWithBrokenRelations ] = useState<BrokenRelationData[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const noteIds = brokenRelations.map(relation => relation.noteId);
|
||||
froca.getNotes(noteIds).then(async (notes) => {
|
||||
const notesWithBrokenRelations: BrokenRelationData[] = [];
|
||||
for (const attr of brokenRelations) {
|
||||
notesWithBrokenRelations.push({
|
||||
note: (await link.createLink(attr.value)).html(),
|
||||
relation: `<code>${attr.name}</code>`,
|
||||
source: (await link.createLink(attr.noteId)).html()
|
||||
});
|
||||
}
|
||||
setNotesWithBrokenRelations(notesWithBrokenRelations);
|
||||
});
|
||||
}, [brokenRelations]);
|
||||
|
||||
if (brokenRelations.length) {
|
||||
return (
|
||||
<div className="broken-relations-wrapper">
|
||||
<div className="alert alert-danger">
|
||||
<h4>{t("delete_notes.broken_relations_to_be_deleted", { relationCount: brokenRelations.length })}</h4>
|
||||
|
||||
<ul className="broken-relations-list" style={{ maxHeight: "200px", overflow: "auto" }}>
|
||||
{brokenRelations.map((_, index) => {
|
||||
return (
|
||||
<li key={index}>
|
||||
<span dangerouslySetInnerHTML={{ __html: t("delete_notes.deleted_relation_text", notesWithBrokenRelations[index]) }} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="no-note-to-delete-wrapper alert alert-info">
|
||||
{t("delete_notes.no_note_to_delete")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class DeleteNotesDialog extends ReactBasicWidget {
|
||||
|
||||
private props: ShowDeleteNotesDialogProps = {};
|
||||
|
||||
get component() {
|
||||
return <DeleteNotesDialogComponent {...this.props} />;
|
||||
}
|
||||
|
||||
async showDeleteNotesDialogEvent({ branchIdsToDelete, callback, forceDeleteAllClones }: ShowDeleteNotesDialogOpts) {
|
||||
this.props = {
|
||||
branchIdsToDelete,
|
||||
callback,
|
||||
forceDeleteAllClones
|
||||
};
|
||||
this.doRender();
|
||||
openDialog(this.$widget);
|
||||
}
|
||||
|
||||
}
|
@ -10,11 +10,16 @@ interface ButtonProps {
|
||||
keyboardShortcut?: string;
|
||||
/** Called when the button is clicked. If not set, the button will submit the form (if any). */
|
||||
onClick?: () => void;
|
||||
primary?: boolean;
|
||||
}
|
||||
|
||||
export default function Button({ buttonRef: _buttonRef, className, text, onClick, keyboardShortcut, icon }: ButtonProps) {
|
||||
export default function Button({ buttonRef: _buttonRef, className, text, onClick, keyboardShortcut, icon, primary }: ButtonProps) {
|
||||
const classes: string[] = ["btn"];
|
||||
if (primary) {
|
||||
classes.push("btn-primary");
|
||||
} else {
|
||||
classes.push("btn-secondary");
|
||||
}
|
||||
if (className) {
|
||||
classes.push(className);
|
||||
}
|
||||
|
@ -6,10 +6,11 @@ interface FormCheckboxProps {
|
||||
*/
|
||||
hint?: string;
|
||||
currentValue: boolean;
|
||||
disabled?: boolean;
|
||||
onChange(newValue: boolean): void;
|
||||
}
|
||||
|
||||
export default function FormCheckbox({ name, label, currentValue, onChange, hint }: FormCheckboxProps) {
|
||||
export default function FormCheckbox({ name, disabled, label, currentValue, onChange, hint }: FormCheckboxProps) {
|
||||
return (
|
||||
<div className="form-checkbox">
|
||||
<label
|
||||
@ -21,6 +22,7 @@ export default function FormCheckbox({ name, label, currentValue, onChange, hint
|
||||
name={name}
|
||||
checked={currentValue || false}
|
||||
value="1"
|
||||
disabled={disabled}
|
||||
onChange={e => onChange((e.target as HTMLInputElement).checked)} />
|
||||
{label}
|
||||
</label>
|
||||
|
@ -6,7 +6,7 @@ import type { CSSProperties } from "preact/compat";
|
||||
interface ModalProps {
|
||||
className: string;
|
||||
title: string | ComponentChildren;
|
||||
size: "lg" | "md" | "sm";
|
||||
size: "xl" | "lg" | "md" | "sm";
|
||||
children: ComponentChildren;
|
||||
footer?: ComponentChildren;
|
||||
footerAlignment?: "right" | "between";
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { AttributeRow } from "./rows.js";
|
||||
|
||||
export interface AppInfo {
|
||||
appVersion: string;
|
||||
dbVersion: number;
|
||||
@ -10,3 +12,8 @@ export interface AppInfo {
|
||||
/** for timezone inference */
|
||||
utcDateTime: string;
|
||||
}
|
||||
|
||||
export interface DeleteNotesPreview {
|
||||
noteIdsToBeDeleted: string[];
|
||||
brokenRelations: AttributeRow[];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user