284 lines
6.9 KiB
Vue
284 lines
6.9 KiB
Vue
<template>
|
|
<div id="step-6">
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12 px-0 mx-0">
|
|
<div class="video-wrapper">
|
|
<!-- Poster -->
|
|
<img
|
|
v-if="!videoLoaded"
|
|
:src="posterPath"
|
|
class="background-poster"
|
|
alt="Poster"
|
|
>
|
|
<!-- Video -->
|
|
<transition name="fade">
|
|
<video
|
|
v-if="showVideo && videoPath"
|
|
:key="videoPath"
|
|
class="background-video"
|
|
:src="videoPath"
|
|
autoplay
|
|
muted
|
|
loop
|
|
playsinline
|
|
@loadedmetadata="handleFadeIn"
|
|
/>
|
|
</transition>
|
|
|
|
<div class="overlay-gradient" />
|
|
|
|
<div class="page-content">
|
|
<PageHeader
|
|
:title="currentTitle"
|
|
/>
|
|
<div v-if="showOverlay" class="overlay">
|
|
<button
|
|
class="btn btn-primary btn-lg"
|
|
style="background-color:#e9c046; border:none; color:black;"
|
|
@click="unlockAudio"
|
|
>
|
|
{{ t('Ready') }}
|
|
</button>
|
|
</div>
|
|
<div class="container-fluid">
|
|
<div class="row justify-content-center">
|
|
<NavigationBar
|
|
v-model:current-index="currentIndex"
|
|
:current-soundscape="currentTitle"
|
|
:adaptive="adaptive"
|
|
@update:soundscape="changeSoundscape"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script setup>
|
|
import { ref, watch, computed, nextTick, onMounted, onBeforeUnmount } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useRouter } from 'vue-router'
|
|
import { useAudioStore } from '@/stores/audio'
|
|
import { useMicStore } from '@/stores/microphone'
|
|
import { useUserStore } from '@/stores/user'
|
|
import { getSoundcapeList } from '~/tracks.config.ts'
|
|
import PageHeader from '@/components/PageHeader.vue'
|
|
import NavigationBar from '@/components/NavigationBar.vue'
|
|
// import VideoBackground from '@/components/VideoBackground.vue'
|
|
definePageMeta({
|
|
middleware: 'auth'
|
|
})
|
|
|
|
const { t } = useI18n()
|
|
const router = useRouter()
|
|
const audioStore = useAudioStore()
|
|
const userStore = useUserStore()
|
|
const { $axios } = useNuxtApp()
|
|
|
|
// States
|
|
const isReady = ref(false)
|
|
const showOverlay = ref(false)
|
|
|
|
const soundscapes = getSoundcapeList()
|
|
const defaultSoundscape = 'Lagoon'
|
|
|
|
const currentIndex = userStore.user?.settings?.soundscape
|
|
? ref(soundscapes.indexOf(userStore.user.settings.soundscape))
|
|
: ref(0)
|
|
|
|
// Soundscape aus Store oder Fallback
|
|
const adaptive = userStore.user?.settings?.adaptive_sound_scape || 'no'
|
|
|
|
// Computed
|
|
const currentTitle = computed(() => userStore.user?.settings?.soundscape || defaultSoundscape)
|
|
const videoPath = computed(() => `/video/${currentTitle.value}.mp4`)
|
|
const posterPath = computed(() => `/images/posters/${currentTitle.value}.jpg`)
|
|
|
|
const showVideo = ref(true)
|
|
const videoLoaded = ref(false)
|
|
|
|
// Watcher für AudioContext-Zustand
|
|
watch(() => audioStore.audioContext?.state, (newState) => {
|
|
showOverlay.value = newState === 'suspended'
|
|
})
|
|
|
|
// Wechsel Video bei Klangänderung
|
|
watch(currentIndex, async () => {
|
|
videoLoaded.value = false
|
|
showVideo.value = false
|
|
await nextTick()
|
|
showVideo.value = true
|
|
})
|
|
|
|
// Methoden
|
|
function changeSoundscape (newSoundscape) {
|
|
const map = {
|
|
Lagoon: 0,
|
|
Tropics: 1,
|
|
Forest: 2,
|
|
Meadow: 3
|
|
}
|
|
currentIndex.value = map[newSoundscape.soundscape] ?? 0
|
|
unmuteActive()
|
|
|
|
// Speichern der Einstellungen im Backend
|
|
$axios.post('/api/update-setting', newSoundscape).then((response) => {
|
|
if (soundscapes.includes(newSoundscape.soundscape)) { userStore.user.settings.soundscape = newSoundscape.soundscape }
|
|
if (response.status !== 200) {
|
|
// wenn der request fehlgeschlagen hat aus welchen gründen auch immer,
|
|
// z.B. keine Internetverbindung, wird ein Interval angeschmissen, dass alle 30 Sekunden probiert die settings zu aktualisieren
|
|
// bis es am Ende mal klappt
|
|
} else {
|
|
// reponse is not 200
|
|
|
|
}
|
|
}).catch((e) => {
|
|
useNuxtApp().$toast.error('save: something went wrong while saving...')
|
|
})
|
|
}
|
|
|
|
function unmuteActive () {
|
|
audioStore.setPlaying(false)
|
|
setTimeout(() => {
|
|
audioStore.setPlaying(true)
|
|
}, 50)
|
|
}
|
|
|
|
async function unlockAudio () {
|
|
await audioStore.ensureAudioContextRunning()
|
|
}
|
|
|
|
function handleFadeIn (e) {
|
|
e.target.style.opacity = 1
|
|
videoLoaded.value = true
|
|
}
|
|
|
|
async function fetchSettings () {
|
|
try {
|
|
const { data } = await useNuxtApp().$axios.post('/api/fetch-settings')
|
|
if (data.success) {
|
|
userStore.settings = data.setting
|
|
const soundscape = data.setting.soundscape || 'Lagoon'
|
|
const index = soundscapes.indexOf(soundscape)
|
|
|
|
currentIndex.value = index >= 0 ? index : 0 // jetzt erst setzen
|
|
// audioStore.changeTrack(soundscape)
|
|
}
|
|
} catch (error) {
|
|
useNuxtApp().$logger.error('Nothing fetched from the backend, please reload and try again.')
|
|
}
|
|
}
|
|
|
|
// Lifecycle
|
|
onMounted(async () => {
|
|
showOverlay.value = true
|
|
await useNuxtApp().$audioPrepared
|
|
showOverlay.value = false
|
|
|
|
await fetchSettings()
|
|
|
|
isReady.value = true
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
useAudioStore().stopAudioContextMonitor()
|
|
useMicStore().detachMicrophone()
|
|
})
|
|
</script>
|
|
|
|
<style>
|
|
|
|
.video-wrapper {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.background-poster,
|
|
.background-video {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
z-index: 0;
|
|
}
|
|
|
|
.background-video {
|
|
opacity: 0;
|
|
transition: opacity 1s ease-in-out;
|
|
}
|
|
|
|
.page-content {
|
|
position: relative;
|
|
z-index: 1;
|
|
height: 100%;
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.fade-enter-active, .fade-leave-active {
|
|
transition: opacity 1.5s ease-in-out;
|
|
}
|
|
.fade-enter-from, .fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
.adaptive{
|
|
position: fixed;
|
|
bottom: 0;
|
|
text-align: center;
|
|
width: 100%;
|
|
}
|
|
.rnboplayer{
|
|
position:sticky;
|
|
width: 225px;
|
|
height: inherit;
|
|
display:flex;
|
|
float: bottom;
|
|
}
|
|
.overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
color: white;
|
|
font-size: 24px;
|
|
z-index: 1000;
|
|
}
|
|
.overlay button {
|
|
padding: 10px 20px;
|
|
font-size: 18px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.bg-gradient {
|
|
position: relative;
|
|
|
|
&::after {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
right: 0;
|
|
background: rgb(153,166,188);
|
|
background: linear-gradient(0deg, rgba(153,166,188,0) 0%, rgba(153,166,188,0.413624824929972) 55%, rgba(153,166,188,1) 100%);
|
|
height: 40vh;
|
|
}
|
|
}
|
|
</style>
|