diff --git a/apps/client/src/widgets/attachment_detail.ts b/apps/client/src/widgets/attachment_detail.ts index 1d2eab902..9030245d8 100644 --- a/apps/client/src/widgets/attachment_detail.ts +++ b/apps/client/src/widgets/attachment_detail.ts @@ -11,89 +11,8 @@ import type FAttachment from "../entities/fattachment.js"; import type { EventData } from "../components/app_context.js"; const TPL = /*html*/` -
- - -
-
-
-

-
-
-
-
-
`; @@ -125,21 +44,6 @@ export default class AttachmentDetailWidget extends BasicWidget { this.$wrapper = this.$widget.find(".attachment-detail-wrapper"); this.$wrapper.addClass(this.isFullDetail ? "full-detail" : "list-view"); - if (!this.isFullDetail) { - const $link = await linkService.createLink(this.attachment.ownerId, { - title: this.attachment.title, - viewScope: { - viewMode: "attachments", - attachmentId: this.attachment.attachmentId - } - }); - $link.addClass("use-tn-links"); - - this.$wrapper.find(".attachment-title").append($link); - } else { - this.$wrapper.find(".attachment-title").text(this.attachment.title); - } - const $deletionWarning = this.$wrapper.find(".attachment-deletion-warning"); const { utcDateScheduledForErasureSince } = this.attachment; @@ -166,10 +70,9 @@ export default class AttachmentDetailWidget extends BasicWidget { $deletionWarning.hide(); } - this.$wrapper.find(".attachment-details").text(t("attachment_detail_2.role_and_size", { role: this.attachment.role, size: utils.formatSize(this.attachment.contentLength) })); this.$wrapper.find(".attachment-actions-container").append(this.attachmentActionsWidget.render()); - const { $renderedContent } = await contentRenderer.getRenderedContent(this.attachment, { imageHasZoom: this.isFullDetail }); + const { $renderedContent } = await ); this.$wrapper.find(".attachment-content-wrapper").append($renderedContent); } diff --git a/apps/client/src/widgets/react/NoteLink.tsx b/apps/client/src/widgets/react/NoteLink.tsx index 2a9ec199d..18cd2a0c2 100644 --- a/apps/client/src/widgets/react/NoteLink.tsx +++ b/apps/client/src/widgets/react/NoteLink.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from "preact/hooks"; -import link from "../../services/link"; +import link, { ViewScope } from "../../services/link"; import { useImperativeSearchHighlighlighting } from "./hooks"; interface NoteLinkOpts { @@ -11,18 +11,25 @@ interface NoteLinkOpts { noPreview?: boolean; noTnLink?: boolean; highlightedTokens?: string[] | null | undefined; + // Override the text of the link, otherwise the note title is used. + title?: string; + viewScope?: ViewScope; } -export default function NoteLink({ className, notePath, showNotePath, showNoteIcon, style, noPreview, noTnLink, highlightedTokens }: NoteLinkOpts) { +export default function NoteLink({ className, notePath, showNotePath, showNoteIcon, style, noPreview, noTnLink, highlightedTokens, title, viewScope }: NoteLinkOpts) { const stringifiedNotePath = Array.isArray(notePath) ? notePath.join("/") : notePath; const ref = useRef(null); const [ jqueryEl, setJqueryEl ] = useState>(); const highlightSearch = useImperativeSearchHighlighlighting(highlightedTokens); useEffect(() => { - link.createLink(stringifiedNotePath, { showNotePath, showNoteIcon }) - .then(setJqueryEl); - }, [ stringifiedNotePath, showNotePath ]); + link.createLink(stringifiedNotePath, { + title, + showNotePath, + showNoteIcon, + viewScope + }).then(setJqueryEl); + }, [ stringifiedNotePath, showNotePath, title, viewScope ]); useEffect(() => { if (!ref.current || !jqueryEl) return; diff --git a/apps/client/src/widgets/type_widgets/Attachment.css b/apps/client/src/widgets/type_widgets/Attachment.css index 56a9b32e6..499f10508 100644 --- a/apps/client/src/widgets/type_widgets/Attachment.css +++ b/apps/client/src/widgets/type_widgets/Attachment.css @@ -11,4 +11,75 @@ justify-content: space-between; align-items: baseline; } +/* #endregion */ + +/* #region Attachment detail */ +.attachment-detail-widget { + height: 100%; +} + +.attachment-detail-wrapper { + margin-bottom: 20px; + display: flex; + flex-direction: column; +} + +.attachment-title-line { + display: flex; + align-items: baseline; + gap: 1em; +} + +.attachment-details { + margin-left: 10px; +} + +.attachment-content-wrapper { + flex-grow: 1; +} + +.attachment-content-wrapper .rendered-content { + height: 100%; +} + +.attachment-content-wrapper pre { + padding: 10px; + margin-top: 10px; + margin-bottom: 10px; +} + +.attachment-detail-wrapper.list-view .attachment-content-wrapper { + max-height: 300px; +} + +.attachment-detail-wrapper.full-detail { + height: 100%; +} + +.attachment-detail-wrapper.full-detail .attachment-content-wrapper { + height: 100%; +} + +.attachment-detail-wrapper.list-view .attachment-content-wrapper pre { + max-height: 400px; +} + +.attachment-content-wrapper img { + margin: 10px; +} + +.attachment-detail-wrapper.list-view .attachment-content-wrapper img, .attachment-detail-wrapper.list-view .attachment-content-wrapper video { + max-height: 300px; + max-width: 90%; + object-fit: contain; +} + +.attachment-detail-wrapper.full-detail .attachment-content-wrapper img { + max-width: 90%; + object-fit: contain; +} + +.attachment-detail-wrapper.scheduled-for-deletion .attachment-content-wrapper img { + filter: contrast(10%); +} /* #endregion */ \ No newline at end of file diff --git a/apps/client/src/widgets/type_widgets/Attachment.tsx b/apps/client/src/widgets/type_widgets/Attachment.tsx index a2577e7ab..3169c53a8 100644 --- a/apps/client/src/widgets/type_widgets/Attachment.tsx +++ b/apps/client/src/widgets/type_widgets/Attachment.tsx @@ -3,14 +3,36 @@ import { TypeWidgetProps } from "./type_widget"; import "./Attachment.css"; import NoteLink from "../react/NoteLink"; import Button from "../react/Button"; -import { useContext } from "preact/hooks"; +import { useContext, useEffect, useRef, useState } from "preact/hooks"; import { ParentComponent } from "../react/react_utils"; import HelpButton from "../react/HelpButton"; +import FAttachment from "../../entities/fattachment"; +import Alert from "../react/Alert"; +import utils from "../../services/utils"; +import content_renderer from "../../services/content_renderer"; export function AttachmentList({ note }: TypeWidgetProps) { + const [ attachments, setAttachments ] = useState([]); + + function refresh() { + note.getAttachments().then(setAttachments); + } + + useEffect(refresh, [ note ]); + return (
+ +
+ {attachments.length ? ( + attachments.map(attachment => ) + ) : ( + + {t("attachment_list.no_attachments")} + + )} +
) } @@ -39,3 +61,42 @@ function AttachmentListHeader({ noteId }: { noteId: string }) { ) } + +function AttachmentDetail({ attachment, isFullDetail }: { attachment: FAttachment, isFullDetail: boolean }) { + const contentWrapper = useRef(null); + + useEffect(() => { + content_renderer.getRenderedContent(attachment, { imageHasZoom: isFullDetail }) + .then(({ $renderedContent }) => { + contentWrapper.current?.replaceChildren(...$renderedContent); + }) + }, [ attachment ]); + + return ( +
+
+
+
+

+ {!isFullDetail ? ( + + ) : (attachment.title)} +

+
+ {t("attachment_detail_2.role_and_size", { role: attachment.role, size: utils.formatSize(attachment.contentLength) })} +
+
+
+ +
+
+
+ ) +} diff --git a/apps/client/src/widgets/type_widgets_old/attachment_list.ts b/apps/client/src/widgets/type_widgets_old/attachment_list.ts index 8f1037631..318ddf457 100644 --- a/apps/client/src/widgets/type_widgets_old/attachment_list.ts +++ b/apps/client/src/widgets/type_widgets_old/attachment_list.ts @@ -6,7 +6,6 @@ import { t } from "../../services/i18n.js"; import type { EventData } from "../../components/app_context.js"; const TPL = /*html*/` -
`; export default class AttachmentListTypeWidget extends TypeWidget { @@ -27,28 +26,12 @@ export default class AttachmentListTypeWidget extends TypeWidget { } async doRefresh(note: Parameters[0]) { - const $helpButton = $(` - - `); - utils.initHelpButtons($helpButton); - - const noteLink = await linkService.createLink(this.noteId); // do separately to avoid race condition between empty() and .append() - noteLink.addClass("use-tn-links"); - this.$list.empty(); this.children = []; this.renderedAttachmentIds = new Set(); const attachments = await note.getAttachments(); - if (attachments.length === 0) { - this.$list.html('
' + t("attachment_list.no_attachments") + "
"); - return; - } - for (const attachment of attachments) { const attachmentDetailWidget = new AttachmentDetailWidget(attachment, false);