aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--viewer/src/assets/scss/theme.scss2
-rw-r--r--viewer/src/locales/en.yml3
-rw-r--r--viewer/src/views/GalleryNavigation.vue2
-rw-r--r--viewer/src/views/item_handlers/async/AsyncEpubViewer.vue149
-rw-r--r--viewer/src/views/item_handlers/async/index.ts23
5 files changed, 179 insertions, 0 deletions
diff --git a/viewer/src/assets/scss/theme.scss b/viewer/src/assets/scss/theme.scss
index 63febbf..fe70f17 100644
--- a/viewer/src/assets/scss/theme.scss
+++ b/viewer/src/assets/scss/theme.scss
@@ -41,6 +41,8 @@ $dropdown-item-color: $palette-600;
41$dropdown-item-hover-color: $palette-500; 41$dropdown-item-hover-color: $palette-500;
42$panel-top-bgcolor: $palette-800; 42$panel-top-bgcolor: $palette-800;
43$panel-top-txtcolor: $primary; 43$panel-top-txtcolor: $primary;
44$panel-bottom-bgcolor: $palette-800;
45$panel-bottom-txtcolor: $primary;
44$panel-left-bgcolor: $palette-800; 46$panel-left-bgcolor: $palette-800;
45$panel-left-txtcolor: $primary; 47$panel-left-txtcolor: $primary;
46$command-buttons-bgcolor: $palette-700; 48$command-buttons-bgcolor: $palette-700;
diff --git a/viewer/src/locales/en.yml b/viewer/src/locales/en.yml
index 86ecd49..280f18b 100644
--- a/viewer/src/locales/en.yml
+++ b/viewer/src/locales/en.yml
@@ -15,6 +15,9 @@ directory:
15 no-results: Empty directory 15 no-results: Empty directory
16download: 16download:
17 download-file-fmt: Download {0} 17 download-file-fmt: Download {0}
18epubViewer:
19 previousSection: Previous section
20 nextSection: Next section
18gallery: 21gallery:
19 resource-loading-error: Error loading resource 22 resource-loading-error: Error loading resource
20 unknown-resource: Resource not found 23 unknown-resource: Resource not found
diff --git a/viewer/src/views/GalleryNavigation.vue b/viewer/src/views/GalleryNavigation.vue
index 0869aaf..b342c52 100644
--- a/viewer/src/views/GalleryNavigation.vue
+++ b/viewer/src/views/GalleryNavigation.vue
@@ -44,6 +44,7 @@ import { computedEager } from '@vueuse/shared';
44import { computed, watchEffect } from 'vue'; 44import { computed, watchEffect } from 'vue';
45import { useI18n } from 'vue-i18n'; 45import { useI18n } from 'vue-i18n';
46import GallerySearch from './GallerySearch.vue'; 46import GallerySearch from './GallerySearch.vue';
47import { EpubViewer } from './item_handlers/async';
47import AudioViewer from './item_handlers/AudioViewer.vue'; 48import AudioViewer from './item_handlers/AudioViewer.vue';
48import DirectoryViewer from './item_handlers/DirectoryViewer.vue'; 49import DirectoryViewer from './item_handlers/DirectoryViewer.vue';
49import DownloadViewer from './item_handlers/DownloadViewer.vue'; 50import DownloadViewer from './item_handlers/DownloadViewer.vue';
@@ -67,6 +68,7 @@ const COMPONENT_BY_TYPE: Record<ItemType, unknown> = {
67 plaintext: PlainTextViewer, 68 plaintext: PlainTextViewer,
68 markdown: MarkdownViewer, 69 markdown: MarkdownViewer,
69 pdf: PdfViewer, 70 pdf: PdfViewer,
71 epub: EpubViewer,
70 video: VideoViewer, 72 video: VideoViewer,
71 audio: AudioViewer, 73 audio: AudioViewer,
72 other: DownloadViewer, 74 other: DownloadViewer,
diff --git a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
new file mode 100644
index 0000000..712a844
--- /dev/null
+++ b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
@@ -0,0 +1,149 @@
1<!--
2-- ldgallery - A static generator which turns a collection of tagged
3-- pictures into a searchable web gallery.
4--
5-- Copyright (C) 2022 Pacien TRAN-GIRARD
6--
7-- This program is free software: you can redistribute it and/or modify
8-- it under the terms of the GNU Affero General Public License as
9-- published by the Free Software Foundation, either version 3 of the
10-- License, or (at your option) any later version.
11--
12-- This program is distributed in the hope that it will be useful,
13-- but WITHOUT ANY WARRANTY; without even the implied warranty of
14-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15-- GNU Affero General Public License for more details.
16--
17-- You should have received a copy of the GNU Affero General Public License
18-- along with this program. If not, see <https://www.gnu.org/licenses/>.
19-->
20
21<template>
22 <div :class="$style.container">
23 <div
24 ref="view"
25 :class="$style.epubView"
26 class="scrollbar"
27 />
28
29 <ul
30 v-if="prevSection || nextSection"
31 :class="$style.navBar"
32 >
33 <li>
34 <a
35 v-if="prevSection"
36 @click.prevent="goToPrevSection"
37 >« {{ prevSectionLabel }}</a>
38 </li>
39
40 <li>
41 {{ currSectionLabel }}
42 </li>
43
44 <li>
45 <a
46 v-if="nextSection"
47 @click.prevent="goToNextSection"
48 >{{ nextSectionLabel }} »</a>
49 </li>
50 </ul>
51 </div>
52</template>
53
54<script setup lang="ts">
55
56import { EPUBItem } from '@/@types/gallery';
57import { useItemResource } from '@/services/ui/ldItemResourceUrl';
58import ePub from 'epubjs';
59import { SpineItem } from 'epubjs/types/section';
60import { PropType, Ref, ref, toRef, computed, watchEffect } from 'vue';
61import { useI18n } from 'vue-i18n';
62const { t } = useI18n();
63
64const props = defineProps({
65 item: { type: Object as PropType<EPUBItem>, required: true },
66});
67
68const { itemResourceUrl } = useItemResource(toRef(props, 'item'));
69const view = ref<HTMLDivElement>();
70
71const book = computed(() => ePub(itemResourceUrl.value));
72
73const rendition = computed(() => {
74 if (!view.value) return;
75 return book.value.renderTo(view.value, {
76 flow: 'scrolled-doc',
77 width: '100%',
78 fullsize: true,
79 });
80});
81
82const currSection = ref<SpineItem>();
83const prevSection = ref<SpineItem>();
84const nextSection = ref<SpineItem>();
85
86// TODO: reflow on side panel open/close event, like when resizing the window
87
88watchEffect(async() => {
89 if (!rendition.value) return;
90 await rendition.value.display();
91 rendition.value.on('rendered', updateNavigation);
92});
93
94function updateNavigation(currentSection: SpineItem) {
95 currSection.value = currentSection;
96 prevSection.value = currentSection.prev();
97 nextSection.value = currentSection.next();
98}
99
100const currSectionLabel = computed(() => getSectionTitle(currSection) ?? '');
101const prevSectionLabel = computed(() =>
102 getSectionTitle(prevSection) ?? t('epubViewer.previousSection'));
103const nextSectionLabel = computed(() =>
104 getSectionTitle(nextSection) ?? t('epubViewer.nextSection'));
105
106function getSectionTitle(section: Ref<SpineItem | undefined>): string | null {
107 if (!section.value?.href) return null;
108 return book.value?.navigation.get(section.value.href).label;
109}
110
111function goToPrevSection() {
112 rendition.value?.prev();
113}
114
115function goToNextSection() {
116 rendition.value?.next();
117}
118</script>
119
120<style lang="scss" module>
121@import "~@/assets/scss/theme";
122
123.container {
124 display: flex;
125 flex-direction: column;
126 height: 100%;
127}
128
129.epubView {
130 flex: 1;
131 overflow-x: hidden;
132}
133
134.navBar {
135 display: flex;
136 flex-direction: row;
137 list-style-type: none;
138 margin: 0;
139 padding: .75em;
140
141 background-color: $panel-bottom-bgcolor;
142 color: $panel-bottom-txtcolor;
143
144 > * {
145 flex: 1;
146 text-align: center;
147 }
148}
149</style>
diff --git a/viewer/src/views/item_handlers/async/index.ts b/viewer/src/views/item_handlers/async/index.ts
new file mode 100644
index 0000000..d4ca996
--- /dev/null
+++ b/viewer/src/views/item_handlers/async/index.ts
@@ -0,0 +1,23 @@
1/* ldgallery - A static generator which turns a collection of tagged
2-- pictures into a searchable web gallery.
3--
4-- Copyright (C) 2022 Pacien TRAN-GIRARD
5--
6-- This program is free software: you can redistribute it and/or modify
7-- it under the terms of the GNU Affero General Public License as
8-- published by the Free Software Foundation, either version 3 of the
9-- License, or (at your option) any later version.
10--
11-- This program is distributed in the hope that it will be useful,
12-- but WITHOUT ANY WARRANTY; without even the implied warranty of
13-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the