chore(react/type_widget): port attachment detail

This commit is contained in:
Elian Doran
2025-09-21 10:56:10 +03:00
parent 3171413a18
commit ae1954c320
6 changed files with 67 additions and 162 deletions

View File

@@ -18,7 +18,7 @@ import Image from "./type_widgets/Image";
import { ReadOnlyCode, EditableCode } from "./type_widgets/code/Code";
import Mermaid from "./type_widgets/Mermaid";
import MindMap from "./type_widgets/MindMap";
import { AttachmentList } from "./type_widgets/Attachment";
import { AttachmentDetail, AttachmentList } from "./type_widgets/Attachment";
/**
* A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one,
@@ -100,6 +100,7 @@ function getCorrespondingWidget(noteType: ExtendedNoteType | undefined, props: T
case "mermaid": return <Mermaid {...props} />
case "mindMap": return <MindMap {...props} />
case "attachmentList": return <AttachmentList {...props} />
case "attachmentDetail": return <AttachmentDetail {...props} />
default: break;
}
}

View File

@@ -95,16 +95,4 @@ export default class AttachmentDetailWidget extends BasicWidget {
throw new Error(t("attachment_detail_2.unrecognized_role", { role: this.attachment.role }));
}
}
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
const attachmentRow = loadResults.getAttachmentRows().find((att) => att.attachmentId === this.attachment.attachmentId);
if (attachmentRow) {
if (attachmentRow.isDeleted) {
this.toggleInt(false);
} else {
this.refresh();
}
}
}
}

View File

@@ -13,7 +13,7 @@
}
/* #endregion */
/* #region Attachment detail */
/* #region Attachment info */
.attachment-detail-widget {
height: 100%;
}
@@ -83,3 +83,22 @@
filter: contrast(10%);
}
/* #endregion */
/* #region Attachment detail */
.attachment-detail {
padding-left: 15px;
padding-right: 15px;
height: 100%;
display: flex;
flex-direction: column;
}
.attachment-detail .links-wrapper {
font-size: larger;
padding: 0 0 16px 0;
}
.attachment-detail .attachment-wrapper {
flex-grow: 1;
}
/* #endregion */

View File

@@ -11,7 +11,11 @@ import Alert from "../react/Alert";
import utils from "../../services/utils";
import content_renderer from "../../services/content_renderer";
import { useTriliumEvent } from "../react/hooks";
import froca from "../../services/froca";
/**
* Displays the full list of attachments of a note and allows the user to interact with them.
*/
export function AttachmentList({ note }: TypeWidgetProps) {
const [ attachments, setAttachments ] = useState<FAttachment[]>([]);
@@ -33,7 +37,7 @@ export function AttachmentList({ note }: TypeWidgetProps) {
<div className="attachment-list-wrapper">
{attachments.length ? (
attachments.map(attachment => <AttachmentDetail key={attachment.attachmentId} attachment={attachment} />)
attachments.map(attachment => <AttachmentInfo key={attachment.attachmentId} attachment={attachment} />)
) : (
<Alert type="info">
{t("attachment_list.no_attachments")}
@@ -69,7 +73,46 @@ function AttachmentListHeader({ noteId }: { noteId: string }) {
)
}
function AttachmentDetail({ attachment, isFullDetail }: { attachment: FAttachment, isFullDetail?: boolean }) {
/**
* Displays information about a single attachment.
*/
export function AttachmentDetail({ note, viewScope }: TypeWidgetProps) {
const [ attachment, setAttachment ] = useState<FAttachment | null | undefined>(undefined);
useEffect(() => {
if (!viewScope?.attachmentId) return;
froca.getAttachment(viewScope.attachmentId).then(setAttachment);
}, [ viewScope ]);
return (
<div className="attachment-detail note-detail-printable">
<div className="links-wrapper use-tn-links">
{t("attachment_detail.owning_note")}{" "}
<NoteLink notePath={note.noteId} />
{t("attachment_detail.you_can_also_open")}{" "}
<NoteLink
notePath={note.noteId}
viewScope={{ viewMode: "attachments" }}
title={t("attachment_detail.list_of_all_attachments")}
/>
<HelpButton
helpPage="0vhv7lsOLy82"
title={t("attachment_list.open_help_page")}
/>
</div>
<div className="attachment-wrapper">
{attachment !== null ? (
attachment && <AttachmentInfo attachment={attachment} isFullDetail />
) : (
<strong>{t("attachment_detail.attachment_deleted")}</strong>
)}
</div>
</div>
)
}
function AttachmentInfo({ attachment, isFullDetail }: { attachment: FAttachment, isFullDetail?: boolean }) {
const contentWrapper = useRef<HTMLDivElement>(null);
useEffect(() => {

View File

@@ -1,101 +0,0 @@
import TypeWidget from "./type_widget.js";
import AttachmentDetailWidget from "../attachment_detail.js";
import linkService from "../../services/link.js";
import froca from "../../services/froca.js";
import utils from "../../services/utils.js";
import { t } from "../../services/i18n.js";
import type FNote from "../../entities/fnote.js";
import type { EventData } from "../../components/app_context.js";
const TPL = /*html*/`
<div class="attachment-detail note-detail-printable">
<style>
.attachment-detail {
padding-left: 15px;
padding-right: 15px;
height: 100%;
display: flex;
flex-direction: column;
}
.attachment-detail .links-wrapper {
font-size: larger;
padding: 0 0 16px 0;
}
.attachment-detail .attachment-wrapper {
flex-grow: 1;
}
</style>
<div class="links-wrapper use-tn-links"></div>
<div class="attachment-wrapper"></div>
</div>`;
export default class AttachmentDetailTypeWidget extends TypeWidget {
$wrapper!: JQuery<HTMLElement>;
$linksWrapper!: JQuery<HTMLElement>;
static getType() {
return "attachmentDetail";
}
doRender() {
this.$widget = $(TPL);
this.$wrapper = this.$widget.find(".attachment-wrapper");
this.$linksWrapper = this.$widget.find(".links-wrapper");
super.doRender();
}
async doRefresh(note: Parameters<TypeWidget["doRefresh"]>[0]) {
this.$wrapper.empty();
this.children = [];
const $helpButton = $(`
<button class="attachment-help-button icon-action bx bx-help-circle"
type="button" data-help-page="attachments.html"
title="${t("attachment_detail.open_help_page")}"
</button>
`);
utils.initHelpButtons($helpButton);
this.$linksWrapper.empty().append(
t("attachment_detail.owning_note"),
await linkService.createLink(this.noteId),
t("attachment_detail.you_can_also_open"),
await linkService.createLink(this.noteId, {
title: t("attachment_detail.list_of_all_attachments"),
viewScope: {
viewMode: "attachments"
}
}),
$helpButton
);
const attachment = this.attachmentId ? await froca.getAttachment(this.attachmentId, true) : null;
if (!attachment) {
this.$wrapper.html("<strong>" + t("attachment_detail.attachment_deleted") + "</strong>");
return;
}
const attachmentDetailWidget = new AttachmentDetailWidget(attachment, true);
this.child(attachmentDetailWidget);
this.$wrapper.append(attachmentDetailWidget.render());
}
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
const attachmentRow = loadResults.getAttachmentRows().find((att) => att.attachmentId === this.attachmentId);
if (attachmentRow?.isDeleted) {
this.refresh(); // all other updates are handled within AttachmentDetailWidget
}
}
get attachmentId() {
return this?.noteContext?.viewScope?.attachmentId;
}
}

View File

@@ -1,45 +0,0 @@
import TypeWidget from "./type_widget.js";
import AttachmentDetailWidget from "../attachment_detail.js";
import linkService from "../../services/link.js";
import utils from "../../services/utils.js";
import { t } from "../../services/i18n.js";
import type { EventData } from "../../components/app_context.js";
const TPL = /*html*/`
`;
export default class AttachmentListTypeWidget extends TypeWidget {
$list!: JQuery<HTMLElement>;
$linksWrapper!: JQuery<HTMLElement>;
renderedAttachmentIds!: Set<string>;
static getType() {
return "attachmentList";
}
doRender() {
this.$widget = $(TPL);
this.$list = this.$widget.find(".attachment-list-wrapper");
this.$linksWrapper = this.$widget.find(".links-wrapper");
super.doRender();
}
async doRefresh(note: Parameters<TypeWidget["doRefresh"]>[0]) {
this.$list.empty();
this.children = [];
this.renderedAttachmentIds = new Set();
const attachments = await note.getAttachments();
for (const attachment of attachments) {
const attachmentDetailWidget = new AttachmentDetailWidget(attachment, false);
this.child(attachmentDetailWidget);
this.renderedAttachmentIds.add(attachment.attachmentId);
this.$list.append(attachmentDetailWidget.render());
}
}
}