diff --git a/komga-webui/src/functions/shortcuts.ts b/komga-webui/src/functions/shortcuts.ts new file mode 100644 index 000000000..fe0fe82e4 --- /dev/null +++ b/komga-webui/src/functions/shortcuts.ts @@ -0,0 +1,105 @@ +import { ReadingDirection } from '@/types/enum-books' + +enum Shortcut { + // Navigation + SEEK_FORWARD = 'seekForward', + SEEK_BACKWARD = 'seekBackward', + // Vertical mode + SEEK_UP = 'seekUp', + SEEK_DOWN = 'seekDown', + SEEK_BEGIN = 'seekBegin', + SEEK_END = 'seekEnd', + // SETTINGS + DIR_LTR = 'directionLTR', + DIR_RTL = 'directionRTL', + DIR_VRT = 'directionVRT', + TOGGLE_DOUBLE_PAGE = 'toggleDoublePage', + CYCLE_SCALE = 'cycleScale', + // OTHER + TOGGLE_TOOLBAR = 'toggleToolbar', + TOGGLE_MENU = 'toggleMenu', + TOGGLE_THUMBNAIL_EXPLORER = 'toggleExplorer', + ESCAPE = 'escape' +} + +interface KeyMapping { + [key: string]: Shortcut +} + +type Action = (ctx: any) => void + +interface Shortcuts { + [key: string]: Action +} + +// consider making this configurable on the server side? +const keyMapping = { + 'PageUp': Shortcut.SEEK_FORWARD, + 'ArrowRight': Shortcut.SEEK_FORWARD, + 'PageDown': Shortcut.SEEK_BACKWARD, + 'ArrowLeft': Shortcut.SEEK_BACKWARD, + 'ArrowDown': Shortcut.SEEK_DOWN, + 'ArrowUp': Shortcut.SEEK_UP, + 'Home': Shortcut.SEEK_BEGIN, + 'End': Shortcut.SEEK_END, + 'm': Shortcut.TOGGLE_TOOLBAR, + 's': Shortcut.TOGGLE_MENU, + 't': Shortcut.TOGGLE_THUMBNAIL_EXPLORER, + 'Escape': Shortcut.ESCAPE, + 'l': Shortcut.DIR_LTR, + 'r': Shortcut.DIR_RTL, + 'v': Shortcut.DIR_VRT, + 'd': Shortcut.TOGGLE_DOUBLE_PAGE, + 'f': Shortcut.CYCLE_SCALE, +} as KeyMapping + +const shortcuts = { + [Shortcut.SEEK_FORWARD]: (ctx: any) => { + ctx.flipDirection ? ctx.prev() : ctx.next() + }, + [Shortcut.SEEK_BACKWARD]: (ctx: any) => { + ctx.flipDirection ? ctx.next() : ctx.prev() + }, + [Shortcut.SEEK_UP]: (ctx: any) => { if (ctx.vertical) ctx.prev() }, + [Shortcut.SEEK_DOWN]: (ctx: any) => { if (ctx.vertical) ctx.next() }, + [Shortcut.SEEK_BEGIN]: (ctx: any) => { ctx.goToFirst() }, + [Shortcut.SEEK_END]: (ctx: any) => { ctx.goToLast() }, + [Shortcut.TOGGLE_TOOLBAR]: (ctx: any) => { ctx.toolbar = !ctx.toolbar }, + [Shortcut.TOGGLE_MENU]: (ctx: any) => { ctx.menu = !ctx.menu }, + [Shortcut.TOGGLE_THUMBNAIL_EXPLORER]: (ctx: any) => { ctx.showThumbnailsExplorer = !ctx.showThumbnailsExplorer }, + [Shortcut.TOGGLE_DOUBLE_PAGE]: (ctx: any) => ctx.toggleDoublePages(), + [Shortcut.CYCLE_SCALE]: (ctx: any) => ctx.cycleScale(), + [Shortcut.DIR_LTR]: (ctx: any) => ctx.changeReadingDir(ReadingDirection.LEFT_TO_RIGHT), + [Shortcut.DIR_RTL]: (ctx: any) => ctx.changeReadingDir(ReadingDirection.RIGHT_TO_LEFT), + [Shortcut.DIR_VRT]: (ctx: any) => ctx.changeReadingDir(ReadingDirection.VERTICAL), + [Shortcut.ESCAPE]: (ctx: any) => { + if (ctx.showThumbnailsExplorer) { + ctx.showThumbnailsExplorer = false + return + } + if (ctx.menu) { + ctx.menu = false + return + } + if (ctx.toolbar) { + ctx.toolbar = false + return + } + ctx.closeBook() + }, +} as Shortcuts + +export function executeShortcut (ctx: any, e: KeyboardEvent): boolean { + let k: string = e.key + if (k in keyMapping) { + let s: Shortcut = keyMapping[k] + if (s in shortcuts) { + let action: Action = shortcuts[s] + if (action) { + action(ctx) + return true + } + } + } + return false +} diff --git a/komga-webui/src/views/BookReader.vue b/komga-webui/src/views/BookReader.vue index 86fcf6708..93d21ac9e 100644 --- a/komga-webui/src/views/BookReader.vue +++ b/komga-webui/src/views/BookReader.vue @@ -249,6 +249,20 @@ Dismiss + + +
+ {{ notification.message }} +
+
@@ -260,6 +274,7 @@ import { getBookTitleCompact } from '@/functions/book-title' import { checkWebpFeature } from '@/functions/check-webp' import { bookPageUrl } from '@/functions/urls' import { ReadingDirection } from '@/types/enum-books' +import { executeShortcut } from '@/functions/shortcuts' import Vue from 'vue' const cookieFit = 'webreader.fit' @@ -301,11 +316,17 @@ export default Vue.extend({ settings: { doublePages: false, swipe: true, - imageFits: Object.values(ImageFit), fit: ImageFit.HEIGHT, readingDirection: ReadingDirection.LEFT_TO_RIGHT, animations: true, backgroundColor: 'black', + imageFits: Object.values(ImageFit), + readingDirs: Object.values(ReadingDirection), + }, + notification: { + enabled: false, + message: '', + timeout: 4000, }, readingDirs: [ { text: 'Left to right', value: ReadingDirection.LEFT_TO_RIGHT }, @@ -494,52 +515,7 @@ export default Vue.extend({ }, methods: { keyPressed (e: KeyboardEvent) { - switch (e.key) { - case 'PageUp': - case 'ArrowRight': - this.flipDirection ? this.prev() : this.next() - break - case 'PageDown': - case 'ArrowLeft': - this.flipDirection ? this.next() : this.prev() - break - case 'ArrowDown': - if (this.vertical) this.next() - break - case 'ArrowUp': - if (this.vertical) this.prev() - break - case 'Home': - this.goToFirst() - break - case 'End': - this.goToLast() - break - case 'm': - this.toolbar = !this.toolbar - break - case 's': - this.menu = !this.menu - break - case 't': - this.showThumbnailsExplorer = !this.showThumbnailsExplorer - break - case 'Escape': - if (this.showThumbnailsExplorer) { - this.showThumbnailsExplorer = false - break - } - if (this.menu) { - this.menu = false - break - } - if (this.toolbar) { - this.toolbar = false - break - } - this.closeBook() - break - } + executeShortcut(this, e) }, async setup (bookId: number, page: number) { this.book = await this.$komgaBooks.getBook(bookId) @@ -686,6 +662,29 @@ export default Vue.extend({ setter(value) } }, + changeReadingDir (dir: ReadingDirection) { + this.readingDirection = dir + let i = this.settings.readingDirs.indexOf(this.readingDirection) + let text = this.readingDirs[i].text + this.sendNotification(`Changing Reading Direction to: ${text}`) + }, + cycleScale () { + let fit: ImageFit = this.settings.fit + let i = (this.settings.imageFits.indexOf(fit) + 1) % (this.settings.imageFits.length) + this.settings.fit = this.settings.imageFits[i] + let text = this.imageFits[i].text + // The text here only works cause this.imageFits has the same index structure as the ImageFit enum + this.sendNotification(`Cycling Scale: ${text}`) + }, + toggleDoublePages () { + this.doublePages = !this.doublePages + this.sendNotification(`${this.doublePages ? 'Enabled' : 'Disabled'} Double Pages`) + }, + sendNotification (message:string, timeout: number = 4000) { + this.notification.timeout = 4000 + this.notification.message = message + this.notification.enabled = true + }, async markProgress (page: number) { this.$komgaBooks.updateReadProgress(this.bookId, { page: page }) },