Update name, remove css building, remove cache size setting

This commit is contained in:
TheGreenPig 2024-09-18 01:47:49 +02:00
parent fce53b9535
commit 6c2222e295
4 changed files with 48 additions and 106 deletions

View file

@ -407,36 +407,6 @@ export function PencilIcon(props: IconProps) {
); );
} }
export function PreviewVisible(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-preview-visible")}
viewBox="0 0 226 226"
>
<path
fill="currentColor"
d="M113,37.66667c-75.33333,0 -103.58333,75.33333 -103.58333,75.33333c0,0 28.25,75.33333 103.58333,75.33333c75.33333,0 103.58333,-75.33333 103.58333,-75.33333c0,0 -28.25,-75.33333 -103.58333,-75.33333zM113,65.91667c25.99942,0 47.08333,21.08392 47.08333,47.08333c0,25.99942 -21.08392,47.08333 -47.08333,47.08333c-25.99942,0 -47.08333,-21.08392 -47.08333,-47.08333c0,-25.99942 21.08392,-47.08333 47.08333,-47.08333zM113,84.75c-15.60204,0 -28.25,12.64796 -28.25,28.25c0,15.60204 12.64796,28.25 28.25,28.25c15.60204,0 28.25,-12.64796 28.25,-28.25c0,-15.60204 -12.64796,-28.25 -28.25,-28.25z"
/>
</Icon>
);
}
export function PreviewInvisible(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-preview-invisible")}
viewBox="0 0 226 226"
>
<path
fill="currentColor"
d="M37.57471,28.15804c-3.83186,0.00101 -7.28105,2.32361 -8.72295,5.87384c-1.4419,3.55022 -0.58897,7.62011 2.15703,10.29267l16.79183,16.79183c-18.19175,14.60996 -29.9888,32.52303 -35.82747,43.03711c-3.12633,5.63117 -3.02363,12.41043 0.03678,18.07927c10.87625,20.13283 42.14532,66.10058 100.99007,66.10058c19.54493,0 35.83986,-5.13463 49.36394,-12.65365l19.31152,19.31152c2.36186,2.46002 5.8691,3.45098 9.16909,2.5907c3.3,-0.86028 5.87708,-3.43736 6.73736,-6.73736c0.86028,-3.3 -0.13068,-6.80724 -2.5907,-9.16909l-150.66666,-150.66667c-1.77289,-1.82243 -4.20732,-2.8506 -6.74984,-2.85075zM113,37.66667c-11.413,0 -21.60375,1.88068 -30.91683,4.81869l24.11182,24.11182c2.23175,-0.32958 4.47909,-0.6805 6.80501,-0.6805c25.99942,0 47.08333,21.08392 47.08333,47.08333c0,2.32592 -0.35092,4.57326 -0.6805,6.80501l32.29623,32.29623c10.1135,-11.22467 17.51573,-22.61015 21.94157,-30.18115c3.3335,-5.68767 3.32011,-12.67425 0.16553,-18.4655c-11.00808,-20.27408 -42.2439,-65.78792 -100.80615,-65.78792zM73.77002,87.08577l13.77555,13.77556c-1.77707,3.67147 -2.79557,7.77466 -2.79557,12.13867c0,15.60342 12.64658,28.25 28.25,28.25c4.364,0 8.46719,-1.01851 12.13867,-2.79557l13.79395,13.79395c-9.356,6.20362 -21.03043,9.17606 -33.4733,7.24642c-19.75617,-3.06983 -35.88427,-19.19794 -38.9541,-38.9541c-1.92879,-12.43739 1.0665,-24.10096 7.26481,-33.45491z"
/>
</Icon>
);
}
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg"; const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg"; const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg"; const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";

View file

@ -4,15 +4,13 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { Logger } from "@utils/Logger";
export class LRUCache { export class LRUCache {
private cache: Map<string, string>; private cache: Map<string, string>;
private maxSize: number; private maxSize: number;
constructor() { constructor(maxSize: number) {
this.cache = new Map(); this.cache = new Map();
this.maxSize = 50; this.maxSize = maxSize;
} }
get(key: string): string | undefined { get(key: string): string | undefined {
@ -47,16 +45,4 @@ export class LRUCache {
} }
this.cache.clear(); 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;
}
} }

View file

@ -4,22 +4,20 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import "./fileViewer.css"; import "./pdfViewer.css";
import { get, set } from "@api/DataStore"; import { get, set } from "@api/DataStore";
import { addAccessory, removeAccessory } from "@api/MessageAccessories"; import { addAccessory, removeAccessory } from "@api/MessageAccessories";
import { updateMessage } from "@api/MessageUpdater"; import { updateMessage } from "@api/MessageUpdater";
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { PreviewInvisible, PreviewVisible } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType, PluginNative } from "@utils/types"; 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"; import { LRUCache } from "./cache";
const Native = VencordNative.pluginHelpers.FileViewer as PluginNative<typeof import("./native")>; const Native = VencordNative.pluginHelpers.PdfViewer as PluginNative<typeof import("./native")>;
const settings = definePluginSettings({ const settings = definePluginSettings({
autoEmptyCache: { autoEmptyCache: {
@ -32,20 +30,10 @@ const settings = definePluginSettings({
description: "Persist the state of opened/closed File Previews across channel switches and reloads.", description: "Persist the state of opened/closed File Previews across channel switches and reloads.",
default: false 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 objectUrlsCache = new LRUCache(20);
const STORE_KEY = "FileViewer_PersistVisible"; const STORE_KEY = "PdfViewer_PersistVisible";
let style: HTMLStyleElement;
interface Attachment { interface Attachment {
id: string; id: string;
@ -58,25 +46,19 @@ interface Attachment {
title: string; title: string;
spoiler: boolean; spoiler: boolean;
previewBlobUrl?: string; 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; }) { function FilePreview({ attachment }: { attachment: Attachment; }) {
const { previewBlobUrl } = attachment; const { previewBlobUrl, previewVisible } = attachment;
if (!previewBlobUrl) return null; if (!previewVisible) return null;
return <div className={"file-viewer container"} id={`file-viewer-${stripLink(attachment.url)}`}><embed src={previewBlobUrl} className="file-viewer preview" title={attachment.filename} /></div>; return (
} <div className={"vc-pdf-viewer-container"}>
{previewBlobUrl ? <embed src={previewBlobUrl} className="vc-pdf-viewer-preview" title={attachment.filename} /> : <Spinner />}
async function buildCss() { </div>
const visiblePreviews: Set<string> | undefined = await get(STORE_KEY); );
const elements = [...(visiblePreviews || [])].map(url => `#file-viewer-${stripLink(url)}`).join(",");
style.textContent = `
:is(${elements}) {
display: flex !important;
}
`;
} }
function PreviewButton({ attachment, channelId, messageId }: { attachment: Attachment; channelId: string; messageId: string; }) { function PreviewButton({ attachment, channelId, messageId }: { attachment: Attachment; channelId: string; messageId: string; }) {
@ -92,6 +74,7 @@ function PreviewButton({ attachment, channelId, messageId }: { attachment: Attac
try { try {
const buffer = await Native.getBufferResponse(attachment.url); const buffer = await Native.getBufferResponse(attachment.url);
const file = new File([buffer], attachment.filename, { type: attachment.content_type }); const file = new File([buffer], attachment.filename, { type: attachment.content_type });
const blobUrl = URL.createObjectURL(file); const blobUrl = URL.createObjectURL(file);
objectUrlsCache.set(attachment.url, blobUrl); objectUrlsCache.set(attachment.url, blobUrl);
setUrl(blobUrl); setUrl(blobUrl);
@ -100,24 +83,31 @@ function PreviewButton({ attachment, channelId, messageId }: { attachment: Attac
} }
}; };
useEffect(() => { const updateVisibility = async () => {
get(STORE_KEY).then(async data => { const data: Set<string> = await get(STORE_KEY) ?? new Set();
if (visible === null) { if (visible === null) {
setVisible(settings.store.persistPreviewState ? (data ?? new Set()).has(attachment.url) : false); setVisible(settings.store.persistPreviewState ? data.has(attachment.url) : false);
} else { } else {
const persistSet = (data ?? new Set()); if (visible) data.add(attachment.url);
if (visible) persistSet.add(attachment.url); else data.delete(attachment.url);
else persistSet.delete(attachment.url);
await set(STORE_KEY, persistSet); await set(STORE_KEY, data);
buildCss();
attachment.previewVisible = visible;
updateMessage(channelId, messageId);
} }
}); };
useEffect(() => {
updateVisibility();
if (visible && !url) initPdfData(); if (visible && !url) initPdfData();
}, [visible]); }, [visible]);
useEffect(() => { useEffect(() => {
attachment.previewBlobUrl = url; attachment.previewBlobUrl = url;
updateMessage(channelId, messageId,); updateMessage(channelId, messageId);
return () => { return () => {
if (url && settings.store.autoEmptyCache) { if (url && settings.store.autoEmptyCache) {
objectUrlsCache.delete(attachment.url); objectUrlsCache.delete(attachment.url);
@ -129,20 +119,20 @@ function PreviewButton({ attachment, channelId, messageId }: { attachment: Attac
{tooltipProps => ( {tooltipProps => (
<div <div
{...tooltipProps} {...tooltipProps}
className="file-viewer toggle" className="vc-pdf-viewer-toggle"
role="button" role="button"
onClick={() => { onClick={() => {
setVisible(v => !v); setVisible(v => !v);
}} }}
> >
{visible ? <PreviewInvisible /> : <PreviewVisible />} {visible ? <Icons.EyeSlashIcon /> : <Icons.EyeIcon />}
</div> </div>
)} )}
</Tooltip>; </Tooltip>;
} }
export default definePlugin({ export default definePlugin({
name: "FileViewer", name: "PdfViewer",
description: "Preview PDF Files without having to download them", description: "Preview PDF Files without having to download them",
authors: [Devs.AGreenPig], authors: [Devs.AGreenPig],
dependencies: ["MessageAccessoriesAPI", "MessageUpdaterAPI",], dependencies: ["MessageAccessoriesAPI", "MessageUpdaterAPI",],
@ -157,8 +147,7 @@ export default definePlugin({
} }
], ],
start() { start() {
objectUrlsCache.setMaxSize(Math.round(settings.store.cacheSize)); addAccessory("pdfViewer", props => {
addAccessory("fileViewer", props => {
const pdfAttachments = props.message.attachments.filter(a => a.content_type === "application/pdf"); const pdfAttachments = props.message.attachments.filter(a => a.content_type === "application/pdf");
if (!pdfAttachments.length) return null; if (!pdfAttachments.length) return null;
@ -170,18 +159,13 @@ export default definePlugin({
</ErrorBoundary> </ErrorBoundary>
); );
}, -1); }, -1);
style = document.createElement("style");
style.id = "VencordFileViewer";
document.head.appendChild(style);
}, },
renderPreviewButton: ErrorBoundary.wrap(e => { renderPreviewButton: ErrorBoundary.wrap(e => {
if (e.item.originalItem.content_type !== "application/pdf") return null; if (e.item.originalItem.content_type !== "application/pdf") return null;
return <PreviewButton attachment={e.item.originalItem} channelId={e.message.channel_id} messageId={e.message.id} />; return <PreviewButton attachment={e.item.originalItem} channelId={e.message.channel_id} messageId={e.message.id} />;
}, }),
stop() { stop() {
objectUrlsCache.clear(); objectUrlsCache.clear();
removeAccessory("fileViewer"); removeAccessory("pdfViewer");
style.remove();
} }
}); });

View file

@ -1,4 +1,4 @@
.file-viewer.container { .vc-pdf-viewer-container {
resize: both; resize: both;
overflow: hidden; overflow: hidden;
max-width: 100%; max-width: 100%;
@ -6,16 +6,18 @@
max-height: 80vh; max-height: 80vh;
min-height: 500px; min-height: 500px;
box-sizing: border-box; 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%; width: 100%;
flex-grow: 1; flex-grow: 1;
border: none; border: none;
} }
.file-viewer.toggle { .vc-pdf-viewer-toggle {
justify-self: center; justify-self: center;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -24,6 +26,6 @@
cursor: pointer; cursor: pointer;
} }
.file-viewer.toggle:hover { .vc-pdf-viewer-toggle:hover {
color: var(--interactive-hover); color: var(--interactive-hover);
} }