Skip to content

Commit

Permalink
[MS] FileControlsFlux component
Browse files Browse the repository at this point in the history
  • Loading branch information
Ironicbay committed Dec 31, 2024
1 parent 2d78b4b commit af336bb
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 34 deletions.
6 changes: 3 additions & 3 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"axios": "^1.7.4",
"file-type": "^19.6.0",
"luxon": "^3.4.4",
"megashark-lib": "git+https://github.com/Scille/megashark-lib.git#a0b53d6226ff01b8651f1fec1a08161578c2cc9a",
"megashark-lib": "git+https://github.com/Scille/megashark-lib.git#dfeafca42b3612b815a7d1508cad8ac42cdd4b47",
"mammoth": "^1.8.0",
"monaco-editor": "^0.52.0",
"pdfjs-dist": "^4.8.69",
Expand Down
59 changes: 59 additions & 0 deletions client/src/components/viewers/controls/FileControlsFlux.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!-- Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS -->
<template>
<div class="progress">
<ms-slider
class="progress-slider"
v-model="sliderState"
@change="updateSliderState"
:max-value="length"
:increment-value="500"
/>
{{ `${(sliderState.progress / 100).toFixed(0)} / ${(length / 100).toFixed(0)}` }}
</div>
</template>

<script setup lang="ts">
import { MsSlider, SliderState } from 'megashark-lib';
import { onUnmounted, ref, watch } from 'vue';

const props = defineProps<{
modelValue: SliderState;
length: number;
}>();

const emits = defineEmits<{
(event: 'update:modelValue', state: SliderState): void;
}>();

const sliderState = ref<SliderState>({ progress: 0, paused: true });

// Update the slider as the media is playing
const cancelProgressWatch = watch(
() => props.modelValue.progress,
() => {
sliderState.value.progress = props.modelValue.progress;
},
);
const cancelPausedWatch = watch(
() => props.modelValue.paused,
() => {
console.log(props.modelValue.paused);
sliderState.value.paused = props.modelValue.paused;
},
);

onUnmounted(() => {
cancelProgressWatch();
cancelPausedWatch();
});

function updateSliderState(value: SliderState): void {
emits('update:modelValue', value);
}
</script>

<style scoped lang="scss">
.progress {
width: 15rem;
}
</style>
11 changes: 10 additions & 1 deletion client/src/components/viewers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@

import FileControls from '@/components/viewers/controls/FileControls.vue';
import FileControlsButton from '@/components/viewers/controls/FileControlsButton.vue';
import FileControlsFlux from '@/components/viewers/controls/FileControlsFlux.vue';
import FileControlsGroup from '@/components/viewers/controls/FileControlsGroup.vue';
import FileControlsInput from '@/components/viewers/controls/FileControlsInput.vue';
import FileControlsPagination from '@/components/viewers/controls/FileControlsPagination.vue';
import FileControlsZoom from '@/components/viewers/controls/FileControlsZoom.vue';

export { FileControls, FileControlsButton, FileControlsGroup, FileControlsInput, FileControlsPagination, FileControlsZoom };
export {
FileControls,
FileControlsButton,
FileControlsFlux,
FileControlsGroup,
FileControlsInput,
FileControlsPagination,
FileControlsZoom,
};
78 changes: 55 additions & 23 deletions client/src/views/viewers/AudioViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,35 @@
@ended="updateMediaData"
/>
</template>
<!-- Disabled till we add an illustration in the viewer -->
<!-- <template #controls>
<template #controls>
<file-controls-flux
v-model="progress"
:length="length"
/>
<!-- Disabled till we add an illustration in the viewer -->
<file-controls>
<file-controls-button
:class="{'flip-horizontal-ion-icon': ended}"
:class="{ 'flip-horizontal-ion-icon': ended }"
:icon="getPlaybackIcon()"
@click="togglePlayback"
/>
<file-controls-button
<!-- <file-controls-button
:icon="getVolumeIcon()"
@click="toggleVolume"
/>
/> -->
</file-controls>
</template> -->
</template>
</file-viewer-wrapper>
</template>

<script setup lang="ts">
// import { refresh, play, pause, volumeHigh, volumeLow, volumeMedium, volumeMute } from 'ionicons/icons';
import { onMounted, ref } from 'vue';
import { onMounted, onUnmounted, ref, watch } from 'vue';
import { FileViewerWrapper } from '@/views/viewers';
import { FileContentInfo } from '@/views/viewers/utils';
import { FileControls, FileControlsButton, FileControlsFlux } from '@/components/viewers';
import { SliderState } from 'megashark-lib';
import { refresh, play, pause } from 'ionicons/icons';

const props = defineProps<{
contentInfo: FileContentInfo;
Expand All @@ -47,25 +54,49 @@ const props = defineProps<{

const src = ref('');
const audioElement = ref();
const paused = ref(true);
const length = ref(0);
const volume = ref(1);
const ended = ref(false);
const progress = ref<SliderState>({ progress: 0, paused: true });

const cancelProgressWatch = watch(
() => progress.value,
() => {
if (audioElement.value) {
audioElement.value.currentTime = progress.value.progress / 100;
if (audioElement.value.paused !== progress.value.paused) {
togglePlayback();
}
}
},
);

onMounted(async () => {
src.value = URL.createObjectURL(new Blob([props.contentInfo.data], { type: props.contentInfo.mimeType }));
setInterval(() => {
if (!progress.value.paused) {
progress.value.progress = audioElement.value?.currentTime * 100;
}
}, 30);
});

// function togglePlayback(): void {
// audioElement.value.paused ? audioElement.value.play() : audioElement.value.pause();
// }
onUnmounted(() => {
cancelProgressWatch();
});

function togglePlayback(): void {
audioElement.value.paused ? audioElement.value.play() : audioElement.value.pause();
}

// function toggleVolume(): void {
// audioElement.value.volume = VOLUME_LEVELS[(VOLUME_LEVELS.indexOf(audioElement.value.volume) + 1) % VOLUME_LEVELS.length];
// }

function updateMediaData(event: Event): void {
volume.value = (event.target as HTMLAudioElement).volume;
paused.value = (event.target as HTMLAudioElement).paused;
length.value = (event.target as HTMLVideoElement).duration * 100;
progress.value.progress = (event.target as HTMLVideoElement).currentTime * 100;
progress.value.paused = (event.target as HTMLVideoElement).paused;
ended.value = (event.target as HTMLAudioElement).ended;
}

Expand All @@ -84,17 +115,18 @@ function updateMediaData(event: Event): void {
// }
// }

// function getPlaybackIcon(): string {
// if (ended.value) {
// return refresh;
// }
// switch (paused.value) {
// case true:
// return play;
// case false:
// return pause;
// }
// }
function getPlaybackIcon(): string {
if (ended.value) {
return refresh;
}
switch (progress.value.paused) {
case true:
return play;
case false:
return pause;
}
return play;
}
</script>

<style scoped lang="scss"></style>
41 changes: 36 additions & 5 deletions client/src/views/viewers/VideoViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
</video>
</template>
<template #controls>
<file-controls-flux
v-model="progress"
:length="length"
/>
<file-controls>
<file-controls-button
:class="{ 'flip-horizontal-ion-icon': ended }"
Expand All @@ -42,10 +46,11 @@

<script setup lang="ts">
import { refresh, play, pause, volumeHigh, volumeLow, volumeMedium, volumeMute, scan } from 'ionicons/icons';
import { onMounted, ref } from 'vue';
import { onMounted, onUnmounted, ref, watch } from 'vue';
import { FileContentInfo } from '@/views/viewers/utils';
import { FileControls, FileControlsButton } from '@/components/viewers';
import { FileControls, FileControlsButton, FileControlsFlux } from '@/components/viewers';
import { FileViewerWrapper } from '@/views/viewers';
import { SliderState } from 'megashark-lib';

const props = defineProps<{
contentInfo: FileContentInfo;
Expand All @@ -55,12 +60,35 @@ const VOLUME_LEVELS = [0, 0.25, 0.5, 1];

const src = ref('');
const videoElement = ref();
const paused = ref(true);
const length = ref(0);
const volume = ref(1);
const ended = ref(false);
const progress = ref<SliderState>({ progress: 0, paused: true });

const cancelProgressWatch = watch(
() => progress.value,
() => {
if (videoElement.value) {
videoElement.value.currentTime = progress.value.progress / 100;
if (videoElement.value.paused !== progress.value.paused) {
togglePlayback();
}
}
},
);

onMounted(async () => {
src.value = URL.createObjectURL(new Blob([props.contentInfo.data], { type: props.contentInfo.mimeType }));

setInterval(() => {
if (!progress.value.paused) {
progress.value.progress = videoElement.value?.currentTime * 100;
}
}, 30);
});

onUnmounted(() => {
cancelProgressWatch();
});

function togglePlayback(): void {
Expand All @@ -77,8 +105,10 @@ function toggleFullScreen(): void {

function updateMediaData(event: Event): void {
volume.value = (event.target as HTMLVideoElement).volume;
paused.value = (event.target as HTMLVideoElement).paused;
ended.value = (event.target as HTMLVideoElement).ended;
length.value = (event.target as HTMLVideoElement).duration * 100;
progress.value.progress = (event.target as HTMLVideoElement).currentTime * 100;
progress.value.paused = (event.target as HTMLVideoElement).paused;
}

function getVolumeIcon(): string {
Expand All @@ -100,12 +130,13 @@ function getPlaybackIcon(): string {
if (ended.value) {
return refresh;
}
switch (paused.value) {
switch (progress.value.paused) {
case true:
return play;
case false:
return pause;
}
return play;
}
</script>

Expand Down
15 changes: 14 additions & 1 deletion client/tests/e2e/specs/file_viewers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,22 @@ msTest('Audio viewer', async ({ documents }) => {
await expect(documents.locator('.file-viewer').locator('.file-viewer-topbar').locator('ion-text')).toHaveText(/^File_[a-z0-9_]+\.mp3$/);

const bottomBar = documents.locator('.file-viewer-bottombar');
await expect(bottomBar).toBeEmpty();
const buttons = bottomBar.locator('.file-controls-button');
const wrapper = documents.locator('.file-viewer-wrapper');
const audio = wrapper.locator('audio');
const fluxBar = bottomBar.locator('.slider');

await expectMedia(audio).toHaveDuration(7.967347);
await expectMedia(audio).toHaveCurrentTime(0.0);
await expectMedia(audio).toHaveVolume(1);

await buttons.nth(0).click();
await documents.waitForTimeout(500);
await buttons.nth(0).click();
expect(await Media.getCurrentTime(audio)).toBeGreaterThan(0.1);

await fluxBar.click();
await expectMedia(audio).toHaveCurrentTime(3.96);
});

msTest('Video viewer', async ({ documents }) => {
Expand All @@ -142,6 +151,7 @@ msTest('Video viewer', async ({ documents }) => {
const buttons = bottomBar.locator('.file-controls-button');
const wrapper = documents.locator('.file-viewer-wrapper');
const video = wrapper.locator('video');
const fluxBar = bottomBar.locator('.slider');

await expect(buttons).toHaveCount(3);

Expand Down Expand Up @@ -170,6 +180,9 @@ msTest('Video viewer', async ({ documents }) => {

await buttons.nth(1).click();
await expectMedia(video).toHaveVolume(0);

await fluxBar.click();
await expectMedia(video).toHaveCurrentTime(1.77);
});

msTest('Text viewer', async ({ documents }) => {
Expand Down

0 comments on commit af336bb

Please sign in to comment.