From dff1f976215d8b8e0aa5816cbcea86e6a140ec2b Mon Sep 17 00:00:00 2001 From: "Mr. Stallion" <mrstallion@nobody.nowhere.fauxdomain.ext> Date: Fri, 12 Jul 2019 17:11:55 -0500 Subject: [PATCH] Fix to image preview load bug --- bbcode/UrlTagView.vue | 18 ++++-- chat/ImagePreview.vue | 90 ++++++++++++++++++++++---- chat/image-preview-mutator.ts | 20 ++++-- site/character_page/character_page.vue | 4 +- 4 files changed, 109 insertions(+), 23 deletions(-) diff --git a/bbcode/UrlTagView.vue b/bbcode/UrlTagView.vue index dd0c7cb..b22e25a 100644 --- a/bbcode/UrlTagView.vue +++ b/bbcode/UrlTagView.vue @@ -1,15 +1,17 @@ <template> <span> <i class="fa fa-link"></i> + <!-- No prevent for @click on purpose --> <a :href="url" rel="nofollow noreferrer noopener" target="_blank" class="user-link" - @mouseover="show()" - @mouseenter="show()" - @mouseleave="dismiss()" - @mouseout="dismiss()" + @click="handleClick()" + @mouseover.prevent="show()" + @mouseenter.prevent="show()" + @mouseleave.prevent="dismiss()" + @mouseout.prevent="dismiss()" @click.middle.prevent="toggleStickyness()" >{{text}}</a> <span @@ -45,8 +47,8 @@ this.dismiss(); } - dismiss(): void { - EventBus.$emit('imagepreview-dismiss', {url: this.url}); + dismiss(force: boolean = false): void { + EventBus.$emit('imagepreview-dismiss', {url: this.url, force}); } show(): void { @@ -57,5 +59,9 @@ toggleStickyness(): void { EventBus.$emit('imagepreview-toggle-stickyness', {url: this.url}); } + + handleClick(): void { + this.dismiss(true); + } } </script> diff --git a/chat/ImagePreview.vue b/chat/ImagePreview.vue index 06bcbca..a6b6ba9 100644 --- a/chat/ImagePreview.vue +++ b/chat/ImagePreview.vue @@ -18,6 +18,7 @@ </template> <script lang="ts"> + import * as _ from 'lodash'; import {Component, Hook} from '@f-list/vue-ts'; import Vue from 'vue'; import { EventBus, EventBusEvent } from './event-bus'; @@ -50,6 +51,8 @@ externalUrl: string | null = null; internalUrl: string | null = null; + lastExternalUrl = ''; + url: string | null = null; domain: string | undefined; @@ -74,7 +77,7 @@ (eventData: EventBusEvent) => { // console.log('Event dismiss', eventData.url); - this.dismiss(eventData.url as string); + this.dismiss(eventData.url as string, eventData.force as boolean); } ); @@ -111,6 +114,7 @@ } ); + webview.addEventListener( 'did-fail-load', (event: Event) => { @@ -160,19 +164,41 @@ ); + _.each( + ['did-start-loading', 'load-commit', 'will-navigate', 'did-navigate', 'did-navigate-in-page', 'update-target-url'], + (en: string) => { + webview.addEventListener( + en, + (event: Event) => { + if (this.debug) + console.log(`ImagePreview ${en}`, event); + } + ); + } + ); + + setInterval( () => { if (((this.visible) && (!this.exitInterval) && (!this.shouldDismiss)) || (this.interval)) this.initialCursorPosition = screen.getCursorScreenPoint(); - if ((this.visible) && (this.shouldDismiss) && (this.hasMouseMovedSince()) && (!this.exitInterval) && (!this.interval)) + if ((this.visible) && (this.shouldDismiss) && (this.hasMouseMovedSince()) && (!this.exitInterval) && (!this.interval)) { + if (this.debug) { + console.log('ImagePreview: call hide from interval'); + } + this.hide(); + } }, 10 ); } hide(): void { + if (this.debug) + console.log('ImagePreview: hide', this.externalUrlVisible, this.internalUrlVisible); + this.cancelExitTimer(); this.url = null; @@ -181,6 +207,9 @@ if (this.externalUrlVisible) { const webview = this.getWebview(); + if (this.debug) + console.log('ImagePreview: exec hide mutator'); + webview.executeJavaScript(this.jsMutator.getHideMutator()); } @@ -198,7 +227,11 @@ this.sticky = false; } - dismiss(url: string): void { + dismiss(url: string, force: boolean = false): void { + if (this.debug) { + console.log('ImagePreview: dismiss', url); + } + if (this.url !== url) return; // simply ignore @@ -220,9 +253,12 @@ this.exitUrl = this.url; this.shouldDismiss = true; - if (!this.hasMouseMovedSince()) + if ((!this.hasMouseMovedSince()) && (!force)) return; + if (this.debug) + console.log('ImagePreview: dismiss.exec', this.externalUrlVisible, this.internalUrlVisible, url); + // This timeout makes the preview window disappear with a slight delay, which helps UX // when dealing with situations such as quickly scrolling text that moves the cursor away // from the link @@ -234,16 +270,37 @@ } show(url: string): void { + if (this.debug) + console.log('ImagePreview: show', this.externalUrlVisible, this.internalUrlVisible, + this.visible, this.hasMouseMovedSince(), !!this.interval, this.sticky, url); + // console.log('SHOW'); - if ((this.visible) && (!this.hasMouseMovedSince())) + if ((this.visible) && (!this.exitInterval) && (!this.hasMouseMovedSince())) { + if (this.debug) { + console.log('ImagePreview: show cancel: visible & not moved'); + } return; + } - if ((this.url === url) && ((this.visible) || (this.interval))) - return; + if ((this.url === url) && ((this.visible) || (this.interval))) { + if (this.debug) { + console.log('ImagePreview: same url'); + } - if ((this.url) && (this.sticky) && (this.visible)) return; + } + + if ((this.url) && (this.sticky) && (this.visible)) { + if (this.debug) { + console.log('ImagePreview: sticky visible'); + } + + return; + } + + if (this.debug) + console.log('ImagePreview: show.exec', url); const due = ((url === this.exitUrl) && (this.exitInterval)) ? 0 : 100; @@ -258,6 +315,9 @@ // tslint:disable-next-line no-unnecessary-type-assertion this.interval = setTimeout( () => { + if (this.debug) + console.log('ImagePreview: show.timeout', this.url); + const isInternal = this.isInternalUrl(); this.internalUrlVisible = isInternal; @@ -269,14 +329,22 @@ const webview = this.getWebview(); try { - if (webview.getURL() === this.url) { + if ((webview.getURL() === this.url) || (this.url === this.lastExternalUrl)) { + if (this.debug) + console.log('ImagePreview: exec re-show mutator'); + webview.executeJavaScript(this.jsMutator.getReShowMutator()); + } else { + if (this.debug) + console.log('ImagePreview: skip re-show because urls don\'t match', this.url, webview.getURL()); } + } catch (err) { - console.log('Webview reuse error', err); + console.error('ImagePreview: Webview reuse error', err); } this.externalUrl = this.url; + this.lastExternalUrl = this.url as string; } this.visible = true; @@ -297,7 +365,7 @@ return ((p.x !== this.initialCursorPosition.x) || (p.y !== this.initialCursorPosition.y)); } catch (err) { - console.error(err); + console.error('ImagePreview', err); return true; } } diff --git a/chat/image-preview-mutator.ts b/chat/image-preview-mutator.ts index 167f866..e7ba23f 100644 --- a/chat/image-preview-mutator.ts +++ b/chat/image-preview-mutator.ts @@ -36,7 +36,7 @@ export class ImagePreviewMutator { if (!mutator) mutator = this.hostMutators['default']; - return this.wrapJs(mutator.injectJs); + return this.wrapJs(mutator.injectJs) + this.getReShowMutator(); } matchMutator(url: string): PreviewMutator | undefined { @@ -253,15 +253,27 @@ export class ImagePreviewMutator { opacity: 1 !important; text-align: center !important; "></div> - `); + `) + this.wrapJs( + ` + window.__flistUnhide = () => { + const elements = document.querySelectorAll('#flistHider'); + + if (elements) { + elements.forEach( (el) => el.remove() ); + } + }; + ` + ); } getReShowMutator(): string { return this.wrapJs( ` - const el = document.querySelector('#flistHider'); + const elements = document.querySelectorAll('#flistHider'); - if (el) { el.remove(); } + if (elements) { + elements.forEach( (el) => el.remove() ); + } ` ); } diff --git a/site/character_page/character_page.vue b/site/character_page/character_page.vue index d20e1fe..83abcb8 100644 --- a/site/character_page/character_page.vue +++ b/site/character_page/character_page.vue @@ -141,14 +141,14 @@ beforeMount(): void { this.shared.authenticated = this.authenticated; - console.log('Beforemount'); + // console.log('Beforemount'); } @Hook('mounted') async mounted(): Promise<void> { await this.load(false); - console.log('mounted'); + // console.log('mounted'); } @Watch('tab')