2024-10-08 12:01:51 -04:00
|
|
|
/*
|
|
|
|
* Vencord, a Discord client mod
|
|
|
|
* Copyright (c) 2024 Vendicated and contributors
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
import "./styles.css";
|
|
|
|
|
|
|
|
import { EquicordDevs } from "@utils/constants";
|
2024-10-16 17:52:50 -04:00
|
|
|
import definePlugin from "@utils/types";
|
|
|
|
|
|
|
|
import { getMimeType, isLinkAnImage, settings, stripDiscordParams } from "./settings";
|
|
|
|
|
|
|
|
let currentPreview: HTMLDivElement | null = null;
|
|
|
|
let currentPreviewFile: HTMLImageElement | HTMLVideoElement | null = null;
|
|
|
|
let currentPreviewFileSize: [number, number] | null = null;
|
|
|
|
let currentPreviewType: "image" | "video" | null = null;
|
|
|
|
let loadingSpinner: HTMLDivElement | null = null;
|
|
|
|
let isCtrlHeld: boolean = false;
|
|
|
|
let zoomLevel: number = 1;
|
|
|
|
let dragOffsetX: number = 0;
|
|
|
|
let dragOffsetY: number = 0;
|
|
|
|
let isDragging: boolean = false;
|
|
|
|
let shouldKeepPreviewOpenTimeout: NodeJS.Timeout | null = null;
|
|
|
|
let shouldKeepPreviewOpen: boolean = false;
|
|
|
|
let hoverDelayTimeout: NodeJS.Timeout | null = null;
|
2024-10-17 11:06:43 -04:00
|
|
|
let lastMouseEvent: MouseEvent | null = null;
|
2024-10-16 17:52:50 -04:00
|
|
|
|
|
|
|
let observer: MutationObserver | null = null;
|
|
|
|
|
|
|
|
function deleteCurrentPreview() {
|
2024-10-17 11:06:43 -04:00
|
|
|
if (!currentPreview || !currentPreviewFile || !currentPreviewFileSize || !currentPreviewType || !loadingSpinner) return;
|
2024-10-16 17:52:50 -04:00
|
|
|
|
|
|
|
currentPreview.remove();
|
2024-10-17 11:06:43 -04:00
|
|
|
loadingSpinner = null;
|
|
|
|
lastMouseEvent = null;
|
2024-10-16 17:52:50 -04:00
|
|
|
currentPreview = null;
|
|
|
|
currentPreviewFile = null;
|
|
|
|
currentPreviewFileSize = null;
|
|
|
|
currentPreviewType = null;
|
2024-10-17 11:06:43 -04:00
|
|
|
lastMouseEvent = null;
|
|
|
|
loadingSpinner = null;
|
2024-10-16 17:52:50 -04:00
|
|
|
zoomLevel = 1;
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function scanObjects(element: Element) {
|
|
|
|
if (settings.store.messageImages) {
|
|
|
|
element.querySelectorAll('[data-role="img"]:not([data-processed="true"])').forEach(img => {
|
|
|
|
const messageParent = img.closest("[class^='messageListItem_']");
|
|
|
|
if (messageParent) {
|
|
|
|
addHoverListener(img);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (settings.store.messageAvatars) {
|
|
|
|
const selectors = [
|
|
|
|
'img[src*="cdn.discordapp.com/avatars/"]:not([data-processed="true"])',
|
|
|
|
'img[src*="cdn.discordapp.com/guilds/"]:not([data-processed="true"])',
|
|
|
|
'img[src^="/assets/"][class*="avatar"]:not([data-processed="true"])',
|
|
|
|
];
|
|
|
|
|
|
|
|
const jointSelector = selectors.join(", ");
|
|
|
|
element.querySelectorAll(jointSelector).forEach(avatar => {
|
|
|
|
const messageParent = avatar.closest("[class^='messageListItem_']");
|
|
|
|
if (messageParent) {
|
|
|
|
addHoverListener(avatar);
|
|
|
|
}
|
|
|
|
});
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (settings.store.messageLinks) {
|
|
|
|
element.querySelectorAll("span:not([data-processed='true'])").forEach(span => {
|
|
|
|
const url = span.textContent?.replace(/<[^>]*>?/gm, "").trim();
|
|
|
|
if (url && (url.startsWith("http://") || url.startsWith("https://")) && isLinkAnImage(url)) {
|
|
|
|
const messageParent = span.closest("[class^='messageListItem_']");
|
|
|
|
if (messageParent) {
|
|
|
|
addHoverListener(span);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (settings.store.messageStickers) {
|
|
|
|
element.querySelectorAll('img[data-type="sticker"]:not([data-processed="true"])').forEach(sticker => {
|
|
|
|
const messageParent = sticker.closest("[class^='messageListItem_']");
|
|
|
|
if (messageParent) {
|
|
|
|
addHoverListener(sticker);
|
|
|
|
}
|
|
|
|
});
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
2024-10-16 17:52:50 -04:00
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function createObserver() {
|
|
|
|
return new MutationObserver(mutations => {
|
|
|
|
mutations.forEach(mutation => {
|
|
|
|
if (mutation.type === "childList") {
|
|
|
|
mutation.addedNodes.forEach(addedNode => {
|
|
|
|
if (addedNode instanceof HTMLElement) {
|
|
|
|
const element = addedNode as HTMLElement;
|
|
|
|
scanObjects(element);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function loadImagePreview(url: string) {
|
|
|
|
const urlParams = new URLSearchParams(url.split("?")[1]);
|
|
|
|
const formatParam = urlParams.get("format");
|
|
|
|
const extension = formatParam || url.split(".").pop()?.split("?")[0] || "";
|
|
|
|
const [allowed, mimeType] = getMimeType(extension);
|
|
|
|
|
|
|
|
if (!allowed) return;
|
|
|
|
|
2024-10-17 12:57:57 -04:00
|
|
|
const [maxWidth, maxHeight] = settings.store.defaultMaxSize === "0" ? [Infinity, Infinity] : settings.store.defaultMaxSize.split("x").map(Number);
|
2024-10-16 17:52:50 -04:00
|
|
|
currentPreviewType = mimeType.includes("video") ? "video" : "image";
|
|
|
|
|
|
|
|
const preview = document.createElement("div");
|
|
|
|
preview.className = "image-preview";
|
|
|
|
|
|
|
|
loadingSpinner = document.createElement("div");
|
|
|
|
loadingSpinner.className = "loading-spinner";
|
|
|
|
|
|
|
|
preview.appendChild(loadingSpinner);
|
|
|
|
document.body.appendChild(preview);
|
|
|
|
currentPreview = preview;
|
|
|
|
|
|
|
|
const fileInfo = document.createElement("div");
|
|
|
|
fileInfo.className = "file-info";
|
|
|
|
|
|
|
|
const fileName = document.createElement("span");
|
|
|
|
const fileSize = document.createElement("span");
|
2024-10-17 11:06:43 -04:00
|
|
|
fileSize.className = "file-size";
|
|
|
|
const fileSizeSpan = document.createElement("p");
|
|
|
|
const showingSize = document.createElement("p");
|
2024-10-16 17:52:50 -04:00
|
|
|
const mimeTypeSpan = document.createElement("span");
|
|
|
|
|
2024-10-17 11:06:43 -04:00
|
|
|
const updatePositionAfterLoad = () => {
|
|
|
|
if (lastMouseEvent && currentPreview) {
|
|
|
|
updatePreviewPosition(lastMouseEvent, currentPreview);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-10-17 12:57:57 -04:00
|
|
|
const resizeMedia = (mediaWidth: number, mediaHeight: number) => {
|
|
|
|
const mediaElement = currentPreviewFile as HTMLImageElement | HTMLVideoElement | null;
|
|
|
|
if (!mediaElement) return;
|
|
|
|
|
|
|
|
if (mediaWidth > maxWidth || mediaHeight > maxHeight) {
|
|
|
|
const widthRatio = maxWidth / mediaWidth;
|
|
|
|
const heightRatio = maxHeight / mediaHeight;
|
|
|
|
const scale = Math.min(widthRatio, heightRatio);
|
|
|
|
|
|
|
|
mediaElement.style.width = `${mediaWidth * scale}px`;
|
|
|
|
mediaElement.style.height = `${mediaHeight * scale}px`;
|
|
|
|
}
|
|
|
|
|
|
|
|
updatePositionAfterLoad();
|
|
|
|
};
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
fileName.textContent = url.split("/").pop()?.split("?")[0] || "";
|
2024-10-17 11:06:43 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
mimeTypeSpan.textContent = mimeType;
|
|
|
|
|
|
|
|
if (currentPreviewType === "video") {
|
|
|
|
const video = document.createElement("video");
|
|
|
|
video.src = url;
|
|
|
|
video.className = "preview-media";
|
|
|
|
video.autoplay = true;
|
|
|
|
video.muted = true;
|
|
|
|
video.loop = true;
|
|
|
|
video.style.pointerEvents = "none";
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
video.onplay = () => {
|
|
|
|
video.removeAttribute("controls");
|
|
|
|
};
|
|
|
|
|
|
|
|
video.onloadeddata = () => {
|
|
|
|
currentPreviewFileSize = [video.videoWidth, video.videoHeight];
|
2024-10-17 11:06:43 -04:00
|
|
|
fileSizeSpan.textContent = `${currentPreviewFileSize[0]}x${currentPreviewFileSize[1]}`;
|
|
|
|
fileSize.appendChild(fileSizeSpan);
|
|
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
if (!currentPreviewFileSize) return;
|
|
|
|
const showingMediaSize = [video.clientWidth, video.clientHeight];
|
|
|
|
if (showingMediaSize[0] !== currentPreviewFileSize[0] && showingMediaSize[1] !== currentPreviewFileSize[1]) {
|
|
|
|
showingSize.textContent = showingMediaSize ? `(${showingMediaSize[0]}x${showingMediaSize[1]})` : "";
|
|
|
|
fileSize.appendChild(showingSize);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (loadingSpinner) loadingSpinner.remove();
|
|
|
|
video.style.display = "block";
|
2024-10-17 11:06:43 -04:00
|
|
|
|
2024-10-17 12:57:57 -04:00
|
|
|
resizeMedia(video.videoWidth, video.videoHeight);
|
2024-10-16 17:52:50 -04:00
|
|
|
};
|
|
|
|
|
2024-10-17 11:06:43 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
preview.appendChild(video);
|
|
|
|
currentPreviewFile = video;
|
2024-10-08 12:01:51 -04:00
|
|
|
} else {
|
2024-10-16 17:52:50 -04:00
|
|
|
const img = new Image();
|
|
|
|
img.src = url;
|
|
|
|
img.className = "preview-media";
|
|
|
|
img.onload = () => {
|
|
|
|
currentPreviewFileSize = [img.naturalWidth, img.naturalHeight];
|
2024-10-17 11:06:43 -04:00
|
|
|
fileSizeSpan.textContent = `${currentPreviewFileSize[0]}x${currentPreviewFileSize[1]}`;
|
|
|
|
fileSize.appendChild(fileSizeSpan);
|
|
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
if (!currentPreviewFileSize) return;
|
|
|
|
|
|
|
|
const showingMediaSize = [img.clientWidth, img.clientHeight];
|
|
|
|
if (showingMediaSize[0] !== currentPreviewFileSize[0] && showingMediaSize[1] !== currentPreviewFileSize[1]) {
|
|
|
|
showingSize.textContent = showingMediaSize ? `(${showingMediaSize[0]}x${showingMediaSize[1]})` : "";
|
|
|
|
fileSize.appendChild(showingSize);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (loadingSpinner) loadingSpinner.remove();
|
|
|
|
img.style.display = "block";
|
2024-10-17 11:06:43 -04:00
|
|
|
|
2024-10-17 12:57:57 -04:00
|
|
|
resizeMedia(img.naturalWidth, img.naturalHeight);
|
2024-10-16 17:52:50 -04:00
|
|
|
};
|
2024-10-17 11:06:43 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
preview.appendChild(img);
|
|
|
|
currentPreviewFile = img;
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
fileInfo.appendChild(mimeTypeSpan);
|
|
|
|
fileInfo.appendChild(fileName);
|
|
|
|
fileInfo.appendChild(fileSize);
|
|
|
|
preview.appendChild(fileInfo);
|
|
|
|
|
2024-10-17 11:06:43 -04:00
|
|
|
if (settings.store.mouseOnlyMode) {
|
|
|
|
currentPreviewFile.addEventListener("mouseover", () => {
|
|
|
|
if (currentPreview && !isCtrlHeld) {
|
|
|
|
shouldKeepPreviewOpen = true;
|
|
|
|
currentPreview.classList.add("allow-zoom-and-drag");
|
|
|
|
}
|
|
|
|
});
|
2024-10-10 20:03:18 -04:00
|
|
|
|
2024-10-17 11:06:43 -04:00
|
|
|
currentPreviewFile.addEventListener("mouseout", () => {
|
|
|
|
if (currentPreview && !isCtrlHeld && shouldKeepPreviewOpen) {
|
|
|
|
deleteCurrentPreview();
|
|
|
|
shouldKeepPreviewOpen = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-10-10 20:03:18 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
currentPreview.addEventListener("wheel", (event: WheelEvent) => {
|
2024-10-17 11:06:43 -04:00
|
|
|
const [{ zoomFactor }, zoomSpeed] = [settings.store, 0.0005];
|
2024-10-16 17:52:50 -04:00
|
|
|
|
|
|
|
if (isCtrlHeld || event.target === currentPreview || event.target === currentPreviewFile) {
|
|
|
|
event.preventDefault();
|
2024-10-17 11:06:43 -04:00
|
|
|
|
|
|
|
zoomLevel += event.deltaY * -zoomSpeed * zoomFactor;
|
2024-10-16 17:52:50 -04:00
|
|
|
|
|
|
|
zoomLevel = Math.min(Math.max(zoomLevel, 0.5), 10);
|
|
|
|
|
|
|
|
const previewMedia = currentPreviewFile as HTMLImageElement | HTMLVideoElement | null;
|
|
|
|
if (previewMedia) {
|
|
|
|
const rect = previewMedia.getBoundingClientRect();
|
|
|
|
let offsetX = (event.clientX - rect.left) / rect.width;
|
|
|
|
let offsetY = (event.clientY - rect.top) / rect.height;
|
|
|
|
|
|
|
|
offsetX = Math.min(Math.max(offsetX, 0.1), 0.9);
|
|
|
|
offsetY = Math.min(Math.max(offsetY, 0.1), 0.9);
|
|
|
|
|
|
|
|
previewMedia.style.transformOrigin = `${offsetX * 100}% ${offsetY * 100}%`;
|
|
|
|
previewMedia.style.transform = `scale(${zoomLevel})`;
|
2024-10-10 20:03:18 -04:00
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
2024-10-16 17:52:50 -04:00
|
|
|
});
|
|
|
|
|
2024-10-17 11:06:43 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
currentPreview.addEventListener("mousedown", (event: MouseEvent) => {
|
|
|
|
if ((isCtrlHeld || shouldKeepPreviewOpen) && currentPreview) {
|
|
|
|
isDragging = true;
|
|
|
|
|
|
|
|
const rect = currentPreview.getBoundingClientRect();
|
|
|
|
dragOffsetX = event.clientX - rect.left;
|
|
|
|
dragOffsetY = event.clientY - rect.top;
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function updatePreviewPosition(mouseEvent: MouseEvent, element: HTMLElement) {
|
|
|
|
if (currentPreview && !isCtrlHeld) {
|
|
|
|
const padding = 15;
|
|
|
|
const maxWidth = window.innerWidth * 0.9;
|
|
|
|
const maxHeight = window.innerHeight * 0.9;
|
2024-10-10 20:03:18 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
const previewWidth = currentPreview.offsetWidth;
|
|
|
|
const previewHeight = currentPreview.offsetHeight;
|
|
|
|
|
|
|
|
let left = mouseEvent.pageX + padding;
|
|
|
|
let top = mouseEvent.pageY + padding;
|
|
|
|
|
|
|
|
if (left + previewWidth > window.innerWidth) {
|
|
|
|
left = mouseEvent.pageX - previewWidth - padding;
|
|
|
|
if (left < padding) {
|
|
|
|
left = window.innerWidth - previewWidth - padding;
|
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (top + previewHeight > window.innerHeight) {
|
|
|
|
top = mouseEvent.pageY - previewHeight - padding;
|
2024-10-17 11:06:43 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (top < padding) {
|
2024-10-17 11:06:43 -04:00
|
|
|
top = window.innerHeight - previewHeight - padding * 2;
|
2024-10-10 20:03:18 -04:00
|
|
|
}
|
2024-10-17 11:06:43 -04:00
|
|
|
} else {
|
|
|
|
top = Math.min(top, window.innerHeight - previewHeight - padding * 2);
|
2024-10-10 20:03:18 -04:00
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
currentPreview.style.left = `${left}px`;
|
|
|
|
currentPreview.style.top = `${top}px`;
|
|
|
|
|
|
|
|
const mediaElement = element as HTMLImageElement | HTMLVideoElement | null;
|
|
|
|
if (mediaElement) {
|
|
|
|
mediaElement.style.maxWidth = `${maxWidth}px`;
|
|
|
|
mediaElement.style.maxHeight = `${maxHeight}px`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-10-10 20:03:18 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function addHoverListener(element: Element) {
|
|
|
|
element.setAttribute("data-processed", "true");
|
2024-10-10 20:03:18 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
element.addEventListener("mouseover", event => {
|
2024-10-17 11:06:43 -04:00
|
|
|
if (currentPreview || loadingSpinner) {
|
2024-10-16 17:52:50 -04:00
|
|
|
if (isCtrlHeld) return;
|
2024-10-10 20:03:18 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
deleteCurrentPreview();
|
2024-10-10 20:03:18 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (shouldKeepPreviewOpenTimeout) {
|
|
|
|
clearTimeout(shouldKeepPreviewOpenTimeout);
|
|
|
|
shouldKeepPreviewOpenTimeout = null;
|
2024-10-10 20:03:18 -04:00
|
|
|
}
|
2024-10-16 17:52:50 -04:00
|
|
|
}
|
2024-10-10 20:03:18 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (hoverDelayTimeout) {
|
|
|
|
clearTimeout(hoverDelayTimeout);
|
|
|
|
hoverDelayTimeout = null;
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
const mouseEvent = event as MouseEvent;
|
|
|
|
lastMouseEvent = mouseEvent;
|
|
|
|
const imageURL: string | null =
|
|
|
|
element.getAttribute("data-safe-src") ||
|
|
|
|
element.getAttribute("src") ||
|
|
|
|
element.getAttribute("href") ||
|
|
|
|
element.textContent;
|
|
|
|
const strippedURL: string | null = imageURL
|
|
|
|
? stripDiscordParams(imageURL)
|
|
|
|
: null;
|
|
|
|
|
|
|
|
if (!strippedURL) return;
|
|
|
|
|
|
|
|
hoverDelayTimeout = setTimeout(() => {
|
|
|
|
loadImagePreview(strippedURL);
|
|
|
|
if (lastMouseEvent) {
|
|
|
|
updatePreviewPosition(lastMouseEvent, element as HTMLElement);
|
|
|
|
}
|
|
|
|
}, settings.store.hoverDelay * 1000);
|
2024-10-10 20:03:18 -04:00
|
|
|
});
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
element.addEventListener("mousemove", event => {
|
|
|
|
if (!hoverDelayTimeout) return;
|
|
|
|
|
|
|
|
lastMouseEvent = event as MouseEvent;
|
|
|
|
|
|
|
|
if (currentPreview && !isCtrlHeld) {
|
|
|
|
updatePreviewPosition(lastMouseEvent, element as HTMLElement);
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
2024-10-16 17:52:50 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
element.addEventListener("mouseout", () => {
|
|
|
|
if (hoverDelayTimeout) {
|
|
|
|
clearTimeout(hoverDelayTimeout);
|
|
|
|
hoverDelayTimeout = null;
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function remove() {
|
|
|
|
if (currentPreview && !isCtrlHeld && !shouldKeepPreviewOpen) {
|
|
|
|
deleteCurrentPreview();
|
|
|
|
}
|
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
if (settings.store.mouseOnlyMode) {
|
|
|
|
shouldKeepPreviewOpenTimeout = setTimeout(remove, 500);
|
|
|
|
} else {
|
|
|
|
remove();
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function handleKeydown(event: KeyboardEvent) {
|
2024-10-17 11:06:43 -04:00
|
|
|
if (event.key === "Control" && currentPreview) {
|
2024-10-16 17:52:50 -04:00
|
|
|
isCtrlHeld = true;
|
2024-10-17 11:06:43 -04:00
|
|
|
currentPreview.classList.add("allow-zoom-and-drag");
|
2024-10-16 17:52:50 -04:00
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function handleKeyup(event: KeyboardEvent) {
|
|
|
|
if (event.key === "Control") {
|
|
|
|
isCtrlHeld = false;
|
|
|
|
if (currentPreview) {
|
|
|
|
deleteCurrentPreview();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function handleMousemove(event: MouseEvent) {
|
|
|
|
if (isDragging && (isCtrlHeld || shouldKeepPreviewOpen) && currentPreview) {
|
|
|
|
const left = event.clientX - dragOffsetX;
|
|
|
|
const top = event.clientY - dragOffsetY;
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
currentPreview.style.left = `${left}px`;
|
|
|
|
currentPreview.style.top = `${top}px`;
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
function handleMouseup() {
|
|
|
|
if (currentPreview && isDragging) {
|
|
|
|
isDragging = false;
|
2024-10-10 20:03:18 -04:00
|
|
|
}
|
2024-10-16 17:52:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function removeHoverListeners() {
|
|
|
|
const processedElements = document.querySelectorAll('[data-processed="true"]');
|
|
|
|
|
|
|
|
processedElements.forEach(element => {
|
|
|
|
const clone = element.cloneNode(true);
|
|
|
|
element.replaceWith(clone);
|
2024-10-17 11:06:43 -04:00
|
|
|
element.removeAttribute("data-processed");
|
2024-10-16 17:52:50 -04:00
|
|
|
});
|
|
|
|
}
|
2024-10-08 12:01:51 -04:00
|
|
|
|
|
|
|
export default definePlugin({
|
2024-10-08 12:05:45 -04:00
|
|
|
name: "ImagePreview",
|
2024-10-16 17:52:50 -04:00
|
|
|
description: "Hover on message images, avatars, links, and message stickers to show a full preview.",
|
2024-10-08 12:01:51 -04:00
|
|
|
authors: [EquicordDevs.creations],
|
|
|
|
settings: settings,
|
|
|
|
|
|
|
|
start() {
|
2024-10-16 17:52:50 -04:00
|
|
|
const targetNode = document.querySelector('[class*="app-"]');
|
|
|
|
if (!targetNode) return;
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
scanObjects(targetNode);
|
|
|
|
document.addEventListener("keydown", handleKeydown);
|
|
|
|
document.addEventListener("keyup", handleKeyup);
|
|
|
|
document.addEventListener("mousemove", handleMousemove);
|
|
|
|
document.addEventListener("mouseup", handleMouseup);
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
observer = createObserver();
|
|
|
|
observer.observe(targetNode, { childList: true, subtree: true });
|
2024-10-08 12:01:51 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
stop() {
|
2024-10-16 17:52:50 -04:00
|
|
|
if (observer) observer.disconnect();
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
deleteCurrentPreview();
|
|
|
|
removeHoverListeners();
|
2024-10-08 12:01:51 -04:00
|
|
|
|
2024-10-16 17:52:50 -04:00
|
|
|
document.removeEventListener("keydown", handleKeydown);
|
|
|
|
document.removeEventListener("keyup", handleKeyup);
|
|
|
|
document.removeEventListener("mousemove", handleMousemove);
|
|
|
|
document.removeEventListener("mouseup", handleMouseup);
|
2024-10-08 12:01:51 -04:00
|
|
|
}
|
|
|
|
});
|