diff --git a/Plugins/EditUsers/EditUsers.plugin.js b/Plugins/EditUsers/EditUsers.plugin.js index f5114e0ee0..3c9065d937 100644 --- a/Plugins/EditUsers/EditUsers.plugin.js +++ b/Plugins/EditUsers/EditUsers.plugin.js @@ -2,7 +2,7 @@ * @name EditUsers * @author DevilBro * @authorId 278543574059057154 - * @version 4.5.3 + * @version 4.5.4 * @description Allows you to locally edit Users * @invite Jx3TjNS * @donate https://www.paypal.me/MircoWittrien @@ -17,8 +17,13 @@ module.exports = (_ => { "info": { "name": "EditUsers", "author": "DevilBro", - "version": "4.5.3", + "version": "4.5.4", "description": "Allows you to locally edit Users" + }, + "changeLog": { + "fixed": { + "Voice Channels": "Works in Voice Channels again" + } } }; @@ -130,8 +135,9 @@ module.exports = (_ => { QuickSwitchUserResult: "render", SearchPopoutComponent: "render", PrivateChannelCallParticipants: "render", - VideoParticipants: "default", ChannelCall: "render", + ChannelCallGrid: "default", + HorizontalVideoParticipants: "default", PictureInPictureVideo: "default", UserSummaryItem: "render" }, @@ -1228,18 +1234,19 @@ module.exports = (_ => { } } - processVideoParticipants (e) { + processChannelCall (e) { if (BDFDB.ArrayUtils.is(e.instance.props.participants) && this.settings.places.voiceChat) { e.instance.props.participants = [].concat(e.instance.props.participants); for (let i in e.instance.props.participants) if (e.instance.props.participants[i] && e.instance.props.participants[i].user) e.instance.props.participants[i] = Object.assign({}, e.instance.props.participants[i], {user: this.getUserData(e.instance.props.participants[i].user.id)}); } } - processChannelCall (e) { - if (BDFDB.ArrayUtils.is(e.instance.props.participants) && this.settings.places.voiceChat) { - e.instance.props.participants = [].concat(e.instance.props.participants); - for (let i in e.instance.props.participants) if (e.instance.props.participants[i] && e.instance.props.participants[i].user) e.instance.props.participants[i] = Object.assign({}, e.instance.props.participants[i], {user: this.getUserData(e.instance.props.participants[i].user.id)}); - } + processChannelCallGrid (e) { + this.processChannelCall(e); + } + + processHorizontalVideoParticipants (e) { + this.processChannelCall(e); } processPictureInPictureVideo (e) { diff --git a/Plugins/ImageUtilities/ImageUtilities.plugin.js b/Plugins/ImageUtilities/ImageUtilities.plugin.js index 242facd433..f4adf66c3a 100644 --- a/Plugins/ImageUtilities/ImageUtilities.plugin.js +++ b/Plugins/ImageUtilities/ImageUtilities.plugin.js @@ -2,7 +2,7 @@ * @name ImageUtilities * @author DevilBro * @authorId 278543574059057154 - * @version 4.6.6 + * @version 4.6.7 * @description Adds several Utilities for Images/Videos (Gallery, Download, Reverse Search, Zoom, Copy, etc.) * @invite Jx3TjNS * @donate https://www.paypal.me/MircoWittrien @@ -17,21 +17,12 @@ module.exports = (_ => { "info": { "name": "ImageUtilities", "author": "DevilBro", - "version": "4.6.6", + "version": "4.6.7", "description": "Adds several Utilities for Images/Videos (Gallery, Download, Reverse Search, Zoom, Copy, etc.)" }, "changeLog": { - "progress": { - "Settings": "SETTINGS HAVE BEEN REORGANIZED AND CAUSED SOME SETTINGS TO RESET, CHECK YOUR PLUGIN SETTINGS IF SOMETHING IS BEHAVING DIFFERENTLY" - }, - "fixed": { - "Banners": "No longer show in low Resolution" - }, - "added": { - "Resize in Chat": "Added Option to resize Images in Messages" - }, "improved": { - "Gallery Mode": "Now allows you to switch between all currently loaded Images in a Channel" + "Gallery Mode Channel Wide": "Gallery Mode now let's you move between all images/gifs in a channel, it will automatically load new images/gifs once you hit the last/first in the current queue" } } }; @@ -75,9 +66,13 @@ module.exports = (_ => { } } : (([Plugin, BDFDB]) => { var _this; - var firedEvents = [], clickedImage; + var firedEvents = []; var ownLocations = {}, downloadsFolder; + var firstViewedImage, viewedImage, viewedImageTimeout; + var cachedImages; + var eventTypes = {}; + const imgUrlReplaceString = "DEVILBRO_BD_REVERSEIMAGESEARCH_REPLACE_IMAGEURL"; const fileTypes = { @@ -105,6 +100,42 @@ module.exports = (_ => { "wmv": {copyable: false, searchable: false, video: true} }; + const LazyImageSiblingComponent = class LazyImageSibling extends BdApi.React.Component { + render() { + if (!this.props.loadedImage) { + const instace = this; + const imageThrowaway = document.createElement("img"); + imageThrowaway.addEventListener("load", function() { + let aRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount)); + let resizeX = (aRects.width/this.width) * 0.8, resizeY = (aRects.height/this.height) * 0.65 + let ratio = resizeX < resizeY ? resizeX : resizeY; + instace.props.loadedImage = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.LazyImage, { + src: imageThrowaway.src, + width: this.width, + height: this.height, + maxWidth: this.width * ratio, + maxHeight: this.height * ratio + }); + BDFDB.ReactUtils.forceUpdate(instace); + }); + imageThrowaway.src = this.props.url; + } + return BDFDB.ReactUtils.createElement("div", { + className: BDFDB.DOMUtils.formatClassName(BDFDB.disCN._imageutilitiessibling, this.props.className), + onClick: _ => _this.switchImages(this.props.modalInstance, this.props.offset), + children: [ + this.props.loadedImage || BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Spinner, { + type: BDFDB.LibraryComponents.Spinner.Type.SPINNING_CIRCLE + }), + BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { + className: BDFDB.disCNS._imageutilitiesswitchicon + BDFDB.disCN.svgicon, + name: this.props.svgIcon + }) + ] + }); + } + }; + const ImageDetailsComponent = class ImageDetails extends BdApi.React.Component { componentDidMount() { this.props.attachment = BDFDB.ReactUtils.findValue(BDFDB.ObjectUtils.get(this, `${BDFDB.ReactUtils.instanceKey}.return`), "attachment", {up: true}); @@ -168,7 +199,9 @@ module.exports = (_ => { onLoad () { _this = this; firedEvents = []; - clickedImage = null; + firstViewedImage = null; + viewedImage = null; + cachedImages = null; this.defaults = { viewerSettings: { @@ -219,7 +252,7 @@ module.exports = (_ => { LazyImage: "render" }, after: { - ImageModal: ["render", "componentDidMount"], + ImageModal: ["render", "componentDidMount", "componentWillUnmount"], LazyImage: "componentDidMount", LazyImageZoomable: "render", UserBanner: "default" @@ -338,10 +371,7 @@ module.exports = (_ => { } onStart () { - BDFDB.ListenerUtils.add(this, document.body, "click", BDFDB.dotCNS.message + BDFDB.dotCNS.imagewrapper + BDFDB.dotCNC.imageoriginallink + BDFDB.dotCNS.message + BDFDB.dotCNS.imagewrapper + "img", e => { - clickedImage = (BDFDB.DOMUtils.getParent(BDFDB.dotCN.imagewrapper, e.target) || e.target).querySelector("img") || e.target; - BDFDB.TimeUtils.timeout(_ => {clickedImage = null;}, 1000); - }); + BDFDB.ListenerUtils.add(this, document.body, "click", BDFDB.dotCNS.message + BDFDB.dotCNS.imagewrapper + BDFDB.dotCNC.imageoriginallink + BDFDB.dotCNS.message + BDFDB.dotCNS.imagewrapper + "img", e => this.cacheClickedImage(e.target)); BDFDB.PatchUtils.patch(this, BDFDB.LibraryModules.MediaComponentUtils, "renderImageComponent", { after: e => { @@ -767,17 +797,14 @@ module.exports = (_ => { id: BDFDB.ContextMenuUtils.createItemId(this.name, "copy-file"), action: _ => this.copyFile(urlData.original) }), - BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, { + !document.querySelector(BDFDB.dotCN.imagemodal) && BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, { label: this.labels.context_view.replace("{{var0}}", type), id: BDFDB.ContextMenuUtils.createItemId(this.name, "view-file"), action: _ => { - let img = document.createElement(isVideo ? "video" : "img"); - img.addEventListener(isVideo ? "loadedmetadata" : "load", function() { + const imageThrowaway = document.createElement(isVideo ? "video" : "img"); + imageThrowaway.addEventListener(isVideo ? "loadedmetadata" : "load", function() { BDFDB.LibraryModules.ModalUtils.openModal(modalData => { - if (target) { - clickedImage = (BDFDB.DOMUtils.getParent(BDFDB.dotCN.imagewrapper, target) || target).querySelector("img") || target; - BDFDB.TimeUtils.timeout(_ => {clickedImage = null;}, 1000); - } + _this.cacheClickedImage(target); return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ModalComponents.ModalRoot, Object.assign({ className: BDFDB.disCN.imagemodal }, modalData, { @@ -785,7 +812,7 @@ module.exports = (_ => { "aria-label": BDFDB.LanguageUtils.LanguageStrings.IMAGE, children: BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.ImageModal, { animated: !!isVideo, - src: urlData.src || urlData.file, + src: imageThrowaway.src, original: urlData.original, width: isVideo ? this.videoWidth : this.width, height: isVideo ? this.videoHeight : this.height, @@ -804,7 +831,7 @@ module.exports = (_ => { }), true); }); }); - img.src = urlData.src || urlData.file; + imageThrowaway.src = urlData.src || urlData.file; } }), BDFDB.ContextMenuUtils.createItem(BDFDB.LibraryComponents.MenuItems.MenuItem, { @@ -854,14 +881,24 @@ module.exports = (_ => { ].filter(n => n) }); } - + processImageModal (e) { - if (clickedImage) e.instance._cachedImage = clickedImage; - let url = this.getImageSrc(e.instance._cachedImage && e.instance._cachedImage.src ? e.instance._cachedImage : e.instance.props.src); - url = this.getImageSrc(typeof e.instance.props.children == "function" && e.instance.props.children(Object.assign({}, e.instance.props, {size: e.instance.props})).props.src) || url; - let isVideo = this.isValid(url, "video"); - let messages = this.getAllGalleryImages(); - if (e.returnvalue) { + if (e.methodname == "componentDidMount") { + BDFDB.TimeUtils.clear(viewedImageTimeout); + + let modal = BDFDB.DOMUtils.getParent(BDFDB.dotCN.modal, e.node); + if (modal) modal.className = BDFDB.DOMUtils.formatClassName(modal.className, this.settings.viewerSettings.galleryMode && BDFDB.disCN._imageutilitiesgallery, this.settings.viewerSettings.details && BDFDB.disCN._imageutilitiesdetailsadded); + } + else if (e.methodname == "componentWillUnmount") { + firstViewedImage = null; + viewedImage = null; + cachedImages = null; + this.cleanupListeners("Gallery"); + } + else { + let url = this.getImageSrc(viewedImage && viewedImage.proxy_url || typeof e.instance.props.children == "function" && e.instance.props.children(Object.assign({}, e.instance.props, {size: e.instance.props})).props.src || e.instance.props.src); + let isVideo = this.isValid(url, "video"); + let [children, index] = BDFDB.ReactUtils.findParent(e.returnvalue, {props: [["className", BDFDB.disCN.downloadlink]]}); if (index > -1) { let type = isVideo ? BDFDB.LanguageUtils.LanguageStrings.VIDEO : BDFDB.LanguageUtils.LanguageStrings.IMAGE; @@ -966,60 +1003,94 @@ module.exports = (_ => { ] ].flat(10).filter(n => n) }); - } - let imageIndex = 0, amount = 1; - if (messages.length) { - let data = this.getSiblingsAndPosition(e.instance._cachedImage || url, messages); - imageIndex = data.index; - amount = data.amount; - if (data.previous) { - if (e.instance.previousRef) e.returnvalue.props.children.push(this.createImageWrapper(e.instance, e.instance.previousRef, "previous", BDFDB.LibraryComponents.SvgIcon.Names.LEFT_CARET)); - else this.loadImage(e.instance, data.previous, "previous"); - } - if (data.next) { - if (e.instance.nextRef) e.returnvalue.props.children.push(this.createImageWrapper(e.instance, e.instance.nextRef, "next", BDFDB.LibraryComponents.SvgIcon.Names.RIGHT_CARET)); - else this.loadImage(e.instance, data.next, "next"); + + if (this.settings.viewerSettings.details) { + e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN._imageutilitiesdetailswrapper, + children: [ + e.instance.props.alt && {label: "Alt", text: e.instance.props.alt}, + {label: "Source", text: url}, + {label: "Size", text: `${e.instance.props.width}x${e.instance.props.height}px`}, + {label: "Image", text: `${cachedImages && (cachedImages.index + 1) || 1} of ${cachedImages && cachedImages.amount || 1}`} + ].filter(n => n).map(data => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextElement, { + className: BDFDB.disCN._imageutilitiesdetails, + children: [ + BDFDB.ReactUtils.createElement("div", { + className: BDFDB.disCN._imageutilitiesdetailslabel, + children: data.label + ":" + }), + data.text + ] + })) + })); } } - if (this.settings.viewerSettings.details) e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement("div", { - className: BDFDB.disCN._imageutilitiesdetailswrapper, - children: [ - e.instance.props.alt && {label: "Alt", text: e.instance.props.alt}, - {label: "Source", text: url}, - {label: "Size", text: `${e.instance.props.width}x${e.instance.props.height}px`}, - {label: "Image", text: `${imageIndex + 1} of ${amount}`} - ].filter(n => n).map(data => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.TextElement, { - className: BDFDB.disCN._imageutilitiesdetails, - children: [ - BDFDB.ReactUtils.createElement("div", { - className: BDFDB.disCN._imageutilitiesdetailslabel, - children: data.label + ":" - }), - data.text - ] - })) - })); - } - if (e.node) { - let modal = BDFDB.DOMUtils.getParent(BDFDB.dotCN.modal, e.node); - if (modal) { - modal.className = BDFDB.DOMUtils.formatClassName(modal.className, messages.length && BDFDB.disCN._imageutilitiesgallery, this.settings.viewerSettings.details && BDFDB.disCN._imageutilitiesdetailsadded); - this.cleanupListeners("Gallery"); - if (messages.length) { - document.keydownImageUtilitiesGalleryListener = event => { - if (!document.contains(e.node)) this.cleanupListeners("Gallery"); - else if (!firedEvents.includes("Gallery")) { - firedEvents.push("Gallery"); - if (event.keyCode == 37) this.switchImages(e.instance, "previous"); - else if (event.keyCode == 39) this.switchImages(e.instance, "next"); + + if (this.settings.viewerSettings.galleryMode && viewedImage) { + if (!cachedImages) { + BDFDB.TimeUtils.clear(viewedImageTimeout); + let channel = BDFDB.LibraryModules.ChannelStore.getChannel(viewedImage.channelId); + BDFDB.LibraryModules.APIUtils.get({ + url: channel && channel.guild_id ? BDFDB.DiscordConstants.Endpoints.SEARCH_GUILD(channel && channel.guild_id) : BDFDB.DiscordConstants.Endpoints.SEARCH_CHANNEL(BDFDB.DiscordConstants.ME), + query: BDFDB.LibraryModules.APIEncodeUtils.stringify({ + channel_id: channel && channel.guild_id ? channel && channel.id : null, + has: "image", + around: viewedImage.messageId + }) + }).then(result => { + if (result) { + const messages = result.body.messages.flat(10).reverse(); + cachedImages = {all: this.filterMessagesForImages(messages, viewedImage)}; + const index = this.getImageIndex(cachedImages.all, viewedImage); + cachedImages = Object.assign(cachedImages, { + firstReached: index == 0, + oldestId: messages[0] ? messages[0].id : null, + index: index, + amount: cachedImages.all.length, + newestId: messages[messages.length-1] ? messages[messages.length-1].id : null, + lastReached: index == (cachedImages.all.length - 1) + }); } - }; - document.keyupImageUtilitiesGalleryListener = _ => { - BDFDB.ArrayUtils.remove(firedEvents, "Gallery", true); - if (!document.contains(e.node)) this.cleanupListeners("Gallery"); - }; - document.addEventListener("keydown", document.keydownImageUtilitiesGalleryListener); - document.addEventListener("keyup", document.keyupImageUtilitiesGalleryListener); + else cachedImages = { + firstReached: null, + oldestId: null, + all: [], + index: -1, + amount: 0, + newestId: null, + lastReached: null + }; + BDFDB.ReactUtils.forceUpdate(e.instance); + }); + return BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Spinner, { + type: BDFDB.LibraryComponents.Spinner.Type.SPINNING_CIRCLE + }); + } + else { + if (cachedImages.all[cachedImages.index - 1]) e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement(LazyImageSiblingComponent, { + className: BDFDB.disCN._imageutilitiesprevious, + modalInstance: e.instance, + url: this.getImageSrc(cachedImages.all[cachedImages.index - 1].thumbnail || cachedImages.all[cachedImages.index - 1]), + offset: -1, + svgIcon: BDFDB.LibraryComponents.SvgIcon.Names.LEFT_CARET + })); + if (cachedImages.all[cachedImages.index + 1]) e.returnvalue.props.children.push(BDFDB.ReactUtils.createElement(LazyImageSiblingComponent, { + className: BDFDB.disCN._imageutilitiesnext, + modalInstance: e.instance, + url: this.getImageSrc(cachedImages.all[cachedImages.index + 1].thumbnail || cachedImages.all[cachedImages.index + 1]), + offset: 1, + svgIcon: BDFDB.LibraryComponents.SvgIcon.Names.RIGHT_CARET + })); + if (cachedImages.all[cachedImages.index - 1] || cachedImages.all[cachedImages.index + 1]) { + this.addListener("keydown", "Gallery", event => { + if (!firedEvents.includes("Gallery")) { + firedEvents.push("Gallery"); + if (event.keyCode == 37) this.switchImages(e.instance, -1); + else if (event.keyCode == 39) this.switchImages(e.instance, 1); + } + }); + this.addListener("keyup", "Gallery", _ => BDFDB.ArrayUtils.remove(firedEvents, "Gallery", true)); + } } } } @@ -1089,7 +1160,7 @@ module.exports = (_ => { document.addEventListener("mouseup", releasing); this.cleanupListeners("Zoom"); - document.wheelImageUtilitiesZoomListener = event2 => { + this.addListener("wheel", "Zoom", event2 => { if (!document.contains(e.node)) this.cleanupListeners("Zoom"); else { if (event2.deltaY < 0 && (this.settings.zoomSettings.zoomLevel + 0.1) <= this.defaults.zoomSettings.zoomLevel.maxValue) { @@ -1101,8 +1172,8 @@ module.exports = (_ => { lens.update(); } } - }; - document.keydownImageUtilitiesZoomListener = event2 => { + }); + this.addListener("keydown", "Zoom", event2 => { if (!document.contains(e.node)) this.cleanupListeners("Zoom"); else if (!firedEvents.includes("Zoom")) { firedEvents.push("Zoom"); @@ -1115,14 +1186,11 @@ module.exports = (_ => { lens.update(); } } - }; - document.keyupImageUtilitiesZoomListener = _ => { + }); + this.addListener("keyup", "Zoom", _ => { BDFDB.ArrayUtils.remove(firedEvents, "Zoom", true); if (!document.contains(e.node)) this.cleanupListeners("Zoom"); - }; - document.addEventListener("wheel", document.wheelImageUtilitiesZoomListener); - document.addEventListener("keydown", document.keydownImageUtilitiesZoomListener); - document.addEventListener("keyup", document.keyupImageUtilitiesZoomListener); + }); vanishObserver = new MutationObserver(changes => {if (!document.contains(e.node)) releasing();}); vanishObserver.observe(appMount, {childList: true, subtree: true}); @@ -1130,10 +1198,9 @@ module.exports = (_ => { } } else { - if (!e.instance.props.resized && this.settings.resizeSettings.imageViewer && BDFDB.ReactUtils.findOwner(BDFDB.ObjectUtils.get(e, `instance.${BDFDB.ReactUtils.instanceKey}`), {name: "ImageModal", up: true})) { - let data = this.settings.viewerSettings.galleryMode ? this.getSiblingsAndPosition(e.instance.props.src, this.getAllGalleryImages()) : {}; + if (this.settings.resizeSettings.imageViewer && BDFDB.ReactUtils.findOwner(BDFDB.ObjectUtils.get(e, `instance.${BDFDB.ReactUtils.instanceKey}`), {name: "ImageModal", up: true})) { let aRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount)); - let ratio = Math.min((aRects.width * (data.previous || data.next ? 0.8 : 1) - 20) / e.instance.props.width, (aRects.height - (this.settings.viewerSettings.details ? 280 : 100)) / e.instance.props.height); + let ratio = Math.min((aRects.width * (this.settings.viewerSettings.galleryMode ? 0.8 : 1) - 20) / e.instance.props.width, (aRects.height - (this.settings.viewerSettings.details ? 280 : 100)) / e.instance.props.height); let width = Math.round(ratio * e.instance.props.width); let height = Math.round(ratio * e.instance.props.height); e.instance.props.width = width; @@ -1143,13 +1210,19 @@ module.exports = (_ => { e.instance.props.src = e.instance.props.src.replace(/width=\d+/, `width=${width}`).replace(/height=\d+/, `height=${height}`); e.instance.props.resized = true; } - if (!e.instance.props.resized && this.settings.resizeSettings.messages && BDFDB.ReactUtils.findOwner(BDFDB.ObjectUtils.get(e, `instance.${BDFDB.ReactUtils.instanceKey}`), {name: "LazyImageZoomable", up: true})) { + if (this.settings.resizeSettings.messages && BDFDB.ReactUtils.findOwner(BDFDB.ObjectUtils.get(e, `instance.${BDFDB.ReactUtils.instanceKey}`), {name: "LazyImageZoomable", up: true})) { + let aRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount)); let mRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCNC.messageaccessory + BDFDB.dotCN.messagecontents)); let mwRects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.messagewrapper)); if (mRects.width || mwRects.width) { let ratio = (mRects.width || (mwRects.width - 120)) / e.instance.props.width; let width = Math.round(ratio * e.instance.props.width); let height = Math.round(ratio * e.instance.props.height); + if (height > (aRects.height * 0.66)) { + let newHeight = Math.round(aRects.height * 0.66); + width = (newHeight/height) * width; + height = newHeight; + } e.instance.props.width = width; e.instance.props.maxWidth = width; e.instance.props.height = height; @@ -1204,6 +1277,19 @@ module.exports = (_ => { }; } + cacheClickedImage (target) { + const image = (BDFDB.DOMUtils.getParent(BDFDB.dotCN.imagewrapper, target) || target).querySelector("img") || target; + if (!image) return; + const message = BDFDB.ReactUtils.findValue(image, "message", {up: true}); + if (!message) return; + firstViewedImage = {messageId: message.id, channelId: message.channel_id, proxy_url: image.src}; + viewedImage = firstViewedImage; + viewedImageTimeout = BDFDB.TimeUtils.timeout(_ => { + firstViewedImage = null; + viewedImage = null; + }, 1000); + } + downloadFile (url, path, fallbackUrl, alternativeName) { url = url.startsWith("/assets") ? (window.location.origin + url) : url; BDFDB.LibraryRequires.request(url, {agentOptions: {rejectUnauthorized: false}, encoding: null}, (error, response, body) => { @@ -1282,91 +1368,118 @@ module.exports = (_ => { if (ext == "quicktime") ext = "mov"; return ext; } - - getAllGalleryImages () { - if (!this.settings.viewerSettings.galleryMode) return []; - return Array.from(document.querySelectorAll(BDFDB.dotCNS.messagelistitem + BDFDB.dotCNS.imagewrapper + "img")); - } - - getSiblingsAndPosition (imgOrUrl, images) { - images = images.flat(10).filter(img => !BDFDB.DOMUtils.getParent(BDFDB.dotCN.spoilerhidden, img)); - let next, previous, index = 0, amount = images.length; - for (let i = 0; i < amount; i++) if (this.isSameImage(imgOrUrl, images[i])) { - index = i; - previous = images[i-1]; - next = images[i+1]; - break; - } - return {next, previous, index, amount}; - } - - isSameImage (src, img) { - return img.src && (Node.prototype.isPrototypeOf(src) && img == src || !Node.prototype.isPrototypeOf(src) && this.getImageSrc(img) == this.getImageSrc(src)); - } getImageSrc (img) { if (!img) return null; - return (typeof img == "string" ? img : (img.src || (img.querySelector("canvas") ? img.querySelector("canvas").src : ""))).split("?width=")[0]; + return (typeof img == "string" ? img : (img.proxy_url || img.src || (img.querySelector("canvas") ? img.querySelector("canvas").src : ""))).split("?width=")[0]; } - createImageWrapper (instance, imgRef, type, svgIcon) { - return BDFDB.ReactUtils.createElement("div", { - className: BDFDB.disCNS._imageutilitiessibling + BDFDB.disCN[`_imageutilities${type}`], - onClick: _ => this.switchImages(instance, type), - children: [ - imgRef, - BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.SvgIcon, { - className: BDFDB.disCNS._imageutilitiesswitchicon + BDFDB.disCN.svgicon, - name: svgIcon + getImageIndex (messages, img) { + return messages.findIndex(i => i.messageId == img.messageId && (messages.filter(n => n.messageId == i.messageId).length < 2 || i.url && img.proxy_url.indexOf(i.url) > -1 || i.proxy_url && img.proxy_url.indexOf(i.proxy_url) > -1)); + } + + filterMessagesForImages (messages, img) { + return messages.filter(m => m && m.hit && (m.id == firstViewedImage.messageId || m.id == img.messageId || m.embeds.length || m.attachments.filter(a => !a.filename.startsWith("SPOILER_")).length)).map(m => [m.attachments, m.embeds].flat(10).filter(n => n).map(i => Object.assign({messageId: m.id, channelId: img.channelId}, i, i.thumbnail, i.video))).flat(10); + } + + switchImages (modalInstance, offset) { + const newIndex = parseInt(cachedImages.index) + parseInt(offset); + if (newIndex < 0 || newIndex > (cachedImages.amount - 1)) return; + + cachedImages.index = newIndex; + const oldImage = viewedImage; + viewedImage = cachedImages.all[cachedImages.index]; + + if (offset > 0 && !cachedImages.lastReached && cachedImages.index == (cachedImages.amount - 1)) { + let channel = BDFDB.LibraryModules.ChannelStore.getChannel(viewedImage.channelId); + BDFDB.LibraryModules.APIUtils.get({ + url: channel && channel.guild_id ? BDFDB.DiscordConstants.Endpoints.SEARCH_GUILD(channel && channel.guild_id) : BDFDB.DiscordConstants.Endpoints.SEARCH_CHANNEL(BDFDB.DiscordConstants.ME), + query: BDFDB.LibraryModules.APIEncodeUtils.stringify({ + channel_id: channel && channel.guild_id ? channel && channel.id : null, + has: "image", + min_id: (BigInt(cachedImages.newestId) - BigInt(1)).toString() }) - ] - }); - } - - loadImage (instance, img, type) { - let imageThrowaway = document.createElement("img"); - let src = this.getImageSrc(img); - imageThrowaway.src = src; - imageThrowaway.onload = _ => { - let arects = BDFDB.DOMUtils.getRects(document.querySelector(BDFDB.dotCN.appmount)); - let resizeY = (arects.height/imageThrowaway.naturalHeight) * 0.65, resizeX = (arects.width/imageThrowaway.naturalWidth) * 0.8; - let resize = resizeX < resizeY ? resizeX : resizeY; - let newHeight = imageThrowaway.naturalHeight * resize; - let newWidth = imageThrowaway.naturalWidth * resize; - instance[type + "Img"] = img; - instance[type + "Ref"] = BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.LazyImage, { - src: src, - height: imageThrowaway.naturalHeight, - width: imageThrowaway.naturalWidth, - maxHeight: newHeight, - maxWidth: newWidth, + }).then(result => { + if (result) { + const messages = result.body.messages.flat(10).reverse(); + const newCachedImages = this.filterMessagesForImages(messages, viewedImage); + const lastOldIndex = this.getImageIndex(newCachedImages, cachedImages.all[cachedImages.all.length-1]); + if (lastOldIndex > -1) { + cachedImages = Object.assign(cachedImages, {all: [].concat(cachedImages.all, newCachedImages.slice(lastOldIndex + 1))}); + const index = this.getImageIndex(cachedImages.all, viewedImage); + cachedImages = Object.assign(cachedImages, { + index: index, + amount: cachedImages.all.length, + newestId: messages[messages.length-1] ? messages[messages.length-1].id : null, + lastReached: index == (cachedImages.all.length - 1) + }); + } + BDFDB.ReactUtils.forceUpdate(modalInstance); + } }); - BDFDB.ReactUtils.forceUpdate(instance); - }; + } + if (offset < 0 && !cachedImages.firstReached && cachedImages.index == 0) { + console.log("first"); + let channel = BDFDB.LibraryModules.ChannelStore.getChannel(viewedImage.channelId); + BDFDB.LibraryModules.APIUtils.get({ + url: channel && channel.guild_id ? BDFDB.DiscordConstants.Endpoints.SEARCH_GUILD(channel && channel.guild_id) : BDFDB.DiscordConstants.Endpoints.SEARCH_CHANNEL(BDFDB.DiscordConstants.ME), + query: BDFDB.LibraryModules.APIEncodeUtils.stringify({ + channel_id: channel && channel.guild_id ? channel && channel.id : null, + has: "image", + max_id: (BigInt(cachedImages.oldestId) + BigInt(1)).toString() + }) + }).then(result => { + if (result) { + const messages = result.body.messages.flat(10).reverse(); + const newCachedImages = this.filterMessagesForImages(messages, viewedImage); + const firstOldIndex = this.getImageIndex(newCachedImages, cachedImages.all[0]); + if (firstOldIndex > -1) { + cachedImages = Object.assign(cachedImages, {all: [].concat(newCachedImages.slice(0, firstOldIndex), cachedImages.all)}); + const index = this.getImageIndex(cachedImages.all, viewedImage); + cachedImages = Object.assign(cachedImages, { + firstReached: index == 0, + oldestId: messages[0] ? messages[0].id : null, + index: index, + amount: cachedImages.all.length + }); + } + BDFDB.ReactUtils.forceUpdate(modalInstance); + } + }); + } + let isVideo = this.isValid(viewedImage.proxy_url, "video"); + modalInstance.props.animated = !!isVideo; + modalInstance.props.original = viewedImage.proxy_url; + modalInstance.props.placeholder = viewedImage.thumbnail && viewedImage.thumbnail.proxy_url || viewedImage.proxy_url; + modalInstance.props.src = viewedImage.proxy_url; + modalInstance.props.width = viewedImage.width; + modalInstance.props.height = viewedImage.height; + modalInstance.props.children = !isVideo ? null : (videoData => BDFDB.ReactUtils.createElement(BDFDB.LibraryComponents.Video, { + src: viewedImage.proxy_url, + width: videoData.size.width, + height: videoData.size.height, + naturalWidth: viewedImage.width, + naturalHeight: viewedImage.height, + play: true + })); + BDFDB.ReactUtils.forceUpdate(modalInstance); } - switchImages (instance, type) { - let img = instance[type + "Img"]; - let imgRef = instance[type + "Ref"]; - if (!img || !imgRef) return; - delete instance.previousRef; - delete instance.nextRef; - delete instance.previousImg; - delete instance.nextImg; - instance.props.original = imgRef.props.src; - instance.props.placeholder = imgRef.props.src; - instance.props.src = imgRef.props.src; - instance.props.height = imgRef.props.height; - instance.props.width = imgRef.props.width; - instance._cachedImage = img; - BDFDB.ReactUtils.forceUpdate(instance); + addListener (eventType, type, callback) { + if (!type || !eventType || typeof callback != "function") return; + if (!eventTypes[type]) eventTypes[type] = []; + if (!eventTypes[type].includes(eventType)) eventTypes[type].push(eventType); + document.removeEventListener(eventType, document[`${eventType}${this.name}${type}Listener`]); + delete document[`${eventType}${this.name}${type}Listener`]; + document[`${eventType}${this.name}${type}Listener`] = callback; + document.addEventListener(eventType, document[`${eventType}${this.name}${type}Listener`]); } cleanupListeners (type) { - if (!type) return; - for (let eventType of ["wheel", "keydown", "keyup"]) { - document.removeEventListener("wheel", document[`${eventType}ImageUtilities${type}Listener`]); - delete document[`${eventType}ImageUtilities${type}Listener`]; + if (!type || !eventTypes[type]) return; + for (let eventType of eventTypes[type]) { + document.removeEventListener(eventType, document[`${eventType}${this.name}${type}Listener`]); + delete document[`${eventType}${this.name}${type}Listener`]; } } @@ -1746,7 +1859,6 @@ module.exports = (_ => { submenu_disabled: "All disabled", toast_copy_failed: "{{var0}} could not be copied to the Clipboard", toast_copy_success: "{{var0}} was copied to the Clipboard", - toast_message_deleted: "Message was successfully deleted", toast_save_failed: "{{var0}} could not be saved in '{{var1}}'", toast_save_success: "{{var0}} was saved in '{{var1}}'" };