add pdf viewer

This commit is contained in:
Yisroel Baum 2026-06-25 22:39:21 +03:00
parent f1c4e896b6
commit 208c683dac
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
7 changed files with 1114 additions and 13 deletions

View file

@ -1,16 +1,26 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { computed, watch } from 'vue'
import { computed, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import ElementParentLink from '@/components/ElementParentLink.vue'
import ElementSiblingNavigation from '@/components/ElementSiblingNavigation.vue'
import PdfViewerModal from '@/components/PdfViewerModal.vue'
import SiteHeader from '@/components/SiteHeader.vue'
import { useElementsStore } from '@/stores/elements'
type TimestampPart = string | undefined
type ElementPdfFolder = 'short' | 'long'
interface ActivePdf {
openUrl: string
sourceUrl: string
title: string
}
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL as string
const route = useRoute()
const elementsStore = useElementsStore()
const activePdf = ref<ActivePdf | null>(null)
const {
element,
childElements,
@ -46,9 +56,23 @@ const youtubeEmbedUrl = computed(() => {
return getYoutubeEmbedUrl(youtubeUrl)
})
const activePdfTitle = computed(() => {
return activePdf.value?.title ?? ''
})
const activePdfUrl = computed(() => {
return activePdf.value?.sourceUrl ?? ''
})
const activePdfOpenUrl = computed(() => {
return activePdf.value?.openUrl ?? ''
})
watch(
elementId,
(currentElementId) => {
activePdf.value = null
if (typeof currentElementId !== 'string' || currentElementId === '') {
return
}
@ -58,6 +82,61 @@ watch(
{ immediate: true },
)
function openPdfViewer(pdfTitle: string, pdfUrl: string | null): void {
if (pdfUrl === null) {
return
}
activePdf.value = {
openUrl: pdfUrl,
sourceUrl: getPdfViewerUrl(pdfUrl),
title: pdfTitle,
}
}
function closePdfViewer(): void {
activePdf.value = null
}
function getPdfViewerUrl(pdfUrl: string): string {
let parsedPdfUrl: URL
try {
parsedPdfUrl = new URL(pdfUrl, window.location.origin)
} catch {
return pdfUrl
}
const pathSegments = parsedPdfUrl.pathname
.split('/')
.filter((pathSegment) => {
return pathSegment !== ''
})
if (pathSegments.length !== 4) {
return pdfUrl
}
if (pathSegments[0] !== 'storage' || pathSegments[1] !== 'element-pdfs') {
return pdfUrl
}
const pdfFolder = pathSegments[2]
const fileName = pathSegments[3]
if (!isElementPdfFolder(pdfFolder) || fileName === undefined) {
return pdfUrl
}
const encodedFileName = encodeURIComponent(fileName)
return `${API_BASE_URL}/api/element-pdfs/${pdfFolder}/${encodedFileName}`
}
function isElementPdfFolder(
pdfFolder: string | undefined,
): pdfFolder is ElementPdfFolder {
return pdfFolder === 'short' || pdfFolder === 'long'
}
function getYoutubeEmbedUrl(youtubeUrl: string | null): string | null {
if (youtubeUrl === null || youtubeUrl === '') {
return null
@ -266,26 +345,24 @@ function isShortYoutubeHost(hostname: string): boolean {
v-if="element.shortPdfPath !== null || element.longPdfPath !== null"
class="element-page__actions"
>
<a
<button
v-if="element.shortPdfPath !== null"
:href="element.shortPdfPath"
type="button"
class="element-page__pdf-link"
data-cy="element-short-pdf-link"
target="_blank"
rel="noreferrer"
data-cy="element-short-pdf-viewer-button"
@click="openPdfViewer('Short PDF', element.shortPdfPath)"
>
View Short PDF
</a>
<a
</button>
<button
v-if="element.longPdfPath !== null"
:href="element.longPdfPath"
type="button"
class="element-page__pdf-link"
data-cy="element-long-pdf-link"
target="_blank"
rel="noreferrer"
data-cy="element-long-pdf-viewer-button"
@click="openPdfViewer('Long PDF', element.longPdfPath)"
>
View Long PDF
</a>
</button>
</div>
<nav
@ -326,6 +403,13 @@ function isShortYoutubeHost(hostname: string): boolean {
/>
</section>
</main>
<PdfViewerModal
:is-open="activePdf !== null"
:open-url="activePdfOpenUrl"
:pdf-title="activePdfTitle"
:pdf-url="activePdfUrl"
@close="closePdfViewer"
/>
</div>
</template>