Initial commit
This commit is contained in:
283
pages/index.vue
Normal file
283
pages/index.vue
Normal file
@@ -0,0 +1,283 @@
|
||||
<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>
|
Reference in New Issue
Block a user