BIV v1.1.0 IMAGE ZOOM

This commit is contained in:
_Lighty_ 2020-03-04 16:32:57 +01:00
parent 293109d114
commit 145e036f4d
3 changed files with 339 additions and 118 deletions

View File

@ -37,16 +37,16 @@ var BetterImageViewer = (() => {
twitter_username: '' twitter_username: ''
} }
], ],
version: '1.0.4', version: '1.1.0',
description: 'Telegram image viewer ported to Discord. Adds ability to go between images in the current channel with arrow keys, or on screen buttons. Also provides info about the image, who posted it and when.', description: 'Adds ability to go between images in the current channel with arrow keys, or on screen buttons, and has click to zoom. Also provides info about the image, who posted it and when.',
github: 'https://github.com/1Lighty', github: 'https://github.com/1Lighty',
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/BetterImageViewer/BetterImageViewer.plugin.js' github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/BetterImageViewer/BetterImageViewer.plugin.js'
}, },
changelog: [ changelog: [
{ {
title: 'fixes', title: 'IMAGE ZOOM IS HERE!',
type: 'fixed', type: 'added',
items: ['Fixed plugin refusing to work in PTB and canary'] items: ['*SOME* people kept asking, so here you go.\nAdded image zoom, simply activate by click and holding on the image.', 'Use scroll wheel to zoom in and out, hold shift while scrolling to change lens size.', "If you prefer using a different plugin to handle zooming for you, that's fine too, there is a toggle to disable it in settings.", 'The click and hold can of course be easily changed in the settings to your prefered method of zooming.\n![zoommode](https://i.imgur.com/A8HjQb9.png)', 'There are other options too, for those that may want it!\n![moreoptions](https://i.imgur.com/JGNe7Re.png)']
} }
], ],
defaultConfig: [ defaultConfig: [
@ -120,13 +120,74 @@ var BetterImageViewer = (() => {
value: false value: false
} }
] ]
},
{
type: 'category',
id: 'zoom',
name: 'Image Zoom settings',
collapsible: true,
shown: false,
settings: [
{
name: 'Enable image zoom',
id: 'enabled',
type: 'switch',
value: true
},
{
name: 'Zoom enable mode',
id: 'enableMode',
type: 'radio',
value: 0,
options: [
{ name: 'Click and hold', value: 0 },
{ name: 'Click to toggle', value: 1 },
{ name: 'Scroll to toggle', value: 2 }
]
},
{
name: 'Anti-aliasing',
note: 'On low resolution images, pixels become blurry',
id: 'interp',
type: 'switch',
value: true
},
{
name: 'Round lens',
note: 'why',
id: 'round',
type: 'switch',
value: true
},
{
name: 'Allow lens to go out of bounds',
note: 'Allows the lens to go beyond the border of the image',
id: 'outOfBounds',
type: 'switch',
value: false
},
{
name: 'Allow lens to clip out of view',
note: 'Allows the lens to go beyond the window',
id: 'outOfScreen',
type: 'switch',
value: true
},
{
name: 'Movement smoothing',
note: 'Not recommended to disable. Smooths out movement and zoom',
id: 'smoothing',
type: 'switch',
value: true
}
]
} }
] ]
}; };
/* Build */ /* Build */
const buildPlugin = ([Plugin, Api]) => { const buildPlugin = ([Plugin, Api]) => {
const { Utilities, WebpackModules, DiscordModules, ReactComponents, DiscordAPI, Logger, Patcher, PluginUtilities, PluginUpdater } = Api; const { Utilities, WebpackModules, DiscordModules, ReactComponents, DiscordAPI, Logger, Patcher, PluginUtilities, PluginUpdater, Structs } = Api;
const { React, ReactDOM, ModalStack, DiscordConstants, Dispatcher, GuildStore, GuildMemberStore, TextElement, MessageStore, APIModule, NavigationUtils } = DiscordModules; const { React, ReactDOM, ModalStack, DiscordConstants, Dispatcher, GuildStore, GuildMemberStore, TextElement, MessageStore, APIModule, NavigationUtils } = DiscordModules;
let PluginBrokenFatal = false; let PluginBrokenFatal = false;
@ -179,6 +240,8 @@ var BetterImageViewer = (() => {
} }
const ReactSpring = WebpackModules.getByProps('useTransition'); const ReactSpring = WebpackModules.getByProps('useTransition');
const Easing = WebpackModules.getByProps('Easing').Easing;
class Image extends (() => { class Image extends (() => {
const Image = WebpackModules.getByDisplayName('Image'); const Image = WebpackModules.getByDisplayName('Image');
if (Image) return Image; if (Image) return Image;
@ -188,19 +251,16 @@ var BetterImageViewer = (() => {
})() { })() {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = { zooming: false, visible: false, panelWH: [props.hiddenSettings.panelWH, props.hiddenSettings.panelWH], panelX: [0, 0], panelY: [0, 0], offsetX: [0, 0], offsetY: [0, 0], zoom: [1.5, 1.5] };
hovered: false,
x: 0,
y: 0,
startX: 0,
startY: 0,
zoom: 1.5,
offsetX: 0,
offsetY: 0
};
XenoLib._.bindAll(this, ['handleMouseDown', 'handleMouseUp', 'handleMouseWheel']); XenoLib._.bindAll(this, ['handleMouseDown', 'handleMouseUp', 'handleMouseWheel']);
const throttled = XenoLib._.throttle(this.handleMouseMove.bind(this), 50); this._handleMouseMove = this.handleMouseMove.bind(this);
this.handleMouseMove = e => this.state.hovered && throttled(e.clientX, e.clientY); this.handleMouseMove = e => this.state.zooming && this._handleMouseMove(e.clientX, e.clientY);
this._handleSaveLensWHChangeDC = new TimingModule.DelayedCall(1000, this._handleSaveLensWHChange.bind(this));
this._WHCS = true;
this.handleSaveLensWHChange = () => {
this._WHCS = false;
this._handleSaveLensWHChangeDC();
};
} }
componentDidMount() { componentDidMount() {
if (super.componentDidMount) super.componentDidMount(); if (super.componentDidMount) super.componentDidMount();
@ -214,46 +274,103 @@ var BetterImageViewer = (() => {
window.removeEventListener('mouseup', this.handleMouseUp); window.removeEventListener('mouseup', this.handleMouseUp);
window.removeEventListener('mousemove', this.handleMouseMove); window.removeEventListener('mousemove', this.handleMouseMove);
window.removeEventListener('mousewheel', this.handleMouseWheel); window.removeEventListener('mousewheel', this.handleMouseWheel);
this._handleSaveLensWHChangeDC.cancel();
this._handleSaveLensWHChange();
} }
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState, snapshot) {
if (super.componentDidUpdate) super.componentDidUpdate(prevProps, prevState); if (super.componentDidUpdate) super.componentDidUpdate(prevProps, prevState, snapshot);
this.getRawImage(); if (this.props.src !== prevProps.src) {
this.getRawImage();
}
}
setStateImmediate(state) {
for (const prop in state) this.state[prop] = state[prop];
}
_handleSaveLensWHChange() {
Dispatcher.dispatch({ type: 'BIV_LENS_WH_CHANGE', value: this.state.panelWH[0] });
this._WHCS = true;
} }
handleMouseDown(e) { handleMouseDown(e) {
console.log(e); if (e.button !== DiscordConstants.MouseButtons.PRIMARY) return;
this.setState({ hovered: !this.state.hovered, startX: e.clientX, startY: e.clientY, x: e.clientX, y: e.clientY }); if (this.state.zooming) return this.setState({ zooming: false });
else if (this.props.settings.enableMode === 2) return; /* scroll to toggle */
this.state;
this.setStateImmediate({ zooming: true, visible: true });
this._handleMouseMove(e.clientX, e.clientY, true);
e.preventDefault();
} }
handleMouseUp() { handleMouseUp() {
return; /* click and hold mode */
this.setState({ hovered: false }); if (this.props.settings.enableMode !== 0) return;
this.setState({ zooming: false });
} }
handleMouseMove(x, y) { handleMouseMove(cx, cy, start) {
x = Math.min(this._ref.offsetLeft + this._ref.offsetWidth, Math.max(this._ref.offsetLeft, x)); if (!this.props.settings.outOfBounds) {
y = Math.min(this._ref.offsetTop + this._ref.offsetHeight, Math.max(this._ref.offsetTop, y)); cx = Math.min(this._ref.offsetLeft + this._ref.offsetWidth, Math.max(this._ref.offsetLeft, cx));
const offsetX = ((x - this._ref.offsetLeft) / (this._ref.offsetWidth + 250)) * (this._ref.offsetWidth + 250); cy = Math.min(this._ref.offsetTop + this._ref.offsetHeight, Math.max(this._ref.offsetTop, cy));
const offsetY = ((y - this._ref.offsetTop) / (this._ref.offsetHeight + 250)) * (this._ref.offsetHeight + 250); }
this.setState({ x, y, offsetX, offsetY }); let panelWH = this.state.panelWH[0];
if (!this.props.settings.outOfScreen) {
if (Structs.Screen.height < Structs.Screen.width && panelWH > Structs.Screen.height) panelWH = Structs.Screen.height - 2;
else if (Structs.Screen.height > Structs.Screen.width && panelWH > Structs.Screen.width) panelWH = Structs.Screen.width - 2;
}
const offsetX = cx - this._ref.offsetLeft;
const offsetY = cy - this._ref.offsetTop;
let panelX = cx - panelWH / 2;
let panelY = cy - panelWH / 2;
if (!this.props.settings.outOfScreen) {
if (panelX < 0) panelX = 0;
else if (panelX + panelWH > Structs.Screen.width) panelX = Structs.Screen.width - (panelWH + 2);
if (panelY < 0) panelY = 0;
else if (panelY + panelWH > Structs.Screen.height) panelY = Structs.Screen.height - (panelWH + 2);
}
this.setState({ panelX: [panelX, start ? panelX : this.state.panelX[1]], panelY: [panelY, start ? panelY : this.state.panelY[1]], offsetX: [offsetX, start ? offsetX : this.state.offsetX[1]], offsetY: [offsetY, start ? offsetY : this.state.offsetY[1]], zoom: [this.state.zoom[0], start ? this.state.zoom[0] : this.state.zoom[1]], panelWH: [panelWH, start ? panelWH : this.state.panelWH[1]] });
} }
handleMouseWheel(e) { handleMouseWheel(e) {
/* scroll to toggle mode */
const scrollToggle = this.props.settings.enableMode === 2;
if ((!scrollToggle || (scrollToggle && e.shiftKey)) && !this.state.zooming) return;
if (e.deltaY < 0) { if (e.deltaY < 0) {
this.setState({ if (e.shiftKey) {
zoom: Math.min(this.state.zoom * 1.1, 10) this.state.panelWH[0] *= 1.1;
}); if (Structs.Screen.height > Structs.Screen.width && this.state.panelWH[0] > (this.props.settings.outOfScreen ? Structs.Screen.height * 2 : Structs.Screen.height)) this.state.panelWH[0] = this.props.settings.outOfScreen ? Structs.Screen.height * 2 : Structs.Screen.height - 2;
else if (Structs.Screen.height < Structs.Screen.width && this.state.panelWH[0] > (this.props.settings.outOfScreen ? Structs.Screen.width * 2 : Structs.Screen.width)) this.state.panelWH[0] = this.props.settings.outOfScreen ? Structs.Screen.width * 2 : Structs.Screen.width - 2;
this.state.panelWH[0] = Math.ceil(this.state.panelWH[0]);
this._handleMouseMove(e.clientX, e.clientY);
this._handleSaveLensWHChangeDC.delay();
} else {
const nextZoom = [Math.min(this.state.zoom[0] * 1.1, 60), this.state.zoom[1]];
if (scrollToggle && !this.state.zooming) {
this.setStateImmediate({ zooming: true, visible: true, zoom: nextZoom });
this._handleMouseMove(e.clientX, e.clientY, true);
} else this.setState({ zoom: nextZoom });
}
} else if (e.deltaY > 0) { } else if (e.deltaY > 0) {
this.setState({ if (e.shiftKey) {
zoom: Math.max(this.state.zoom * 0.9) this.state.panelWH[0] *= 0.9;
}); if (this.state.panelWH[0] < 75) this.state.panelWH[0] = 75;
this.state.panelWH[0] = Math.ceil(this.state.panelWH[0]);
this._handleMouseMove(e.clientX, e.clientY);
this._handleSaveLensWHChangeDC.delay();
} else {
const nextZoom = this.state.zoom[0] * 0.9;
if (scrollToggle && nextZoom < 1.5) this.setStateImmediate({ zooming: false });
this.setState({ zoom: [Math.max(nextZoom, 1.5), this.state.zoom[1]] });
}
} }
} }
getRawImage() { getRawImage() {
if (this.__BIV_updating) return; if (this.__BIV_updating || this.props.__BIV_animated) return;
this.__BIV_updating = true; this.__BIV_updating = true;
const src = this.props.src; const src = this.props.src;
const fullSource = (() => { const fullSource = (() => {
const split = src.split('?')[0]; const split = src.split('?')[0];
const SaveToRedux = BdApi.getPlugin('SaveToRedux'); const SaveToRedux = BdApi.getPlugin && BdApi.getPlugin('SaveToRedux');
if (SaveToRedux) return SaveToRedux.formatURL(split, src.substr(src.indexOf('?')).indexOf('size=') !== -1, '', '').url; const needsSize = src.substr(src.indexOf('?')).indexOf('size=') !== -1;
return split; try {
if (SaveToRedux) return SaveToRedux.formatURL(split, needsSize, '', '').url;
} catch (_) {}
return split + (needsSize ? '?size=2048' : '');
})(); })();
this.__BIV_updating = true; this.__BIV_updating = true;
this.setState({ raw: fullSource }); this.setState({ raw: fullSource });
@ -262,49 +379,75 @@ var BetterImageViewer = (() => {
render() { render() {
const ret = super.render(); const ret = super.render();
ret.props.onMouseDown = this.handleMouseDown; ret.props.onMouseDown = this.handleMouseDown;
delete ret.props.settings;
ret.ref = e => (this._ref = e); ret.ref = e => (this._ref = e);
if (this.state.hovered) { if (this.state.visible) {
ret.props.children.push( ret.props.children.push(
ReactDOM.createPortal( ReactDOM.createPortal(
React.createElement( React.createElement(
ReactSpring.Spring, ReactSpring.Spring,
{ {
native: true, native: true,
from: { x: this.state.startX, y: this.state.startY }, from: { opacity: 0 },
to: { x: this.state.x, y: this.state.y }, to: { opacity: this.state.zooming ? 1 : 0 },
config: { mass: 1, tension: 2000, friction: 100 } config: { duration: 100 },
onRest: () => {
!this.state.zooming && this.setState({ visible: false });
}
}, },
e => ea => [
React.createElement( React.createElement(ReactSpring.animated.div, {
ReactSpring.animated.div, style: {
{ opacity: ea.opacity
style: {
width: 500,
height: 500,
position: 'absolute',
top: e.y,
left: e.x,
overflow: 'hidden'
}
}, },
React.createElement( className: 'BIV-zoom-backdrop'
'div', }),
{ React.createElement(
style: { ReactSpring.Spring,
position: 'absolute', {
transform: `scale(${this.state.zoom})`, native: true,
transformOrigin: '0px 0px', from: { panelX: this.state.panelX[1], panelY: this.state.panelY[1], panelWH: this.state.panelWH[1], offsetX: this.state.offsetX[1], offsetY: this.state.offsetY[1], zoom: this.state.zoom[1], opacity: 0 },
left: -this.state.offsetX, to: { panelX: this.state.panelX[0], panelY: this.state.panelY[0], panelWH: this.state.panelWH[0], offsetX: this.state.offsetX[0], offsetY: this.state.offsetY[0], zoom: this.state.zoom[0], opacity: 1 },
top: -this.state.offsetY immediate: !this.props.settings.smoothing,
} config: { mass: 1, tension: 1500, friction: 75, easing: Easing.linear, clamp: false }
}, },
React.createElement('img', { e =>
src: this.state.raw, React.createElement(
width: this.props.width, ReactSpring.animated.div,
height: this.props.height {
}) style: {
) width: e.panelWH,
height: e.panelWH,
left: e.panelX,
top: e.panelY,
opacity: ea.opacity
},
className: XenoLib.joinClassNames('BIV-zoom-lens', { 'BIV-zoom-lens-round': this.props.settings.round })
},
React.createElement(
ReactSpring.animated.div,
{
style: {
position: 'absolute',
left: ReactSpring.to([e.zoom, e.offsetX, e.panelWH], (z, x, wh) => -x * z + wh / 2),
top: ReactSpring.to([e.zoom, e.offsetY, e.panelWH], (z, y, wh) => -y * z + wh / 2)
}
},
React.createElement(this.props.__BIV_animated ? ReactSpring.animated.video : ReactSpring.animated.img, {
src: this.props.__BIV_animated ? this.props.__BIV_src : this.state.raw,
width: e.zoom.to(e => e * this.props.width),
height: e.zoom.to(e => e * this.props.height),
style: this.props.settings.interp
? undefined
: {
imageRendering: 'pixelated'
},
...(this.props.__BIV_animated ? { autoPlay: true, muted: true, loop: true } : {})
})
)
)
) )
]
), ),
overlayDOMNode overlayDOMNode
) )
@ -321,10 +464,10 @@ var BetterImageViewer = (() => {
PluginBrokenFatal = true; PluginBrokenFatal = true;
return class error {}; return class error {};
})() { })() {
componentDidUpdate(props, prevState) { componentDidUpdate(props, prevState, snapshot) {
this._cancellers.forEach(e => e()); this._cancellers.forEach(e => e());
this._cancellers.clear(); this._cancellers.clear();
super.componentDidUpdate(props, prevState); super.componentDidUpdate(props, prevState, snapshot);
if (this.__BIV_updating) return; if (this.__BIV_updating) return;
this.__BIV_updating = true; this.__BIV_updating = true;
const max = ImageUtils.zoomFit(this.props.width, this.props.height); const max = ImageUtils.zoomFit(this.props.width, this.props.height);
@ -1023,6 +1166,7 @@ var BetterImageViewer = (() => {
constructor() { constructor() {
super(); super();
XenoLib.changeName(__filename, this.name); XenoLib.changeName(__filename, this.name);
this.handleWHChange = this.handleWHChange.bind(this);
const oOnStart = this.onStart.bind(this); const oOnStart = this.onStart.bind(this);
this._startFailure = message => { this._startFailure = message => {
PluginUpdater.checkForUpdate(this.name, this.version, this._config.info.github_raw); PluginUpdater.checkForUpdate(this.name, this.version, this._config.info.github_raw);
@ -1049,13 +1193,17 @@ var BetterImageViewer = (() => {
this.promises = { state: { cancelled: false } }; this.promises = { state: { cancelled: false } };
if (PluginBrokenFatal) return this._startFailure('Plugin is in a broken state.'); if (PluginBrokenFatal) return this._startFailure('Plugin is in a broken state.');
if (NoImageZoom) this._startFailure('Image zoom is broken.'); if (NoImageZoom) this._startFailure('Image zoom is broken.');
if (BdApi.getPlugin && BdApi.getPlugin('ImageZoom') && global.pluginCookie && pluginCookie['ImageZoom']) XenoLib.Notifications.warning(`[**${this.name}**] Using ImageZoom while having the zoom function in BetterImageViewer enabled is unsupported! Please disable one or the other.`, { timeout: 15000 });
this.hiddenSettings = XenoLib.loadData(this.name, 'hidden', { panelWH: 500 });
this.patchAll(); this.patchAll();
Dispatcher.subscribe('MESSAGE_DELETE', this.handleMessageDelete); Dispatcher.subscribe('MESSAGE_DELETE', this.handleMessageDelete);
Dispatcher.subscribe('MESSAGE_DELETE_BULK', this.handlePurge); Dispatcher.subscribe('MESSAGE_DELETE_BULK', this.handlePurge);
Dispatcher.subscribe('BIV_LENS_WH_CHANGE', this.handleWHChange);
PluginUtilities.addStyle( PluginUtilities.addStyle(
this.short + '-CSS', this.short + '-CSS',
` `
.BIV-left, .BIV-right { .BIV-left,
.BIV-right {
position: absolute; position: absolute;
top: 90px; top: 90px;
bottom: 90px; bottom: 90px;
@ -1064,41 +1212,41 @@ var BetterImageViewer = (() => {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
transition: all .25s ease-in-out; transition: all 0.25s ease-in-out;
color: gray; color: gray;
} }
.BIV-disabled { .BIV-disabled {
color: #4d4d4d; color: #4d4d4d;
} }
.BIV-left.BIV-inactive,
.BIV-left.BIV-inactive, .BIV-right.BIV-inactive { .BIV-right.BIV-inactive {
opacity: 0; opacity: 0;
} }
.BIV-info.BIV-inactive { .BIV-info.BIV-inactive {
opacity: 0.65; opacity: 0.65;
} }
.BIV-left:not(.BIV-disabled):hover,
.BIV-left:not(.BIV-disabled):hover, .BIV-right:not(.BIV-disabled):hover { .BIV-right:not(.BIV-disabled):hover {
background-color: hsla(0, 0%, 49%, 0.2); background-color: hsla(0, 0%, 49%, 0.2);
color: white; color: #fff;
} }
.BIV-left { .BIV-left {
left: 0; left: 0;
} }
.BIV-right { .BIV-right {
right: 0; right: 0;
} }
.BIV-left > svg, .BIV-right > svg { .BIV-left > svg,
width: 30px; .BIV-right > svg {
height: 30px; width: 30px;
height: 30px;
} }
.BIV-info { .BIV-info {
position: absolute; position: absolute;
left: 18px; left: 18px;
bottom: 12px; bottom: 12px;
height: 42px; height: 42px;
transition: opacity .35s ease-in-out; transition: opacity 0.35s ease-in-out;
} }
.BIV-info-extra { .BIV-info-extra {
left: unset; left: unset;
@ -1112,11 +1260,11 @@ var BetterImageViewer = (() => {
text-align: end; text-align: end;
} }
.BIV-info-wrapper { .BIV-info-wrapper {
bottom: 0; bottom: 0;
position: absolute; position: absolute;
white-space: nowrap; white-space: nowrap;
} }
.BIV-info-wrapper > .${TextElement.Colors.PRIMARY} { .BIV-info-wrapper > .primary-jw0I4K {
display: flex; display: flex;
align-items: center; align-items: center;
} }
@ -1124,45 +1272,60 @@ var BetterImageViewer = (() => {
display: flex; display: flex;
margin-left: 5px; margin-left: 5px;
} }
.BIV-requesting > svg[name="Nova_Search"], .BIV-requesting > svg[name="LeftCaret"], .BIV-requesting > svg[name="RightCaret"] { .BIV-requesting > svg[name='Nova_Search'],
.BIV-requesting > svg[name='LeftCaret'],
.BIV-requesting > svg[name='RightCaret'] {
width: 16px; width: 16px;
height: 16px; height: 16px;
} }
.BIV-zoom-backdrop,
.biv-overlay {
width: 100%;
height: 100%;
position: absolute;
}
.BIV-inactive { .BIV-inactive {
transition: opacity 1s ease-in-out; transition: opacity 1s ease-in-out;
} }
.BIV-hidden { .BIV-hidden {
opacity: 0; opacity: 0;
} }
.BIV-info-wrapper .username-1A8OIy {
.BIV-info-wrapper .${UsernameClassname.split(' ')[0]} {
max-width: 900px; max-width: 900px;
overflow-x: hidden; overflow-x: hidden;
margin-right: .25rem; margin-right: 0.25rem;
} }
.biv-overlay { .biv-overlay {
pointer-events: none; pointer-events: none;
position: absolute;
z-index: 1000; z-index: 1000;
width: 100%;
height: 100%;
} }
.biv-overlay > * { .biv-overlay > * {
pointer-events: all; pointer-events: all;
} }
@keyframes BIV-spin { @keyframes BIV-spin {
0% { 0% {
transform: rotate(0deg) transform: rotate(0);
} }
to { to {
transform: rotate(1turn) transform: rotate(1turn);
} }
} }
.BIV-searching-icon-spin { .BIV-searching-icon-spin {
animation: BIV-spin 2s linear infinite; animation: BIV-spin 2s linear infinite;
} }
.BIV-zoom-lens {
position: absolute;
overflow: hidden;
cursor: none;
border: 1px solid #0092ff;
}
.BIV-zoom-lens-round {
border-radius: 50%;
border: 2px solid #0092ff;
}
.BIV-zoom-backdrop {
background: rgba(0, 0, 0, 0.4);
}
` `
); );
} }
@ -1173,6 +1336,7 @@ var BetterImageViewer = (() => {
Patcher.unpatchAll(); Patcher.unpatchAll();
Dispatcher.unsubscribe('MESSAGE_DELETE', this.handleMessageDelete); Dispatcher.unsubscribe('MESSAGE_DELETE', this.handleMessageDelete);
Dispatcher.unsubscribe('MESSAGE_DELETE_BULK', this.handlePurge); Dispatcher.unsubscribe('MESSAGE_DELETE_BULK', this.handlePurge);
Dispatcher.unsubscribe('BIV_LENS_WH_CHANGE', this.handleWHChange);
PluginUtilities.removeStyle(this.short + '-CSS'); PluginUtilities.removeStyle(this.short + '-CSS');
} }
@ -1181,6 +1345,10 @@ var BetterImageViewer = (() => {
return PluginUtilities.loadSettings(this.name, Utilities.deepclone(this.defaultSettings ? this.defaultSettings : defaultSettings)); return PluginUtilities.loadSettings(this.name, Utilities.deepclone(this.defaultSettings ? this.defaultSettings : defaultSettings));
} }
saveHiddenSettings() {
PluginUtilities.saveData(this.name, 'hidden', this.hiddenSettings);
}
handleMessageDelete(e) { handleMessageDelete(e) {
const { channelId, id: messageId } = e; const { channelId, id: messageId } = e;
if (e['__\x4e\x4f\x552Prevent\x46\x41\x47\x47\x4f\x54']) return; if (e['__\x4e\x4f\x552Prevent\x46\x41\x47\x47\x4f\x54']) return;
@ -1191,6 +1359,10 @@ var BetterImageViewer = (() => {
if (e['__\x4e\x4f\x552Prevent\x46\x41\x47\x47\x4f\x54']) return; if (e['__\x4e\x4f\x552Prevent\x46\x41\x47\x47\x4f\x54']) return;
stripPurgedMessages(channelId, messageIds); stripPurgedMessages(channelId, messageIds);
} }
handleWHChange({ value }) {
this.hiddenSettings.panelWH = value;
this.saveHiddenSettings();
}
/* PATCHES */ /* PATCHES */
@ -1334,9 +1506,12 @@ var BetterImageViewer = (() => {
const height = (props && props.height) || _this.props.height; const height = (props && props.height) || _this.props.height;
const reqUrl = (() => { const reqUrl = (() => {
const split = src.split('?')[0]; const split = src.split('?')[0];
const SaveToRedux = BdApi.getPlugin('SaveToRedux'); const SaveToRedux = BdApi.getPlugin && BdApi.getPlugin('SaveToRedux');
if (SaveToRedux) return SaveToRedux.formatURL(split, src.substr(src.indexOf('?')).indexOf('size=') !== -1, '', '', (props && props.original) || _this.props.original).url; const needsSize = src.substr(src.indexOf('?')).indexOf('size=') !== -1;
return split; try {
if (SaveToRedux) return SaveToRedux.formatURL(split, needsSize, '', '').url;
} catch (_) {}
return split + (needsSize ? '?size=2048' : '');
})(); })();
const max = ImageUtils.zoomFit(width, height); const max = ImageUtils.zoomFit(width, height);
const ratio = getRatio(width, height, max.width, max.height); const ratio = getRatio(width, height, max.width, max.height);
@ -1431,10 +1606,31 @@ var BetterImageViewer = (() => {
} }
patchLazyImage() { patchLazyImage() {
if (NoImageZoom || true) return; if (NoImageZoom) return;
Patcher.after(WebpackModules.getByDisplayName('LazyImage').prototype, 'render', (_this, _, ret) => { const LazyImage = WebpackModules.getByDisplayName('LazyImage');
if (_this.props.onContextMenu || _this.props.onZoom || _this.props.children) return; Patcher.instead(LazyImage.prototype, 'componentDidUpdate', (_this, [props, state]) => {
/* custom handler, original one caused issues with GIFs not animating */
const animated = LazyImage.isAnimated(_this.props);
if (animated !== LazyImage.isAnimated(props)) {
if (animated) _this.observeVisibility();
else _this.unobserveVisibility();
} else if (state.readyState !== _this.state.readyState && animated) _this.observeVisibility();
else if (!animated) _this.unobserveVisibility();
});
Patcher.after(LazyImage.prototype, 'render', (_this, _, ret) => {
if (!this.settings.zoom.enabled || _this.props.onZoom || _this.state.readyState !== 'READY' || !ret) return;
if (_this.props.animated && ret.props.children) {
/* dirty */
try {
ret.props.__BIV_src = ret.props.children({ size: {} }).props.src;
} catch (e) {
return;
}
}
ret.type = Image; ret.type = Image;
ret.props.settings = this.settings.zoom;
ret.props.__BIV_animated = _this.props.animated;
ret.props.hiddenSettings = this.hiddenSettings;
}); });
} }

View File

@ -1,4 +1,13 @@
# [BetterImageViewer](https://1lighty.github.io/BetterDiscordStuff/?plugin=BetterImageViewer "BetterImageViewer") Changelog # [BetterImageViewer](https://1lighty.github.io/BetterDiscordStuff/?plugin=BetterImageViewer "BetterImageViewer") Changelog
### 1.1.0
- *SOME* people kept asking, so here you go.
- Added image zoom, simply activate by click and holding on the image.
- Use scroll wheel to zoom in and out, hold shift while scrolling to change lens size.
- If you prefer using a different plugin to handle zooming for you, that's fine too, there is a toggle to disable it in settings.
- The click and hold can of course be easily changed in the settings to your prefered method of zooming.
![zoommode](https://i.imgur.com/A8HjQb9.png)
- There are other options too, for those that may want it!
![moreoptions](https://i.imgur.com/JGNe7Re.png)'
### 1.0.4 ### 1.0.4
- Fixed plugin refusing to work in PTB and canary - Fixed plugin refusing to work in PTB and canary

View File

@ -1,12 +1,15 @@
# BetterImageViewer [![download](https://i.imgur.com/OAHgjZu.png)](https://1lighty.github.io/BetterDiscordStuff/?plugin=BetterImageViewer&dl=1 "BetterImageViewer") # BetterImageViewer [![download](https://i.imgur.com/OAHgjZu.png)](https://1lighty.github.io/BetterDiscordStuff/?plugin=BetterImageViewer&dl=1 "BetterImageViewer")
Telegram image viewer ported to Discord. Adds ability to go between images in the current channel with arrow keys, or on screen buttons. Also provides info about the image, who posted it and when. Adds ability to go between images in the current channel with arrow keys, or on screen buttons, and has click to zoom. Also provides info about the image, who posted it and when.
### Features ### Features
Adds left and right arrows when you open an image, shows you who sent the image and when, the images resolution, scaling and size, as well as number of cached images (and estimated in channel total). Adds left and right arrows when you open an image, shows you who sent the image and when, the images resolution, scaling and size, as well as number of cached images (and estimated in channel total).
Can switch between images in the entire channel using the on screen buttons, or arrow keys. Can switch between images in the entire channel using the on screen buttons, or arrow keys.
Click the users name to jump to the message containing the image. Click the users name to jump to the message containing the image.
Right click the navigation buttons to quick jump to the last (or first) image. Right click the navigation buttons to quick jump to the last (or first) image.
Click and hold (or other) to zoom into the image.
### Preview ### Preview
![preview](https://i.imgur.com/oSSWG9u.png) ![preview](https://i.imgur.com/oSSWG9u.png)
![preview2](https://i.imgur.com/GOnWSog.png)
![preview3](https://i.imgur.com/LUhIgc8.png)
### Settings ### Settings
#### UI settings #### UI settings
##### Show image index and number of images left (estimated) ##### Show image index and number of images left (estimated)
@ -22,3 +25,16 @@ Left is % of original resolution, where right is % downscaled.
Without this, you'll only be able to view images currently cached in Discord. Without this, you'll only be able to view images currently cached in Discord.
##### Trigger search when hovering over navigation buttons when needed ##### Trigger search when hovering over navigation buttons when needed
Sometimes, if there's under 5 images left, it will wait for you to press the correct arrow key, or hover over the navigation button itself, once you do that it searches for more images. Setting this to false simply makes it only do that when you click the button instead of hover it. Sometimes, if there's under 5 images left, it will wait for you to press the correct arrow key, or hover over the navigation button itself, once you do that it searches for more images. Setting this to false simply makes it only do that when you click the button instead of hover it.
#### Image Zoom settings
##### Enable image zoom
##### Zoom enable mode
Click and hold
Click to toggle
Scroll to toggle
##### Anti-aliasing
On low resolution images, pixels become blurry
#### Round lens
#### Allow lens to go out of bounds
Allows the lens to go beyond the border of the image
#### Movement smoothing
Not recommended to disable. Smooths out movement and zoom