diff --git a/komga-webui/src/components/LibraryNavigation.vue b/komga-webui/src/components/LibraryNavigation.vue index a4c853ba4..f1ed9b3a5 100644 --- a/komga-webui/src/components/LibraryNavigation.vue +++ b/komga-webui/src/components/LibraryNavigation.vue @@ -22,7 +22,7 @@ export default Vue.extend({ name: 'LibraryNavigation', props: { libraryId: { - type: Number, + type: String, required: true, }, }, diff --git a/komga-webui/src/components/SettingsSelect.vue b/komga-webui/src/components/SettingsSelect.vue index 8660afbbb..636ba5b94 100644 --- a/komga-webui/src/components/SettingsSelect.vue +++ b/komga-webui/src/components/SettingsSelect.vue @@ -1,9 +1,9 @@ + + + + diff --git a/komga-webui/src/components/dialogs/ThumbnailExplorerDialog.vue b/komga-webui/src/components/dialogs/ThumbnailExplorerDialog.vue index 5b2a10daa..671e47cfc 100644 --- a/komga-webui/src/components/dialogs/ThumbnailExplorerDialog.vue +++ b/komga-webui/src/components/dialogs/ThumbnailExplorerDialog.vue @@ -91,7 +91,7 @@ export default Vue.extend({ this.$emit('input', this.input) }, goTo (page: number) { - this.$emit('goToPage', page) + this.$emit('go', page) }, getThumbnailUrl (page: number): string { return bookPageThumbnailUrl(this.bookId, page) diff --git a/komga-webui/src/components/menus/ShortcutHelpMenu.vue b/komga-webui/src/components/menus/ShortcutHelpMenu.vue deleted file mode 100644 index 2ea2223ee..000000000 --- a/komga-webui/src/components/menus/ShortcutHelpMenu.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - - - diff --git a/komga-webui/src/components/readers/ContinuousReader.vue b/komga-webui/src/components/readers/ContinuousReader.vue new file mode 100644 index 000000000..4b0dd733c --- /dev/null +++ b/komga-webui/src/components/readers/ContinuousReader.vue @@ -0,0 +1,159 @@ + + + + diff --git a/komga-webui/src/components/readers/PagedReader.vue b/komga-webui/src/components/readers/PagedReader.vue new file mode 100644 index 000000000..e3c2fb4f1 --- /dev/null +++ b/komga-webui/src/components/readers/PagedReader.vue @@ -0,0 +1,361 @@ + + + + diff --git a/komga-webui/src/functions/reader.ts b/komga-webui/src/functions/reader.ts new file mode 100644 index 000000000..9e9382395 --- /dev/null +++ b/komga-webui/src/functions/reader.ts @@ -0,0 +1,16 @@ +import { ScaleType } from '@/types/enum-reader' +import { ReadingDirection } from '@/types/enum-books' + +export const ScaleTypeText = { + [ScaleType.SCREEN]: 'Fit screen', + [ScaleType.HEIGHT]: 'Fit height', + [ScaleType.WIDTH]: 'Fit width', + [ScaleType.ORIGINAL]: 'Original', +} + +export const ReadingDirectionText = { + [ReadingDirection.LEFT_TO_RIGHT]: 'Left to right', + [ReadingDirection.RIGHT_TO_LEFT]: 'Right to left', + [ReadingDirection.VERTICAL]: 'Vertical', + [ReadingDirection.WEBTOON]: 'Webtoon', +} diff --git a/komga-webui/src/functions/shortcuts.ts b/komga-webui/src/functions/shortcuts.ts deleted file mode 100644 index 5396f039b..000000000 --- a/komga-webui/src/functions/shortcuts.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { ReadingDirection } from '@/types/enum-books' - -interface Map { - [key: string]: V -} -class MultiMap { - dict: Map = {} - - add (key:string, value: V) { - this.dict[key] = (this.dict[key]?.concat([value])) || [value] - } - - get (key: string): V[] { - return this.dict[key] - } - - items () { - return Object.keys(this.dict).map((k) => ({ key: k, value: this.dict[k] })) - } -} - -type Action = (ctx: any) => void - -class Shortcut { - name: string - category: string - description: string - action: Action - - keys: string[] - - constructor (name: string, category: string, description: string, action: Action, keys: string[]) { - this.name = name - this.category = category - this.description = description - this.action = action - this.keys = keys - } - - execute (ctx: any): boolean { - this.action(ctx) - return true - } -} - -const KEY_DISPLAY = { - 'ArrowRight': 'mdi-arrow-right', - 'ArrowLeft': 'mdi-arrow-left', - 'PageUp': 'PgUp', - 'PageDown': 'PgDn', - 'ArrowUp': 'mdi-arrow-up', - 'ArrowDown': 'mdi-arrow-down', - 'Escape': 'Esc', -} as Map - -const SHORTCUTS: Shortcut[] = [] - -function shortcut (name: string, category: string, description: string, action: Action, ...keys: string[]) { - SHORTCUTS.push(new Shortcut(name, category, description, action, keys)) -} - -enum ShortcutCategory { - READER_NAVIGATION = 'Reader Navigation', - READER_SETTINGS = 'Reader Settings', - MENUS = 'Menus' -} - -// Reader Navigation -shortcut('seekForward', ShortcutCategory.READER_NAVIGATION, 'Next Page', - (ctx: any) => { - ctx.flipDirection ? ctx.prev() : ctx.next() - }, 'PageUp', 'ArrowRight') - -shortcut('seekBackward', ShortcutCategory.READER_NAVIGATION, 'Prev Page', - (ctx: any) => { - ctx.flipDirection ? ctx.next() : ctx.prev() - }, 'PageDown', 'ArrowLeft') - -shortcut('seekUp', ShortcutCategory.READER_NAVIGATION, 'Prev Page (Vertical)', - (ctx: any) => { - if (ctx.vertical) { - ctx.prev() - } - } - , 'ArrowUp') - -shortcut('seekDown', ShortcutCategory.READER_NAVIGATION, 'Next Page (Vertical)', - (ctx: any) => { - if (ctx.vertical) { - ctx.next() - } - } - , 'ArrowDown') - -shortcut('seekBegin', ShortcutCategory.READER_NAVIGATION, 'Goto First Page', - (ctx: any) => { - ctx.goToFirst() - } - , 'Home') - -shortcut('seekEnd', ShortcutCategory.READER_NAVIGATION, 'Goto Last Page', - (ctx: any) => { - ctx.goToLast() - } - , 'End') - -// Reader Settings - -shortcut('directionLTR', ShortcutCategory.READER_SETTINGS, 'Direction: Left to Right', - (ctx: any) => ctx.changeReadingDir(ReadingDirection.LEFT_TO_RIGHT) - , 'l') - -shortcut('directionRTL', ShortcutCategory.READER_SETTINGS, 'Direction: Right to Left', - (ctx: any) => ctx.changeReadingDir(ReadingDirection.RIGHT_TO_LEFT) - , 'r') - -shortcut('directionVRT', ShortcutCategory.READER_SETTINGS, 'Direction: Vertical', - (ctx: any) => ctx.changeReadingDir(ReadingDirection.VERTICAL), - 'v') - -shortcut('toggleDoublePage', ShortcutCategory.READER_SETTINGS, 'Toggle Double Page', - (ctx: any) => ctx.toggleDoublePages() - , 'd') - -shortcut('cycleScale', ShortcutCategory.READER_SETTINGS, 'Cycle Scale', - (ctx: any) => ctx.cycleScale() - , 'c') - -// Menus - -shortcut('toggleToolbar', ShortcutCategory.MENUS, 'Toggle Toolbar', - (ctx: any) => { - ctx.toolbar = !ctx.toolbar - }, - 'm') - -shortcut('toggleMenu', ShortcutCategory.MENUS, 'Toggle Settings Menu', - (ctx: any) => { - ctx.menu = !ctx.menu - }, - 's') - -shortcut('toggleExplorer', ShortcutCategory.MENUS, 'Toggle Explorer', - (ctx: any) => { - ctx.showThumbnailsExplorer = !ctx.showThumbnailsExplorer - }, 't') - -shortcut('escape', ShortcutCategory.MENUS, 'Close', - (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() - }, 'Escape') - -// Make sure all shortcuts are registered before this is called -export const shortcutHelp = new MultiMap() -const keyMapping = {} as Map - -function setupShortcuts () { - for (const s of SHORTCUTS) { - for (const key of s.keys) { - keyMapping[key] = s - shortcutHelp.add(s.category, { - key: KEY_DISPLAY[key] || key, - desc: s.description, - }) - } - } -} -setupShortcuts() - -export function executeShortcut (ctx: any, e: KeyboardEvent): boolean { - const k: string = e.key - return keyMapping[k]?.execute(ctx) -} diff --git a/komga-webui/src/functions/shortcuts/bookreader.ts b/komga-webui/src/functions/shortcuts/bookreader.ts new file mode 100644 index 000000000..02d6d7ce4 --- /dev/null +++ b/komga-webui/src/functions/shortcuts/bookreader.ts @@ -0,0 +1,41 @@ +import { Shortcut } from '@/types/shortcuts' +import { ReadingDirection } from '@/types/enum-books' + +export const shortcutsSettings = [ + new Shortcut('Left to Right', + (ctx: any) => ctx.changeReadingDir(ReadingDirection.LEFT_TO_RIGHT) + , 'l'), + new Shortcut('Right to Left', + (ctx: any) => ctx.changeReadingDir(ReadingDirection.RIGHT_TO_LEFT) + , 'r'), + new Shortcut('Vertical', + (ctx: any) => ctx.changeReadingDir(ReadingDirection.VERTICAL) + , 'v'), + new Shortcut('Webtoon', + (ctx: any) => ctx.changeReadingDir(ReadingDirection.WEBTOON) + , 'w'), + new Shortcut('Toggle double pages', + (ctx: any) => ctx.toggleDoublePages() + , 'd'), + new Shortcut('Cycle scale', + (ctx: any) => ctx.cycleScale() + , 'c'), +] + +export const shortcutsMenus = [ + new Shortcut('Show/hide toolbars', + (ctx: any) => ctx.toggleToolbars() + , 'm'), + new Shortcut('Show/hide settings menu', + (ctx: any) => ctx.toggleSettings() + , 's'), + new Shortcut('Show/hide thumbnails explorer', + (ctx: any) => ctx.toggleExplorer() + , 't'), + new Shortcut('Show/hide help', + (ctx: any) => ctx.toggleHelp() + , 'h'), + new Shortcut('Close', + (ctx: any) => ctx.closeDialog() + , 'Escape'), +] diff --git a/komga-webui/src/functions/shortcuts/paged-reader.ts b/komga-webui/src/functions/shortcuts/paged-reader.ts new file mode 100644 index 000000000..b044bfa15 --- /dev/null +++ b/komga-webui/src/functions/shortcuts/paged-reader.ts @@ -0,0 +1,34 @@ +import { Shortcut } from '@/types/shortcuts' + +export const shortcutsLTR = [ + new Shortcut('Previous page', + (ctx: any) => { + ctx.turnLeft() + }, 'ArrowLeft', '←'), + new Shortcut('Next page', + (ctx: any) => { + ctx.turnRight() + }, 'ArrowRight', '→'), +] + +export const shortcutsRTL = [ + new Shortcut('Previous page', + (ctx: any) => { + ctx.turnRight() + }, 'ArrowRight', '→'), + new Shortcut('Next page', + (ctx: any) => { + ctx.turnLeft() + }, 'ArrowLeft', '←'), +] + +export const shortcutsVertical = [ + new Shortcut('Previous page', + (ctx: any) => { + ctx.verticalPrev() + }, 'ArrowUp', '↑'), + new Shortcut('Next page', + (ctx: any) => { + ctx.verticalNext() + }, 'ArrowDown', '↓'), +] diff --git a/komga-webui/src/functions/shortcuts/reader.ts b/komga-webui/src/functions/shortcuts/reader.ts new file mode 100644 index 000000000..0e81a696a --- /dev/null +++ b/komga-webui/src/functions/shortcuts/reader.ts @@ -0,0 +1,12 @@ +import { Shortcut } from '@/types/shortcuts' + +export const shortcutsAll = [ + new Shortcut('First page', + (ctx: any) => { + ctx.goToFirst() + }, 'Home'), + new Shortcut('Last page', + (ctx: any) => { + ctx.goToLast() + }, 'End'), +] diff --git a/komga-webui/src/types/enum-reader.ts b/komga-webui/src/types/enum-reader.ts new file mode 100644 index 000000000..94355a732 --- /dev/null +++ b/komga-webui/src/types/enum-reader.ts @@ -0,0 +1,6 @@ +export enum ScaleType { + SCREEN = 'screen', + WIDTH = 'width', + HEIGHT = 'height', + ORIGINAL = 'original' +} diff --git a/komga-webui/src/types/komga-books.ts b/komga-webui/src/types/komga-books.ts index d001475cc..a8c32b690 100644 --- a/komga-webui/src/types/komga-books.ts +++ b/komga-webui/src/types/komga-books.ts @@ -28,6 +28,15 @@ interface PageDto { height?: number, } +interface PageDtoWithUrl { + number: number, + fileName: string, + mediaType: string, + width?: number, + height?: number, + url: string, +} + interface BookMetadataDto { created: string, lastModified: string, diff --git a/komga-webui/src/types/shortcuts.ts b/komga-webui/src/types/shortcuts.ts new file mode 100644 index 000000000..d7751011f --- /dev/null +++ b/komga-webui/src/types/shortcuts.ts @@ -0,0 +1,21 @@ +type Action = (ctx: any) => void + +export class Shortcut { + description: string + action: Action + key: string + display: string + + + constructor (description: string, action: Action, key: string, display: string = key) { + this.description = description + this.action = action + this.key = key + this.display = display + } + + execute (ctx: any): boolean { + this.action(ctx) + return true + } +} diff --git a/komga-webui/src/views/BookReader.vue b/komga-webui/src/views/BookReader.vue index 0b85902d5..ef3cc0304 100644 --- a/komga-webui/src/views/BookReader.vue +++ b/komga-webui/src/views/BookReader.vue @@ -1,19 +1,13 @@ @@ -268,15 +254,20 @@ import SettingsSelect from '@/components/SettingsSelect.vue' import SettingsSwitch from '@/components/SettingsSwitch.vue' import ThumbnailExplorerDialog from '@/components/dialogs/ThumbnailExplorerDialog.vue' -import ShortcutHelpMenu from '@/components/menus/ShortcutHelpMenu.vue' +import ShortcutHelpDialog from '@/components/dialogs/ShortcutHelpDialog.vue' 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' -import { isPageLandscape } from '@/functions/page' import { Location } from 'vue-router' +import PagedReader from '@/components/readers/PagedReader.vue' +import ContinuousReader from '@/components/readers/ContinuousReader.vue' +import { ScaleType } from '@/types/enum-reader' +import { ReadingDirectionText, ScaleTypeText } from '@/functions/reader' +import { shortcutsLTR, shortcutsRTL, shortcutsVertical } from '@/functions/shortcuts/paged-reader' +import { shortcutsMenus, shortcutsSettings } from '@/functions/shortcuts/bookreader' +import { shortcutsAll } from '@/functions/shortcuts/reader' const cookieFit = 'webreader.fit' const cookieReadingDirection = 'webreader.readingDirection' @@ -285,18 +276,18 @@ const cookieSwipe = 'webreader.swipe' const cookieAnimations = 'webreader.animations' const cookieBackground = 'webreader.background' -enum ImageFit { - WIDTH = 'width', - HEIGHT = 'height', - ORIGINAL = 'original' -} - export default Vue.extend({ name: 'BookReader', - components: { SettingsSwitch, SettingsSelect, ThumbnailExplorerDialog, ShortcutHelpMenu }, + components: { + ContinuousReader, + PagedReader, + SettingsSwitch, + SettingsSelect, + ThumbnailExplorerDialog, + ShortcutHelpDialog, + }, data: () => { return { - ImageFit, book: {} as BookDto, series: {} as SeriesDto, siblingPrevious: {} as BookDto, @@ -305,40 +296,37 @@ export default Vue.extend({ jumpToPreviousBook: false, jumpConfirmationDelay: 3000, snackReadingDirection: false, - pages: [] as PageDto[], + pages: [] as PageDtoWithUrl[], + page: 1, supportedMediaTypes: ['image/jpeg', 'image/png', 'image/gif'], convertTo: 'jpeg', - carouselPage: 0, - showThumbnailsExplorer: false, - toolbar: false, - menu: false, - dialogGoto: false, + showExplorer: false, + showToolbars: false, + showSettings: false, + showHelp: false, goToPage: 1, settings: { doublePages: false, swipe: true, - fit: ImageFit.HEIGHT, - readingDirection: ReadingDirection.LEFT_TO_RIGHT, animations: true, + scale: ScaleType.SCREEN, + readingDirection: ReadingDirection.LEFT_TO_RIGHT, backgroundColor: 'black', - imageFits: Object.values(ImageFit), - readingDirs: Object.values(ReadingDirection), }, + shortcuts: {} as any, notification: { enabled: false, message: '', timeout: 4000, }, - readingDirs: [ - { text: 'Left to right', value: ReadingDirection.LEFT_TO_RIGHT }, - { text: 'Right to left', value: ReadingDirection.RIGHT_TO_LEFT }, - { text: 'Vertical', value: ReadingDirection.VERTICAL }, - ], - imageFits: [ - { text: 'Fit to height', value: ImageFit.HEIGHT }, - { text: 'Fit to width', value: ImageFit.WIDTH }, - { text: 'Original', value: ImageFit.ORIGINAL }, - ], + readingDirs: Object.values(ReadingDirection).map(x => ({ + text: ReadingDirectionText[x], + value: x, + })), + scaleTypes: Object.values(ScaleType).map(x => ({ + text: ScaleTypeText[x], + value: x, + })), backgroundColors: [ { text: 'White', value: 'white' }, { text: 'Black', value: 'black' }, @@ -351,10 +339,10 @@ export default Vue.extend({ this.supportedMediaTypes.push('image/webp') } }) + this.shortcuts = this.$_.keyBy([...shortcutsSettings, ...shortcutsMenus, ...shortcutsAll], x => x.key) + window.addEventListener('keydown', this.keyPressed) }, async mounted () { - window.addEventListener('keydown', this.keyPressed) - this.loadFromCookie(cookieReadingDirection, (v) => { this.readingDirection = v }) @@ -368,14 +356,10 @@ export default Vue.extend({ this.swipe = (v === 'true') }) this.loadFromCookie(cookieFit, (v) => { - if (v) { - this.imageFit = v - } + this.scale = v }) this.loadFromCookie(cookieBackground, (v) => { - if (v) { - this.backgroundColor = v - } + this.backgroundColor = v }) this.setup(this.bookId, Number(this.$route.query.page)) @@ -397,7 +381,7 @@ export default Vue.extend({ next() }, watch: { - currentPage (val) { + page (val) { this.updateRoute() this.goToPage = val this.markProgress(val) @@ -410,32 +394,11 @@ export default Vue.extend({ }, }, computed: { - currentSlide (): number { - return this.carouselPage + 1 - }, - currentPage (): number { - if (this.carouselPage >= 0 && this.carouselPage < this.spreads.length && this.spreads.length > 0) { - return this.spreads[this.carouselPage][0].number - } - return 1 - }, - canPrev (): boolean { - return this.currentSlide > 1 - }, - canNext (): boolean { - return this.currentSlide < this.slidesCount + continuousReader (): boolean { + return this.readingDirection === ReadingDirection.WEBTOON }, progress (): number { - return this.currentPage / this.pagesCount * 100 - }, - maxHeight (): number | null { - return this.imageFit === ImageFit.HEIGHT ? this.$vuetify.breakpoint.height : null - }, - imgStyle (): string { - return this.imageFit === ImageFit.WIDTH ? 'height:intrinsic' : '' - }, - slidesCount (): number { - return this.spreads.length + return this.page / this.pagesCount * 100 }, pagesCount (): number { return this.pages.length @@ -443,6 +406,30 @@ export default Vue.extend({ bookTitle (): string { return getBookTitleCompact(this.book.metadata.title, this.series.metadata.title) }, + readingDirectionText (): string { + return ReadingDirectionText[this.readingDirection] + }, + shortcutsHelp (): object { + let nav = [] + switch (this.readingDirection) { + case ReadingDirection.LEFT_TO_RIGHT: + nav.push(...shortcutsLTR, ...shortcutsAll) + break + case ReadingDirection.RIGHT_TO_LEFT: + nav.push(...shortcutsRTL, ...shortcutsAll) + break + case ReadingDirection.VERTICAL: + nav.push(...shortcutsVertical, ...shortcutsAll) + break + default: + nav.push(...shortcutsAll) + } + return { + 'Reader Navigation': nav, + 'Settings': shortcutsSettings, + 'Menus': shortcutsMenus, + } + }, animations: { get: function (): boolean { @@ -453,31 +440,15 @@ export default Vue.extend({ this.$cookies.set(cookieAnimations, animations, Infinity) }, }, - readingDirection: { - get: function (): ReadingDirection { - return this.settings.readingDirection + scale: { + get: function (): ScaleType { + return this.settings.scale }, - set: function (readingDirection: ReadingDirection): void { - this.settings.readingDirection = readingDirection - this.$cookies.set(cookieReadingDirection, readingDirection, Infinity) - }, - }, - readingDirectionText (): string { - return this.readingDirs.find(x => x.value === this.readingDirection)?.text ?? '' - }, - flipDirection (): boolean { - return this.readingDirection === ReadingDirection.RIGHT_TO_LEFT - }, - vertical (): boolean { - return this.readingDirection === ReadingDirection.VERTICAL - }, - imageFit: { - get: function (): ImageFit { - return this.settings.fit - }, - set: function (fit: ImageFit): void { - this.settings.fit = fit - this.$cookies.set(cookieFit, fit, Infinity) + set: function (scale: ScaleType): void { + if (Object.values(ScaleType).includes(scale)) { + this.settings.scale = scale + this.$cookies.set(cookieFit, scale, Infinity) + } }, }, backgroundColor: { @@ -485,8 +456,21 @@ export default Vue.extend({ return this.settings.backgroundColor }, set: function (color: string): void { - this.settings.backgroundColor = color - this.$cookies.set(cookieBackground, color, Infinity) + if (this.backgroundColors.map(x => x.value).includes(color)) { + this.settings.backgroundColor = color + this.$cookies.set(cookieBackground, color, Infinity) + } + }, + }, + readingDirection: { + get: function (): ReadingDirection { + return this.settings.readingDirection + }, + set: function (readingDirection: ReadingDirection): void { + if (Object.values(ReadingDirection).includes(readingDirection)) { + this.settings.readingDirection = readingDirection + this.$cookies.set(cookieReadingDirection, readingDirection, Infinity) + } }, }, doublePages: { @@ -494,9 +478,7 @@ export default Vue.extend({ return this.settings.doublePages }, set: function (doublePages: boolean): void { - const current = this.currentPage this.settings.doublePages = doublePages - this.goTo(current) this.$cookies.set(cookieDoublePages, doublePages, Infinity) }, }, @@ -509,42 +491,17 @@ export default Vue.extend({ this.$cookies.set(cookieSwipe, swipe, Infinity) }, }, - spreads (): PageDto[][] { - if (this.pages.length === 0) return [] - if (this.doublePages) { - const spreads = [] - spreads.push([this.pages[0]]) - const pages = this.$_.drop(this.$_.dropRight(this.pages)) as PageDto[] - while (pages.length > 0) { - const p = pages.shift() as PageDto - if (isPageLandscape(p)) { - spreads.push([p]) - } else { - if (pages.length > 0) { - const p2 = pages.shift() as PageDto - if (isPageLandscape(p2)) { - spreads.push([p]) - spreads.push([p2]) - } else { - spreads.push([p, p2]) - } - } - } - } - spreads.push([this.pages[this.pages.length - 1]]) - return spreads - } else { - return this.pages.map(p => [p]) - } - }, }, methods: { keyPressed (e: KeyboardEvent) { - executeShortcut(this, e) + this.shortcuts[e.key]?.execute(this) }, async setup (bookId: string, page: number) { this.book = await this.$komgaBooks.getBook(bookId) - this.pages = await this.$komgaBooks.getBookPages(bookId) + const pageDtos = (await this.$komgaBooks.getBookPages(bookId)) + pageDtos.forEach((p: any) => p['url'] = this.getPageUrl(p)) + this.pages = pageDtos as PageDtoWithUrl[] + if (page >= 1 && page <= this.pagesCount) { this.goTo(page) } else if (this.book.readProgress?.completed === false) { @@ -554,16 +511,10 @@ export default Vue.extend({ } // set non-persistent reading direction if exists in metadata - switch (this.book.metadata.readingDirection) { - case ReadingDirection.LEFT_TO_RIGHT: - case ReadingDirection.RIGHT_TO_LEFT: - case ReadingDirection.VERTICAL: - if (this.readingDirection !== this.book.metadata.readingDirection) { - // bypass setter so cookies aren't set - this.settings.readingDirection = this.book.metadata.readingDirection - this.snackReadingDirection = true - } - break + if (this.book.metadata.readingDirection in ReadingDirection && this.readingDirection !== this.book.metadata.readingDirection) { + // bypass setter so cookies aren't set + this.settings.readingDirection = this.book.metadata.readingDirection as ReadingDirection + this.snackReadingDirection = true } try { @@ -577,49 +528,31 @@ export default Vue.extend({ this.siblingPrevious = {} as BookDto } }, - getPageUrl (page: number): string { - if (!this.supportedMediaTypes.includes(this.pages[page - 1].mediaType)) { - return bookPageUrl(this.bookId, page, this.convertTo) + getPageUrl (page: PageDto): string { + if (!this.supportedMediaTypes.includes(page.mediaType)) { + return bookPageUrl(this.bookId, page.number, this.convertTo) } else { - return bookPageUrl(this.bookId, page) + return bookPageUrl(this.bookId, page.number) } }, - turnRight () { - if (this.vertical) return - return this.flipDirection ? this.prev() : this.next() - }, - turnLeft () { - if (this.vertical) return - return this.flipDirection ? this.next() : this.prev() - }, - verticalPrev () { - if (this.vertical) this.prev() - }, - verticalNext () { - if (this.vertical) this.next() - }, - prev () { - if (this.canPrev) { - this.carouselPage-- - window.scrollTo(0, 0) + jumpToPrevious () { + if (this.jumpToPreviousBook) { + this.previousBook() } else { - if (this.jumpToPreviousBook) { - this.previousBook() - } else { - this.jumpToPreviousBook = true - } + this.jumpToPreviousBook = true } }, - next () { - if (this.canNext) { - this.carouselPage++ - window.scrollTo(0, 0) + jumpToNext () { + if (this.jumpToNextBook) { + this.nextBook() } else { - if (this.jumpToNextBook) { - this.nextBook() - } else { - this.jumpToNextBook = true - } + this.jumpToNextBook = true + } + }, + previousBook () { + if (!this.$_.isEmpty(this.siblingPrevious)) { + this.jumpToPreviousBook = false + this.$router.push({ name: 'read-book', params: { bookId: this.siblingPrevious.id.toString() } }) } }, nextBook () { @@ -630,14 +563,8 @@ export default Vue.extend({ this.$router.push({ name: 'read-book', params: { bookId: this.siblingNext.id.toString() } }) } }, - previousBook () { - if (!this.$_.isEmpty(this.siblingPrevious)) { - this.jumpToPreviousBook = false - this.$router.push({ name: 'read-book', params: { bookId: this.siblingPrevious.id.toString() } }) - } - }, goTo (page: number) { - this.carouselPage = this.toSpreadIndex(page) + this.page = page }, goToFirst () { this.goTo(1) @@ -650,46 +577,13 @@ export default Vue.extend({ name: this.$route.name, params: { bookId: this.$route.params.bookId }, query: { - page: this.currentPage.toString(), + page: this.page.toString(), }, } as Location) }, closeBook () { this.$router.push({ name: 'browse-book', params: { bookId: this.bookId.toString() } }) }, - toSinglePages (i: number): number { - if (i === 1) return 1 - if (i === this.slidesCount) return this.pagesCount - return (i - 1) * 2 - }, - toSpreadIndex (i: number): number { - if (this.spreads.length > 0) { - if (this.doublePages) { - for (let j = 0; j < this.spreads.length; j++) { - for (let k = 0; k < this.spreads[j].length; k++) { - if (this.spreads[j][k].number === i) { - return j - } - } - } - } else { - return i - 1 - } - } - return i - 1 - }, - eagerLoad (spreadIndex: number): boolean { - return Math.abs(this.carouselPage - spreadIndex) <= 2 - }, - maxWidth (spreadIndex: number): number | null { - if (this.imageFit !== ImageFit.WIDTH) { - return null - } - if (this.doublePages && this.spreads[spreadIndex].length === 2) { - return this.$vuetify.breakpoint.width / 2 - } - return this.$vuetify.breakpoint.width - }, loadFromCookie (cookieKey: string, setter: (value: any) => void): void { if (this.$cookies.isKey(cookieKey)) { setter(this.$cookies.get(cookieKey)) @@ -697,24 +591,51 @@ export default Vue.extend({ }, changeReadingDir (dir: ReadingDirection) { this.readingDirection = dir - const i = this.settings.readingDirs.indexOf(this.readingDirection) - const text = this.readingDirs[i].text + const text = ReadingDirectionText[this.readingDirection] this.sendNotification(`Changing Reading Direction to: ${text}`) }, cycleScale () { - const fit: ImageFit = this.settings.fit - const i = (this.settings.imageFits.indexOf(fit) + 1) % (this.settings.imageFits.length) - this.imageFit = this.settings.imageFits[i] - const text = this.imageFits[i].text - // The text here only works cause this.imageFits has the same index structure as the ImageFit enum + if (this.continuousReader) return + const enumValues = Object.values(ScaleType) + const i = (enumValues.indexOf(this.settings.scale) + 1) % (enumValues.length) + this.scale = enumValues[i] + const text = ScaleTypeText[this.scale] this.sendNotification(`Cycling Scale: ${text}`) }, toggleDoublePages () { + if (this.continuousReader) return this.doublePages = !this.doublePages this.sendNotification(`${this.doublePages ? 'Enabled' : 'Disabled'} Double Pages`) }, + toggleToolbars () { + this.showToolbars = !this.showToolbars + }, + toggleExplorer () { + this.showExplorer = !this.showExplorer + }, + toggleSettings () { + this.showSettings = !this.showSettings + }, + toggleHelp () { + this.showHelp = !this.showHelp + }, + closeDialog () { + if (this.showExplorer) { + this.showExplorer = false + return + } + if (this.showSettings) { + this.showSettings = false + return + } + if (this.showToolbars) { + this.showToolbars = false + return + } + this.closeBook() + }, sendNotification (message: string, timeout: number = 4000) { - this.notification.timeout = 4000 + this.notification.timeout = timeout this.notification.message = message this.notification.enabled = true }, @@ -724,17 +645,11 @@ export default Vue.extend({ }, }) -