Skip to content

Commit

Permalink
feat: use ocis images from same directory level (#48)
Browse files Browse the repository at this point in the history
* feat: use images from same directory level

* refactor: filter media files

* refactor: revoke urls before unmounting

* fix: parse blob url of used media files only

* fix: rebuild src of local media files only

* fix: fit image to a slide height

* docs: add caching TODO comment

* refactor: fix lint errors

* refactor: fix lint errors

* chore: add changelog item
  • Loading branch information
saw-jan committed Jun 11, 2024
1 parent 2b16f83 commit c0a7a2b
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Use ocis images from same directory level (https://github.com/JankariTech/web-app-presentation-viewer/pull/48)

### Fixed

### Changed
Expand Down
144 changes: 135 additions & 9 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
:class="{ 'dark-mode': isDarkMode }"
>
<div class="reveal">
<div class="slides">
<div id="slideContainer" ref="slideContainer" class="slides">
<section
:data-markdown="url"
:data-separator="dataSeparator"
Expand All @@ -17,10 +17,16 @@
</template>

<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { useThemeStore } from '@ownclouders/web-pkg'
import { computed, onMounted, onBeforeUnmount, ref, unref, watch } from 'vue'
import {
useThemeStore,
useAppDefaults,
useAppFileHandling,
useClientService,
useAppsStore,
useConfigStore
} from '@ownclouders/web-pkg'
import { Resource } from '@ownclouders/web-client/src'
import Reveal from 'reveal.js'
import RevealMarkdown from 'reveal.js/plugin/markdown/markdown'
import RevealHighlight from 'reveal.js/plugin/highlight/highlight'
Expand All @@ -29,10 +35,27 @@ import 'reveal.js/dist/reveal.css'
import 'reveal.js/plugin/highlight/monokai.css'
import 'reveal.js/dist/theme/white.css'
const dataSeparator = '\r?\n---\r?\n'
const dataSeparatorVertical = '\r?\n--\r?\n'
import { getMediaMimeTypes } from './helpers/mediaMimeTypes'
import { id as appId } from '../public/manifest.json'
const themeStore = useThemeStore()
const { loadFolderForFileContext, currentFileContext, activeFiles } = useAppDefaults({
applicationId: appId
})
const { getUrlForResource, revokeUrl } = useAppFileHandling({
clientService: useClientService()
})
const appsStore = useAppsStore()
const { serverUrl } = useConfigStore()
const isDarkMode = ref(themeStore.currentTheme.isDark)
const slideContainer = ref<HTMLElement>()
const mediaUrls = ref<string[]>([])
const mediaBasePath = `${serverUrl}local/`
const dataSeparator = '\r?\n---\r?\n'
const dataSeparatorVertical = '\r?\n--\r?\n'
let reveal: Reveal.Api
defineProps({
Expand All @@ -49,7 +72,10 @@ watch(
}
)
onMounted(() => {
// LIFECYCLE HOOKS
onMounted(async () => {
await loadFolderForFileContext(unref(currentFileContext))
reveal = new Reveal({
plugins: [RevealMarkdown, RevealHighlight]
})
Expand All @@ -59,9 +85,82 @@ onMounted(() => {
progress: true,
history: true,
center: true,
controlsLayout: 'edges'
controlsLayout: 'edges',
markdown: {
baseUrl: mediaBasePath
}
})
reveal.on('ready', async () => {
if (unref(slideContainer) === undefined) {
return
}
const imgElements = (unref(slideContainer) as HTMLElement).getElementsByTagName('img')
const localImgElements = filterLocalImgElements(imgElements)
await updateImageUrls(localImgElements)
})
})
onBeforeUnmount(() => {
unref(mediaUrls).forEach((url) => {
revokeUrl(url)
})
})
// COMPUTED
const mediaMimeTypes = computed(() =>
getMediaMimeTypes(appsStore.externalAppConfig['preview']?.mimeTypes)
)
const mediaFiles = computed<Resource[]>(() => {
if (!unref(activeFiles)) {
return []
}
return unref(activeFiles).filter((file: Resource) => {
if (!file.mimeType) {
return false
}
return unref(mediaMimeTypes).includes(file.mimeType.toLowerCase())
})
})
// METHODS
function filterLocalImgElements(
imgElements: HTMLCollectionOf<HTMLImageElement>
): HTMLImageElement[] {
const localImgElements: HTMLImageElement[] = []
for (const el of imgElements) {
if (el.src.startsWith(mediaBasePath)) {
localImgElements.push(el)
}
}
return localImgElements
}
async function updateImageUrls(localImgElements: HTMLImageElement[]) {
for (const el of localImgElements) {
const src = el.src.split('/').pop() as string
const blobUrl = await parseImageUrl(src)
if (blobUrl) {
el.src = blobUrl
mediaUrls.value.push(blobUrl)
}
}
}
async function parseImageUrl(name: string) {
for (const file of unref(mediaFiles)) {
if (file.name === name) {
const url = await getUrlForResource(unref(currentFileContext).space, file)
// reload the active files
// TODO: implement caching
await loadFolderForFileContext(unref(currentFileContext))
return getBlobUrl(url)
}
}
}
async function getBlobUrl(url: string) {
const data = await fetch(url).then(async (res) => await res.blob())
const blob = new Blob([data], { type: data.type })
return URL.createObjectURL(blob)
}
</script>

<style lang="scss">
Expand All @@ -79,4 +178,31 @@ onMounted(() => {
}
}
}
#slideContainer {
section {
display: flex !important;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
> * {
margin-top: 0;
}
p:has(img) {
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
img {
width: auto;
height: auto;
}
}
}
}
</style>
16 changes: 16 additions & 0 deletions src/helpers/mediaMimeTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const getMediaMimeTypes = (extensionMimeTypes: string[] = []) => {
return [
'audio/flac',
'audio/mpeg',
'audio/ogg',
'audio/wav',
'audio/x-flac',
'audio/x-wav',
'image/gif',
'image/jpeg',
'image/png',
'video/mp4',
'video/webm',
...extensionMimeTypes
]
}

0 comments on commit c0a7a2b

Please sign in to comment.