diff --git a/android/app/src/main/assets/css/index.css b/android/app/src/main/assets/css/index.css index a028dec9..9b27fffa 100644 --- a/android/app/src/main/assets/css/index.css +++ b/android/app/src/main/assets/css/index.css @@ -303,4 +303,27 @@ tts.highlight { opacity: 1; transform: translateX(0); } +} + +#Image-Modal { + position: fixed; + left: 0; + top: 0; + user-select: none; +} + +#Image-Modal.show { + display: flex; + height: 100%; + width: 100%; + max-width: max-content; + max-height: max-content; + background-color: var(--theme-surface-0-9); + align-items: center; + overflow: scroll; +} + +#Image img { + display: block; + width: 100%; } \ No newline at end of file diff --git a/android/app/src/main/assets/js/index.js b/android/app/src/main/assets/js/index.js index 7b21b329..3925eb0e 100644 --- a/android/app/src/main/assets/js/index.js +++ b/android/app/src/main/assets/js/index.js @@ -25,6 +25,7 @@ const selectVolumeIcon = class Reader { constructor() { this.selection = window.getSelection(); + this.viewport = document.querySelector('meta[name=viewport]'); this.footerWrapper = document.getElementById('reader-footer-wrapper'); this.percentage = document.getElementById('reader-percentage'); this.battery = document.getElementById('reader-battery'); @@ -510,6 +511,14 @@ class ContextMenu { }); document.addEventListener('contextmenu', e => { e.preventDefault(); + if (e.target instanceof HTMLImageElement) { + if (imageModal.showing) { + imageModal.hide(); + } else { + imageModal.show(e.target); + } + return; + } this.isOpened = true; if (this.reader.selection.type === 'Range') { this.renderMenu([this.items.START_HERE, this.items.START_READING]); @@ -517,7 +526,7 @@ class ContextMenu { this.renderMenu([this.items.START_READING]); } const { clientX, clientY } = e; - document.body.appendChild(this.contextMenu); + e.target.appendChild(this.contextMenu); const positionY = clientY + this.contextMenu.scrollHeight >= window.innerHeight ? window.innerHeight - this.contextMenu.scrollHeight - 20 @@ -537,6 +546,55 @@ class ContextMenu { } } +/** + * @type {import("./type").ImageModal} + */ +class ImageModal { + /** + * @param {import('./type').Reader} reader + */ + constructor(reader) { + this.reader = reader; + this.$ = document.getElementById('Image-Modal'); + this.img = this.$.querySelector('img'); + this.showing = false; + this.$.onclick = e => { + if (e.target.id !== 'Image-Modal-img') { + e.stopPropagation(); + this.hide(); + } + }; + this.$.ontouchmove = e => { + e.stopImmediatePropagation(); + }; + } + + /** + * + * @param {HTMLImageElement} image + */ + show(image) { + this.img.src = image.src; + this.img.alt = `Can not render image from ${image.src}`; + this.reader.viewport.setAttribute( + 'content', + 'width=device-width, initial-scale=1.0, maximum-scale=10', + ); + this.$.classList.add('show'); + this.showing = true; + } + hide() { + this.$.classList.remove('show'); + this.showing = false; + this.img.src = ''; + this.img.alt = ''; + this.reader.viewport.setAttribute( + 'content', + 'width=device-width, initial-scale=1.0, maximum-scale=1.0', + ); + } +} + try { var swipeHandler = new SwipeHandler(); var toolWrapper = new ToolWrapper(); @@ -544,6 +602,7 @@ try { var scrollHandler = new ScrollHandler(reader, toolWrapper); var tts = new TextToSpeech(reader); toolWrapper.tools = [scrollHandler, tts]; + imageModal = new ImageModal(reader); const contextMenu = new ContextMenu(reader); contextMenu.init(); } catch (e) { diff --git a/android/app/src/main/assets/js/type.d.ts b/android/app/src/main/assets/js/type.d.ts index 436b15b9..76c788f7 100644 --- a/android/app/src/main/assets/js/type.d.ts +++ b/android/app/src/main/assets/js/type.d.ts @@ -7,6 +7,7 @@ import 'typescript/lib/lib.dom'; interface Reader { selection: Selection; + viewport: HTMLMetaElement; footerWrapper: HTMLDivElement; percentage: HTMLDivElement; battery: HTMLDivElement; @@ -60,3 +61,10 @@ interface ContextMenu { closeMenu: (menu: HTMLUListElement) => void; init: () => void; } + +interface ImageModal { + reader: Reader; + showing: boolean; + show: (image: HTMLImageElement) => void; + hide: () => void; +} diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index a8a910f1..2f1bd928 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -11,6 +11,7 @@ @color/colorPrimaryDark @color/colorPrimaryDark @color/colorPrimaryDark + #2596be