diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx index 8bb36ba4b..fa142a18c 100644 --- a/src/components/Icons.tsx +++ b/src/components/Icons.tsx @@ -407,36 +407,6 @@ export function PencilIcon(props: IconProps) { ); } -export function PreviewVisible(props: IconProps) { - return ( - - - - ); -} - -export function PreviewInvisible(props: IconProps) { - return ( - - - - ); -} - const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg"; const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg"; const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg"; diff --git a/src/plugins/fileViewer/cache.ts b/src/plugins/pdfViewer.desktop/cache.ts similarity index 78% rename from src/plugins/fileViewer/cache.ts rename to src/plugins/pdfViewer.desktop/cache.ts index 0606da6c7..45f90728d 100644 --- a/src/plugins/fileViewer/cache.ts +++ b/src/plugins/pdfViewer.desktop/cache.ts @@ -4,15 +4,13 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { Logger } from "@utils/Logger"; - export class LRUCache { private cache: Map; private maxSize: number; - constructor() { + constructor(maxSize: number) { this.cache = new Map(); - this.maxSize = 50; + this.maxSize = maxSize; } get(key: string): string | undefined { @@ -47,16 +45,4 @@ export class LRUCache { } this.cache.clear(); } - - setMaxSize(maxSize: number) { - if (maxSize < 1) { - new Logger("FileViewer").error("Cache size must be at least 1"); - return; - } - this.maxSize = maxSize; - } - - getMaxSize() { - return this.maxSize; - } } diff --git a/src/plugins/fileViewer/index.tsx b/src/plugins/pdfViewer.desktop/index.tsx similarity index 62% rename from src/plugins/fileViewer/index.tsx rename to src/plugins/pdfViewer.desktop/index.tsx index a82e81ed2..28ca5f102 100644 --- a/src/plugins/fileViewer/index.tsx +++ b/src/plugins/pdfViewer.desktop/index.tsx @@ -4,22 +4,20 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import "./fileViewer.css"; +import "./pdfViewer.css"; import { get, set } from "@api/DataStore"; import { addAccessory, removeAccessory } from "@api/MessageAccessories"; import { updateMessage } from "@api/MessageUpdater"; import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; -import { PreviewInvisible, PreviewVisible } from "@components/Icons"; import { Devs } from "@utils/constants"; -import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, PluginNative } from "@utils/types"; -import { Tooltip, useEffect, useState } from "@webpack/common"; +import { Icons, Spinner, Tooltip, useEffect, useState } from "@webpack/common"; import { LRUCache } from "./cache"; -const Native = VencordNative.pluginHelpers.FileViewer as PluginNative; +const Native = VencordNative.pluginHelpers.PdfViewer as PluginNative; const settings = definePluginSettings({ autoEmptyCache: { @@ -32,20 +30,10 @@ const settings = definePluginSettings({ description: "Persist the state of opened/closed File Previews across channel switches and reloads.", default: false }, - cacheSize: { - type: OptionType.SLIDER, - description: "Maximum number of PDF files to cache (after that, the least recently used file will be removed). Lower this value if you're running out of memory.", - default: 50, - restartNeeded: true, - markers: [10, 20, 30, 40, 50, 75, 100], - stickToMarkers: true, - } }); -const objectUrlsCache = new LRUCache(); -const STORE_KEY = "FileViewer_PersistVisible"; - -let style: HTMLStyleElement; +const objectUrlsCache = new LRUCache(20); +const STORE_KEY = "PdfViewer_PersistVisible"; interface Attachment { id: string; @@ -58,25 +46,19 @@ interface Attachment { title: string; spoiler: boolean; previewBlobUrl?: string; + previewVisible?: boolean; } -const stripLink = (url: string) => url.replace("https://cdn.discordapp.com/attachments/", "").split("/").slice(0, 2).join("-"); function FilePreview({ attachment }: { attachment: Attachment; }) { - const { previewBlobUrl } = attachment; + const { previewBlobUrl, previewVisible } = attachment; - if (!previewBlobUrl) return null; + if (!previewVisible) return null; - return
; -} - -async function buildCss() { - const visiblePreviews: Set | undefined = await get(STORE_KEY); - const elements = [...(visiblePreviews || [])].map(url => `#file-viewer-${stripLink(url)}`).join(","); - style.textContent = ` - :is(${elements}) { - display: flex !important; - } - `; + return ( +
+ {previewBlobUrl ? : } +
+ ); } function PreviewButton({ attachment, channelId, messageId }: { attachment: Attachment; channelId: string; messageId: string; }) { @@ -92,6 +74,7 @@ function PreviewButton({ attachment, channelId, messageId }: { attachment: Attac try { const buffer = await Native.getBufferResponse(attachment.url); const file = new File([buffer], attachment.filename, { type: attachment.content_type }); + const blobUrl = URL.createObjectURL(file); objectUrlsCache.set(attachment.url, blobUrl); setUrl(blobUrl); @@ -100,24 +83,31 @@ function PreviewButton({ attachment, channelId, messageId }: { attachment: Attac } }; + const updateVisibility = async () => { + const data: Set = await get(STORE_KEY) ?? new Set(); + + if (visible === null) { + setVisible(settings.store.persistPreviewState ? data.has(attachment.url) : false); + } else { + if (visible) data.add(attachment.url); + else data.delete(attachment.url); + + await set(STORE_KEY, data); + + attachment.previewVisible = visible; + updateMessage(channelId, messageId); + } + }; + useEffect(() => { - get(STORE_KEY).then(async data => { - if (visible === null) { - setVisible(settings.store.persistPreviewState ? (data ?? new Set()).has(attachment.url) : false); - } else { - const persistSet = (data ?? new Set()); - if (visible) persistSet.add(attachment.url); - else persistSet.delete(attachment.url); - await set(STORE_KEY, persistSet); - buildCss(); - } - }); + updateVisibility(); + if (visible && !url) initPdfData(); }, [visible]); useEffect(() => { attachment.previewBlobUrl = url; - updateMessage(channelId, messageId,); + updateMessage(channelId, messageId); return () => { if (url && settings.store.autoEmptyCache) { objectUrlsCache.delete(attachment.url); @@ -129,20 +119,20 @@ function PreviewButton({ attachment, channelId, messageId }: { attachment: Attac {tooltipProps => (
{ setVisible(v => !v); }} > - {visible ? : } + {visible ? : }
)} ; } export default definePlugin({ - name: "FileViewer", + name: "PdfViewer", description: "Preview PDF Files without having to download them", authors: [Devs.AGreenPig], dependencies: ["MessageAccessoriesAPI", "MessageUpdaterAPI",], @@ -157,8 +147,7 @@ export default definePlugin({ } ], start() { - objectUrlsCache.setMaxSize(Math.round(settings.store.cacheSize)); - addAccessory("fileViewer", props => { + addAccessory("pdfViewer", props => { const pdfAttachments = props.message.attachments.filter(a => a.content_type === "application/pdf"); if (!pdfAttachments.length) return null; @@ -170,18 +159,13 @@ export default definePlugin({ ); }, -1); - - style = document.createElement("style"); - style.id = "VencordFileViewer"; - document.head.appendChild(style); }, renderPreviewButton: ErrorBoundary.wrap(e => { if (e.item.originalItem.content_type !== "application/pdf") return null; return ; - }, + }), stop() { objectUrlsCache.clear(); - removeAccessory("fileViewer"); - style.remove(); + removeAccessory("pdfViewer"); } }); diff --git a/src/plugins/fileViewer/fileViewer.css b/src/plugins/pdfViewer.desktop/pdfViewer.css similarity index 69% rename from src/plugins/fileViewer/fileViewer.css rename to src/plugins/pdfViewer.desktop/pdfViewer.css index fc72cc2fa..71bc83f45 100644 --- a/src/plugins/fileViewer/fileViewer.css +++ b/src/plugins/pdfViewer.desktop/pdfViewer.css @@ -1,4 +1,4 @@ -.file-viewer.container { +.vc-pdf-viewer-container { resize: both; overflow: hidden; max-width: 100%; @@ -6,16 +6,18 @@ max-height: 80vh; min-height: 500px; box-sizing: border-box; - display: none; /* Will be set with buildCss */ + display: flex; + justify-content: center; + align-items: center; } -.file-viewer.preview { +.vc-pdf-viewer-preview { width: 100%; flex-grow: 1; border: none; } -.file-viewer.toggle { +.vc-pdf-viewer-toggle { justify-self: center; display: flex; justify-content: center; @@ -24,6 +26,6 @@ cursor: pointer; } -.file-viewer.toggle:hover { +.vc-pdf-viewer-toggle:hover { color: var(--interactive-hover); }