Skip to content

Commit 22bc3b5

Browse files
committed
fixup! feat: viewer 6.0.0
Signed-off-by: skjnldsv <[email protected]>
1 parent fe41614 commit 22bc3b5

File tree

13 files changed

+444
-345
lines changed

13 files changed

+444
-345
lines changed

src/api_package/global.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ declare global {
1111
/**
1212
* Registered viewer handlers.
1313
*/
14-
_nc_viewer_handlers: Map<string, IHandler>
14+
_oca_viewer_handlers: Map<string, IHandler>
1515

1616
/**
1717
* The viewer global instance.
1818
*/
19-
_nc_viewer_service: Viewer
19+
_oca_viewer_service: Viewer
2020
}
2121
}

src/api_package/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ const topLevelViewerAction = new FileAction({
112112
export function registerHandler(handler: IHandler): void {
113113
validateHandler(handler)
114114

115-
window._nc_viewer_handlers ??= new Map<string, IHandler>()
116-
if (window._nc_viewer_handlers.has(handler.id)) {
115+
window._oca_viewer_handlers ??= new Map<string, IHandler>()
116+
if (window._oca_viewer_handlers.has(handler.id)) {
117117
console.warn(`Handler with id ${handler.id} is already registered.`)
118118
return
119119
}
120120

121-
window._nc_viewer_handlers.set(handler.id, handler)
121+
window._oca_viewer_handlers.set(handler.id, handler)
122122

123123
registerFileAction(new FileAction({
124124
id: `${ACTION_VIEWER}-${handler.id}`,
@@ -163,7 +163,7 @@ export function registerHandler(handler: IHandler): void {
163163
}
164164

165165
export function getHandlers() : Map<string, IHandler> {
166-
return window._nc_viewer_handlers ??= new Map<string, IHandler>()
166+
return window._oca_viewer_handlers ??= new Map<string, IHandler>()
167167
}
168168

169169
/**

src/api_package/tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"include": ["*.ts"],
3+
"exclude": ["node_modules", "dist"],
34
"compilerOptions": {
45
"lib": [
56
"DOM",
@@ -13,6 +14,7 @@
1314
"target": "ES2020",
1415
"allowImportingTsExtensions": true,
1516
"erasableSyntaxOnly": true,
16-
"rewriteRelativeImportExtensions": true
17+
"rewriteRelativeImportExtensions": true,
18+
"skipLibCheck": true,
1719
},
1820
}

src/api_package/viewer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ export class Viewer extends EventTarget implements ViewerAPI {
5555

5656
// Init and get the viewer in Modal
5757
export function getViewer(): Viewer {
58-
return window._nc_viewer_service ??= new Viewer()
58+
return window._oca_viewer_service ??= new Viewer()
5959
}
6060

6161
// Create a new Viewer instance in the given element
6262
export function createViewer(el: HTMLElement, file: File): Viewer {
6363
const instance = new Viewer()
64-
el.appendChild(instance)
64+
// el.appendChild(instance)
6565
return instance
6666
}

src/components/Images.vue

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,6 @@ export default {
252252
// Try to make sure that image position at stableX, stableY
253253
// in client coordinates stays in the same place on the screen.
254254
updateZoomAndShift(stableX, stableY, newZoomRatio) {
255-
if (!this.canZoom) {
256-
return
257-
}
258-
259255
// scrolling position relative to the image
260256
const element = this.$refs.image ?? this.$refs.video
261257
const scrollX = stableX - element.getBoundingClientRect().x - (this.width * this.zoomRatio / 2)
@@ -290,10 +286,6 @@ export default {
290286
* @return {void}
291287
*/
292288
updateZoom(event) {
293-
if (!this.canZoom) {
294-
return
295-
}
296-
297289
const isZoomIn = event.deltaY < 0
298290
const newZoomRatio = isZoomIn
299291
? Math.min(this.zoomRatio * 1.1, 5) // prevent too big zoom
@@ -359,10 +351,6 @@ export default {
359351
* @param {DragEvent} event the event
360352
*/
361353
pointerMove(event) {
362-
if (!this.canZoom) {
363-
return
364-
}
365-
366354
if (this.pointerCache.length > 0) {
367355
// Update pointer position in the pointer cache
368356
const index = this.pointerCache.findIndex(
@@ -405,10 +393,6 @@ export default {
405393
406394
},
407395
onDblclick() {
408-
if (!this.canZoom) {
409-
return
410-
}
411-
412396
if (this.zoomRatio > 1) {
413397
this.resetZoom()
414398
} else {

src/components/Videos copy.vue

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
<!--
2+
- SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
3+
- SPDX-License-Identifier: AGPL-3.0-or-later
4+
-->
5+
6+
<template>
7+
<!-- Plyr currently replaces the parent. Wrapping to prevent this
8+
https://github.com/redxtech/vue-plyr/issues/259 -->
9+
<div v-if="url">
10+
<VuePlyr ref="plyr"
11+
:options="options"
12+
:style="{
13+
height: height + 'px',
14+
width: width + 'px'
15+
}">
16+
<video ref="video"
17+
:autoplay="active ? true : null"
18+
:playsinline="true"
19+
:poster="livePhotoPath"
20+
:src="url"
21+
preload="metadata"
22+
@error.capture.prevent.stop.once="onFail"
23+
@ended="donePlaying"
24+
@canplay="doneLoading"
25+
@loadedmetadata="onLoadedMetadata">
26+
27+
<!-- Omitting `type` on purpose because most of the
28+
browsers auto detect the appropriate codec.
29+
Having it set force the browser to comply to
30+
the provided mime instead of detecting a potential
31+
compatibility. -->
32+
33+
{{ t('viewer', 'Your browser does not support videos.') }}
34+
</video>
35+
</VuePlyr>
36+
</div>
37+
</template>
38+
39+
<script lang='ts'>
40+
import '@skjnldsv/vue-plyr/dist/vue-plyr.css'
41+
import type { File } from '@nextcloud/files'
42+
43+
import { imagePath } from '@nextcloud/router'
44+
45+
import { logger } from '../services/logger.ts'
46+
import { findLivePhotoPeerFromName } from '../utils/livePhotoUtils'
47+
import { getPreviewIfAny } from '../utils/previewUtils'
48+
import { preloadMedia } from '../services/mediaPreloader.js'
49+
50+
const VuePlyr = () => import(/* webpackChunkName: 'plyr' */'@skjnldsv/vue-plyr')
51+
52+
const blankVideo = imagePath('viewer', 'blank.mp4')
53+
54+
export default {
55+
name: 'Videos',
56+
57+
components: {
58+
VuePlyr,
59+
},
60+
61+
props: {
62+
node: {
63+
type: Node,
64+
required: true,
65+
},
66+
nodes: {
67+
type: Array as () => File[],
68+
required: true,
69+
},
70+
isFullScreen: {
71+
type: Boolean,
72+
default: false,
73+
},
74+
isSidebarShown: {
75+
type: Boolean,
76+
default: false,
77+
},
78+
height: {
79+
type: Number,
80+
required: true,
81+
},
82+
width: {
83+
type: Number,
84+
required: true,
85+
},
86+
},
87+
data() {
88+
return {
89+
isFullscreenButtonVisible: false,
90+
fallback: false,
91+
}
92+
},
93+
94+
computed: {
95+
livePhotoPath() {
96+
const peerFile = findLivePhotoPeerFromName(this, this.nodes)
97+
98+
if (peerFile === undefined) {
99+
return undefined
100+
}
101+
102+
return getPreviewIfAny(peerFile)
103+
},
104+
player() {
105+
return this.$refs.plyr.player
106+
},
107+
options() {
108+
return {
109+
autoplay: true,
110+
// Used to reset the video streams https://github.com/sampotts/plyr#javascript-1
111+
blankVideo,
112+
controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'fullscreen'],
113+
loadSprite: false,
114+
fullscreen: {
115+
iosNative: true,
116+
},
117+
}
118+
},
119+
},
120+
121+
asyncComputed: {
122+
async url(): Promise<string> {
123+
if (this.fallback) {
124+
return preloadMedia(this.filename)
125+
} else {
126+
return this.src
127+
}
128+
},
129+
},
130+
131+
watch: {
132+
active(val, old) {
133+
// the item was hidden before and is now the current view
134+
if (val === true && old === false) {
135+
this.player.play()
136+
137+
// the item was playing before and is now hidden
138+
} else if (val === false && old === true) {
139+
this.player.pause()
140+
}
141+
},
142+
},
143+
144+
// for some reason the video controls don't get mounted to the dom until after the component (Videos) is mounted,
145+
// using the mounted() hook will leave us with an empty array
146+
updated() {
147+
// Prevent swiping to the next/previous item when scrubbing the timeline or changing volume
148+
const plyrControls = this.$el.querySelectorAll('.plyr__controls__item')
149+
if (!plyrControls || !plyrControls.length) {
150+
return
151+
}
152+
[...plyrControls].forEach(control => {
153+
if (control.getAttribute('data-plyr') === 'fullscreen') {
154+
control.addEventListener('click', this.hideHeaderAndFooter)
155+
}
156+
if (!control?.addEventListener) {
157+
return
158+
}
159+
control.addEventListener('mouseenter', this.disableSwipe)
160+
control.addEventListener('mouseleave', this.enableSwipe)
161+
})
162+
},
163+
164+
beforeDestroy() {
165+
// Force stop any ongoing request
166+
logger.debug('Closing video stream', { filename: this.filename })
167+
this.$refs.video?.pause?.()
168+
this.player.stop()
169+
this.player.destroy()
170+
},
171+
172+
methods: {
173+
hideHeaderAndFooter() {
174+
// work around to get the state of the fullscreen button, aria-selected attribute is not reliable
175+
this.isFullscreenButtonVisible = !this.isFullscreenButtonVisible
176+
if (this.isFullscreenButtonVisible) {
177+
document.body.querySelector('main').classList.add('viewer__hidden-fullscreen')
178+
document.body.querySelector('footer').classList.add('viewer__hidden-fullscreen')
179+
} else {
180+
document.body.querySelector('main').classList.remove('viewer__hidden-fullscreen')
181+
document.body.querySelector('footer').classList.remove('viewer__hidden-fullscreen')
182+
}
183+
},
184+
// Updates the dimensions of the modal
185+
updateVideoSize() {
186+
this.naturalHeight = this.$refs.video?.videoHeight
187+
this.naturalWidth = this.$refs.video?.videoWidth
188+
this.updateHeightWidth()
189+
},
190+
191+
donePlaying() {
192+
// reset and show poster after play
193+
this.$refs.video.autoplay = false
194+
this.$refs.video.load()
195+
},
196+
197+
onLoadedMetadata() {
198+
this.updateVideoSize()
199+
// Force any further loading once we have the metadata
200+
if (!this.active) {
201+
this.player.stop()
202+
}
203+
},
204+
205+
// Fallback to the original image if not already done
206+
onFail() {
207+
if (!this.fallback) {
208+
console.error(`Loading of file ${this.filename} failed, falling back to fetching it by hand`)
209+
this.fallback = true
210+
}
211+
},
212+
},
213+
}
214+
</script>
215+
216+
<style scoped lang="scss">
217+
video {
218+
/* over arrows in tiny screens */
219+
z-index: 20050;
220+
align-self: center;
221+
max-width: 100%;
222+
max-height: 100% !important;
223+
background-color: black;
224+
225+
justify-self: center;
226+
}
227+
228+
:deep() {
229+
.plyr:-webkit-full-screen video {
230+
width: 100% !important;
231+
height: 100% !important;
232+
}
233+
.plyr:fullscreen video {
234+
width: 100% !important;
235+
height: 100% !important;
236+
}
237+
.plyr__progress__container {
238+
flex: 1 1;
239+
}
240+
241+
.plyr {
242+
@import '../mixins/Plyr';
243+
244+
// Override server font style
245+
button {
246+
color: white;
247+
248+
&:hover,
249+
&:focus {
250+
color: var(--color-primary-element-text);
251+
background-color: var(--color-primary-element);
252+
}
253+
}
254+
}
255+
}
256+
</style>
257+
258+
<style lang="scss">
259+
main.viewer__hidden-fullscreen {
260+
height: 100vh !important;
261+
width: 100vw !important;
262+
margin: 0 !important;
263+
}
264+
265+
footer.viewer__hidden-fullscreen {
266+
display: none !important;
267+
}
268+
</style>

0 commit comments

Comments
 (0)