Skip to content

Commit

Permalink
fix(viewer): add loading and error handling with improved UI
Browse files Browse the repository at this point in the history
Signed-off-by: Grigorii K. Shartsev <[email protected]>
  • Loading branch information
ShGKme committed Sep 3, 2024
1 parent 51a7dd4 commit 9fe95fa
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 36 deletions.
91 changes: 91 additions & 0 deletions src/talk/renderer/Viewer/ViewerHandlerBase.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!--
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup>
import { computed } from 'vue'
import { t } from '@nextcloud/l10n'
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import IconAlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'
const props = defineProps({
loading: {
type: Boolean,
required: false,
default: false,
},
error: {
type: [String, Boolean],
required: false,
default: false,
},
/**
* In lazy mode the content is rendered visually hidden during loading.
* Used when loading happens on rendering, for example, loading <img>.
*/
lazy: {
type: Boolean,
required: false,
},
})
const readyToShow = computed(() => !props.loading && !props.error)
</script>

<template>
<div class="viewer-wrapper">
<NcEmptyContent v-if="loading" :name="t('talk_desktop', 'Loading …')" class="delayed-appear">
<template #icon>
<NcLoadingIcon />
</template>
</NcEmptyContent>

<NcEmptyContent v-else-if="error"
:name="t('talk_desktop', 'Could not load the file')"
:description="typeof error === 'string' ? error : undefined">
<template #icon>
<IconAlertCircleOutline />
</template>
</NcEmptyContent>

<div v-if="lazy || readyToShow" class="viewer-wrapper__content" :class="{ 'hidden-visually': lazy && !readyToShow }">
<slot />
</div>
</div>
</template>

<style scoped>
.viewer-wrapper {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.viewer-wrapper__content {
width: 100%;
height: 100%;
}
.viewer-wrapper__content > :deep(*) {
width: 100%;
height: 100%;
}
@keyframes delayed-appear {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.delayed-appear {
opacity: 0;
animation: delayed-appear 0s forwards;
animation-delay: var(--animation-slow);
}
</style>
28 changes: 10 additions & 18 deletions src/talk/renderer/Viewer/ViewerHandlerImages.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<script setup>
import { computed } from 'vue'
import { generateFilePreviewUrl } from './viewer.utils.ts'
import ViewerHandlerMedia from './ViewerHandlerMedia.vue'
const props = defineProps({
file: {
Expand All @@ -18,22 +19,13 @@ const src = computed(() => generateFilePreviewUrl(props.file.fileid, props.file.
</script>

<template>
<div class="media-wrapper">
<img :src="src" :alt="file.basename">
</div>
<ViewerHandlerMedia v-slot="{ mediaClass, handleLoadEnd }">
<img :key="src"
class="viewer-image"
:class="mediaClass"
:src="src"
:alt="file.basename"
@load="handleLoadEnd(false)"
@error="handleLoadEnd(true)">
</ViewerHandlerMedia>
</template>

<style scoped>
.media-wrapper {
display: flex;
height: 100%;
width: 100%;
justify-content: center;
align-items: center;
> * {
max-width: 100%;
max-height: 100%;
}
}
</style>
33 changes: 33 additions & 0 deletions src/talk/renderer/Viewer/ViewerHandlerMedia.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!--
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup>
import { ref } from 'vue'
import ViewerHandlerBase from './ViewerHandlerBase.vue'
const loading = ref(true)
const error = ref(false)
/**
* Handle the end of the loading process
* @param {boolean | string} withError - Whether the loading process ended with an error
*/
function handleLoadEnd(withError = false) {
loading.value = false
error.value = withError
}
</script>

<template>
<ViewerHandlerBase :loading="loading" :error="error" lazy>
<slot :handle-load-end="handleLoadEnd" media-class="viewer-media" />
</ViewerHandlerBase>
</template>

<style scoped>
:deep(.viewer-media) {
object-fit: scale-down;
}
</style>
27 changes: 9 additions & 18 deletions src/talk/renderer/Viewer/ViewerHandlerVideos.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<script setup>
import { computed } from 'vue'
import { generateUserFileDavUrl } from './viewer.utils.ts'
import ViewerHandlerMedia from './ViewerHandlerMedia.vue'
const props = defineProps({
file: {
Expand All @@ -18,22 +19,12 @@ const src = computed(() => generateUserFileDavUrl(props.file.filename))
</script>

<template>
<div class="media-wrapper">
<video :src="src" controls />
</div>
<ViewerHandlerMedia v-slot="{ mediaClass, handleLoadEnd }">
<video class="viewer-video"
:class="mediaClass"
:src="src"
controls
@canplay="handleLoadEnd(false)"
@error="handleLoadEnd(true)" />
</ViewerHandlerMedia>
</template>

<style scoped>
.media-wrapper {
display: flex;
height: 100%;
width: 100%;
justify-content: center;
align-items: center;
> * {
max-width: 100%;
max-height: 100%;
}
}
</style>

0 comments on commit 9fe95fa

Please sign in to comment.