From a8736b0edb99ad433c8a7795ea1f31a8751c2f41 Mon Sep 17 00:00:00 2001
From: pacien
Date: Mon, 31 Oct 2022 00:20:38 +0100
Subject: viewer/epub: add EPUB item handler

This adds a paginated ePub viewer using epub.js.
---
 .../views/item_handlers/async/AsyncEpubViewer.vue  | 149 +++++++++++++++++++++
 1 file changed, 149 insertions(+)
 create mode 100644 viewer/src/views/item_handlers/async/AsyncEpubViewer.vue

(limited to 'viewer/src/views/item_handlers/async/AsyncEpubViewer.vue')

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 @@
+<!--
+-- ldgallery - A static generator which turns a collection of tagged
+--             pictures into a searchable web gallery.
+--
+-- Copyright (C) 2022  Pacien TRAN-GIRARD
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU Affero General Public License as
+-- published by the Free Software Foundation, either version 3 of the
+-- License, or (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU Affero General Public License for more details.
+--
+-- You should have received a copy of the GNU Affero General Public License
+-- along with this program.  If not, see <https://www.gnu.org/licenses/>.
+-->
+
+<template>
+  <div :class="$style.container">
+    <div
+      ref="view"
+      :class="$style.epubView"
+      class="scrollbar"
+    />
+
+    <ul
+      v-if="prevSection || nextSection"
+      :class="$style.navBar"
+    >
+      <li>
+        <a
+          v-if="prevSection"
+          @click.prevent="goToPrevSection"
+        >« {{ prevSectionLabel }}</a>
+      </li>
+
+      <li>
+        {{ currSectionLabel }}
+      </li>
+
+      <li>
+        <a
+          v-if="nextSection"
+          @click.prevent="goToNextSection"
+        >{{ nextSectionLabel }} »</a>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script setup lang="ts">
+
+import { EPUBItem } from '@/@types/gallery';
+import { useItemResource } from '@/services/ui/ldItemResourceUrl';
+import ePub from 'epubjs';
+import { SpineItem } from 'epubjs/types/section';
+import { PropType, Ref, ref, toRef, computed, watchEffect } from 'vue';
+import { useI18n } from 'vue-i18n';
+const { t } = useI18n();
+
+const props = defineProps({
+  item: { type: Object as PropType<EPUBItem>, required: true },
+});
+
+const { itemResourceUrl } = useItemResource(toRef(props, 'item'));
+const view = ref<HTMLDivElement>();
+
+const book = computed(() => ePub(itemResourceUrl.value));
+
+const rendition = computed(() => {
+  if (!view.value) return;
+  return book.value.renderTo(view.value, {
+    flow: 'scrolled-doc',
+    width: '100%',
+    fullsize: true,
+  });
+});
+
+const currSection = ref<SpineItem>();
+const prevSection = ref<SpineItem>();
+const nextSection = ref<SpineItem>();
+
+// TODO: reflow on side panel open/close event, like when resizing the window
+
+watchEffect(async() => {
+  if (!rendition.value) return;
+  await rendition.value.display();
+  rendition.value.on('rendered', updateNavigation);
+});
+
+function updateNavigation(currentSection: SpineItem) {
+  currSection.value = currentSection;
+  prevSection.value = currentSection.prev();
+  nextSection.value = currentSection.next();
+}
+
+const currSectionLabel = computed(() => getSectionTitle(currSection) ?? '');
+const prevSectionLabel = computed(() =>
+  getSectionTitle(prevSection) ?? t('epubViewer.previousSection'));
+const nextSectionLabel = computed(() =>
+  getSectionTitle(nextSection) ?? t('epubViewer.nextSection'));
+
+function getSectionTitle(section: Ref<SpineItem | undefined>): string | null {
+  if (!section.value?.href) return null;
+  return book.value?.navigation.get(section.value.href).label;
+}
+
+function goToPrevSection() {
+  rendition.value?.prev();
+}
+
+function goToNextSection() {
+  rendition.value?.next();
+}
+</script>
+
+<style lang="scss" module>
+@import "~@/assets/scss/theme";
+
+.container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+.epubView {
+  flex: 1;
+  overflow-x: hidden;
+}
+
+.navBar {
+  display: flex;
+  flex-direction: row;
+  list-style-type: none;
+  margin: 0;
+  padding: .75em;
+
+  background-color: $panel-bottom-bgcolor;
+  color: $panel-bottom-txtcolor;
+
+  > * {
+    flex: 1;
+    text-align: center;
+  }
+}
+</style>
-- 
cgit v1.2.3


From f6ce45309e3d0c2b165aaa1d4d3ac24ba08d466f Mon Sep 17 00:00:00 2001
From: Zéro~Informatique
Date: Sun, 27 Nov 2022 21:00:56 +0100
Subject: viewer/epub: resize on drawer movement

Fixed data leaks (view.innerHTML + rendered event)
---
 .../views/item_handlers/async/AsyncEpubViewer.vue  | 39 +++++++++++++---------
 1 file changed, 23 insertions(+), 16 deletions(-)

(limited to 'viewer/src/views/item_handlers/async/AsyncEpubViewer.vue')

diff --git a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
index 712a844..20b1bee 100644
--- a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
+++ b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
@@ -52,43 +52,50 @@
 </template>
 
 <script setup lang="ts">
-
 import { EPUBItem } from '@/@types/gallery';
 import { useItemResource } from '@/services/ui/ldItemResourceUrl';
-import ePub from 'epubjs';
+import { useUiStore } from '@/store/uiStore';
+import ePub, { Rendition } from 'epubjs';
 import { SpineItem } from 'epubjs/types/section';
-import { PropType, Ref, ref, toRef, computed, watchEffect } from 'vue';
+import { computed, PropType, Ref, ref, toRef, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
+
 const { t } = useI18n();
+const uiStore = useUiStore();
 
 const props = defineProps({
   item: { type: Object as PropType<EPUBItem>, required: true },
 });
 
 const { itemResourceUrl } = useItemResource(toRef(props, 'item'));
+
 const view = ref<HTMLDivElement>();
+const rendition = ref<Rendition>();
+const currSection = ref<SpineItem>();
+const prevSection = ref<SpineItem>();
+const nextSection = ref<SpineItem>();
 
 const book = computed(() => ePub(itemResourceUrl.value));
 
-const rendition = computed(() => {
-  if (!view.value) return;
-  return book.value.renderTo(view.value, {
+watch([book, view], ([book, view]) => {
+  if (!view) return;
+  view.innerHTML = '';
+  rendition.value = book.renderTo(view, {
     flow: 'scrolled-doc',
     width: '100%',
-    fullsize: true,
   });
 });
 
-const currSection = ref<SpineItem>();
-const prevSection = ref<SpineItem>();
-const nextSection = ref<SpineItem>();
-
-// TODO: reflow on side panel open/close event, like when resizing the window
+watch(rendition, async(rendition, oldRendition) => {
+  if (!rendition) return;
+  oldRendition?.off('rendered', updateNavigation);
+  await rendition.display();
+  rendition.on('rendered', updateNavigation);
+});
 
-watchEffect(async() => {
-  if (!rendition.value) return;
-  await rendition.value.display();
-  rendition.value.on('rendered', updateNavigation);
+watch(() => uiStore.fullWidth, () => {
+  // Simulate a window resize to force EPub to resize the container
+  setTimeout(() => window.dispatchEvent(new Event('resize')));
 });
 
 function updateNavigation(currentSection: SpineItem) {
-- 
cgit v1.2.3


From 7874bb98ed38468c77d003ccf5f81130353254e0 Mon Sep 17 00:00:00 2001
From: pacien
Date: Mon, 28 Nov 2022 02:51:29 +0100
Subject: viewer/epub: use icons for navigation

---
 .../views/item_handlers/async/AsyncEpubViewer.vue  | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

(limited to 'viewer/src/views/item_handlers/async/AsyncEpubViewer.vue')

diff --git a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
index 20b1bee..2d5c370 100644
--- a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
+++ b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
@@ -34,7 +34,14 @@
         <a
           v-if="prevSection"
           @click.prevent="goToPrevSection"
-        >« {{ prevSectionLabel }}</a>
+        >
+          <fa-icon
+            :icon="faSquareCaretLeft"
+            size="lg"
+            alt="«"
+          />
+          {{ prevSectionLabel }}
+        </a>
       </li>
 
       <li>
@@ -45,7 +52,14 @@
         <a
           v-if="nextSection"
           @click.prevent="goToNextSection"
-        >{{ nextSectionLabel }} »</a>
+        >
+          {{ nextSectionLabel }}
+          <fa-icon
+            :icon="faSquareCaretRight"
+            size="lg"
+            alt="»"
+          />
+        </a>
       </li>
     </ul>
   </div>
@@ -59,6 +73,10 @@ import ePub, { Rendition } from 'epubjs';
 import { SpineItem } from 'epubjs/types/section';
 import { computed, PropType, Ref, ref, toRef, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
+import {
+  faSquareCaretLeft,
+  faSquareCaretRight,
+} from '@fortawesome/free-solid-svg-icons';
 
 const { t } = useI18n();
 const uiStore = useUiStore();
-- 
cgit v1.2.3


From 2aaeb66a074571979e0337e14a011b8038bdf124 Mon Sep 17 00:00:00 2001
From: pacien
Date: Mon, 28 Nov 2022 02:52:45 +0100
Subject: viewer/epub: use LdLink for nav links

---
 viewer/src/views/item_handlers/async/AsyncEpubViewer.vue | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

(limited to 'viewer/src/views/item_handlers/async/AsyncEpubViewer.vue')

diff --git a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
index 2d5c370..07e6dcb 100644
--- a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
+++ b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
@@ -31,9 +31,9 @@
       :class="$style.navBar"
     >
       <li>
-        <a
+        <LdLink
           v-if="prevSection"
-          @click.prevent="goToPrevSection"
+          @click="goToPrevSection"
         >
           <fa-icon
             :icon="faSquareCaretLeft"
@@ -41,7 +41,7 @@
             alt="«"
           />
           {{ prevSectionLabel }}
-        </a>
+        </LdLink>
       </li>
 
       <li>
@@ -49,9 +49,9 @@
       </li>
 
       <li>
-        <a
+        <LdLink
           v-if="nextSection"
-          @click.prevent="goToNextSection"
+          @click="goToNextSection"
         >
           {{ nextSectionLabel }}
           <fa-icon
@@ -59,7 +59,7 @@
             size="lg"
             alt="»"
           />
-        </a>
+        </LdLink>
       </li>
     </ul>
   </div>
@@ -73,6 +73,7 @@ import ePub, { Rendition } from 'epubjs';
 import { SpineItem } from 'epubjs/types/section';
 import { computed, PropType, Ref, ref, toRef, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
+import LdLink from '@/components/LdLink.vue';
 import {
   faSquareCaretLeft,
   faSquareCaretRight,
-- 
cgit v1.2.3


From 711d6a471930ef9caf6b09129226c68f5d74575a Mon Sep 17 00:00:00 2001
From: pacien
Date: Mon, 28 Nov 2022 02:59:12 +0100
Subject: viewer/epub: normalise item handler background for books

Book files expect a clear background.
---
 viewer/src/views/item_handlers/async/AsyncEpubViewer.vue | 1 +
 1 file changed, 1 insertion(+)

(limited to 'viewer/src/views/item_handlers/async/AsyncEpubViewer.vue')

diff --git a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
index 07e6dcb..b02eab3 100644
--- a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
+++ b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
@@ -150,6 +150,7 @@ function goToNextSection() {
   display: flex;
   flex-direction: column;
   height: 100%;
+  background-color: $viewer-epub-background;
 }
 
 .epubView {
-- 
cgit v1.2.3


From d84f0f48c9b1dc73ec20a1cf5c31feeb744aa3d9 Mon Sep 17 00:00:00 2001
From: pacien
Date: Mon, 28 Nov 2022 03:03:40 +0100
Subject: viewer/epub: expand navbar links clickble area

---
 viewer/src/views/item_handlers/async/AsyncEpubViewer.vue | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

(limited to 'viewer/src/views/item_handlers/async/AsyncEpubViewer.vue')

diff --git a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
index b02eab3..b5c0cb4 100644
--- a/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
+++ b/viewer/src/views/item_handlers/async/AsyncEpubViewer.vue
@@ -168,9 +168,13 @@ function goToNextSection() {
   background-color: $panel-bottom-bgcolor;
   color: $panel-bottom-txtcolor;
 
-  > * {
+  > li {
     flex: 1;
     text-align: center;
+
+    > a {
+      padding: .5em;
+    }
   }
 }
 </style>
-- 
cgit v1.2.3

