380 lines
12 KiB
Vue
380 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12 px-0 mx-0">
|
|
<video-background
|
|
src="/video/bg-video.mp4"
|
|
style=" height: 100vh;"
|
|
poster="/images/poster.png"
|
|
>
|
|
<div class="container-fluid pt-3 px-lg-4">
|
|
<div class="header d-flex flex-column flex-fill flex-md-row justify-content-between">
|
|
<div class="header__logo pt-md-1 pt-lg-1">
|
|
<nuxt-link class="navbar-brand" to="/">
|
|
<div class="text-center text-md-start">
|
|
<img src="/mindboostlogo.svg" height="35" class="img " alt="imae">
|
|
</div>
|
|
</nuxt-link>
|
|
</div>
|
|
</div>
|
|
<div class="content">
|
|
<div class="row text-center">
|
|
<div class="col-12">
|
|
<h1 v-if="true" class="h3 fw-bold mb-4">
|
|
{{ t("The microphone does not provide any level") }}
|
|
</h1>
|
|
<h1 v-else class="h3 fw-bold mb-4">
|
|
{{ t("Nothing to worry, the microphone is working.") }}
|
|
</h1>
|
|
<p class="text-muted">
|
|
{{ t("Please check the input level of the microphone in the audio") }}
|
|
</p>
|
|
</div>
|
|
<div class="row justify-content-center pt-4">
|
|
<div class="col-11">
|
|
<form>
|
|
<div class="row justify-content-center mb-4">
|
|
<div class="col-md-5 text-center">
|
|
<label for="inputSelect" class="fw-semibold">{{ t('Input device:') }}</label>
|
|
<p class="pt-0 mt-0 text-muted pb-2 mb-0" style="font-size: 14px;font-weight: 500">
|
|
({{ t('select laptop or mobile device microphone') }})
|
|
</p>
|
|
<select id="inputSelect" v-model="selectedInput" class="form-select pt-1 mt-0 select-box" @change="handleInputChange">
|
|
<option v-for="device in audioInputDevices" :key="device.value" :value="device.value">
|
|
{{ device.label }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class=" pt-3 text-center">
|
|
<div class="progress-container">
|
|
<div class="checkmark col-md-6 pt-2 d-block d-sm-inline-block d-inline-block">
|
|
<p class="fw-semibold">{{ t("Microphone Indicator") }}</p>
|
|
<div class="col-12 text-center d-flex justify-content-center col-lg-12 pb-5">
|
|
<AudioReactiveBar ref="AudioReactiveBar" />
|
|
</div>
|
|
<div class="col-12 text-center pt-1">
|
|
<a href="/" class="btn text-white px-4 fs-5 fw-bolder py-2" type="button" style="background-color: rgb(233, 192, 70);">{{ t('Next') }}</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</video-background>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import { mapActions, mapState } from 'pinia'
|
|
import { useCounterStore } from '@/stores/counter'
|
|
import { useMicStore } from '@/stores/microphone'
|
|
import { useUserStore } from '@/stores/user'
|
|
|
|
export default {
|
|
name: 'FixProblem',
|
|
setup () {
|
|
const { t } = useI18n()
|
|
const localePath = useLocalePath()
|
|
const AudioReactiveBar = ref(null)
|
|
const micStore = useMicStore()
|
|
|
|
return {
|
|
micStore,
|
|
t,
|
|
localePath,
|
|
AudioReactiveBar
|
|
|
|
}
|
|
},
|
|
data () {
|
|
return {
|
|
bar_val: 100,
|
|
audioOutputDevices: [],
|
|
selectedInput: null,
|
|
selectedOutput: null,
|
|
stream: null,
|
|
micReady: false,
|
|
detectedInput: false
|
|
|
|
}
|
|
},
|
|
computed: {
|
|
...mapState(useCounterStore, ['count']),
|
|
// ...mapState(useMicStore, ['microphones'])
|
|
audioInputDevices () {
|
|
return this.micStore.availableDevices.map(device => ({
|
|
label: device.label || `Microphone ${device.deviceId}`,
|
|
value: device.deviceId
|
|
}))
|
|
}
|
|
},
|
|
created () {
|
|
this.$watch(
|
|
() => this.audioInputDevices,
|
|
(newDevices) => {
|
|
if (newDevices.length > 0 && this.selectedInput === null) {
|
|
this.selectedInput = newDevices[0].value
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
},
|
|
fillInputList () {
|
|
try {
|
|
this.getUserMedia()
|
|
.then(() => {
|
|
return navigator.mediaDevices.enumerateDevices()
|
|
})
|
|
.then((devices) => {
|
|
// useNuxtApp().$logger.log('DEVICES', { devices })
|
|
// Filtere die Geräte nach Typ
|
|
this.audioInputDevices = devices.filter(device => device.kind === 'audioinput')
|
|
this.audioOutputDevices = devices.filter(device => device.kind === 'audiooutput')
|
|
|
|
// Setze die Standardwerte, falls keine Geräte verfügbar sind
|
|
this.selectedDevice = this.audioInputDevices.length > 0 ? this.audioInputDevices[0].deviceId : null
|
|
|
|
// Überprüfe, ob `audioInputDevice` und `audioOutputDevice` existieren
|
|
if (this.audioInputDevice && this.audioInputDevice.deviceId) {
|
|
this.selectedInput = this.audioInputDevices.findIndex(item => item.deviceId === this.audioInputDevice.deviceId)
|
|
} else {
|
|
this.selectedInput = -1 // Kein passendes Gerät gefunden
|
|
}
|
|
|
|
if (this.audioOutputDevice && this.audioOutputDevice.deviceId) {
|
|
this.selectedOutput = this.audioOutputDevices.findIndex(item => item.deviceId === this.audioOutputDevice.deviceId)
|
|
} else {
|
|
this.selectedOutput = -1 // Kein passendes Gerät gefunden
|
|
}
|
|
|
|
// Standardauswahl, falls nichts gefunden wird
|
|
if (this.selectedInput < 0) { this.selectedInput = 0 }
|
|
if (this.selectedOutput < 0) { this.selectedOutput = 0 }
|
|
|
|
// Warnung anzeigen, wenn Geräte gleich sind
|
|
if (this.checkIfSameDevice()) {
|
|
this.$toast.warning(this.t('bluetoothWarning'), {
|
|
duration: 6000,
|
|
pauseOnHover: true,
|
|
dismissible: true,
|
|
queue: true,
|
|
position: 'bottom-left'
|
|
})
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
// Fehler bei der Geräteabfrage behandeln
|
|
this.$toast.error(`Error enumerating media devices: ${error.message}`)
|
|
useNuxtApp().$logger.error('Error during device enumeration:', error)
|
|
})
|
|
} catch (error) {
|
|
// Allgemeine Fehlerbehandlung
|
|
this.$toast.error('Unexpected error occurred.')
|
|
useNuxtApp().$logger.error('Unexpected error:', error)
|
|
}
|
|
},
|
|
async mounted () {
|
|
this.increment(75)
|
|
await this.micStore.updateAvailableDevices()
|
|
},
|
|
beforeUnmount () {
|
|
this.detachMicrophone()
|
|
},
|
|
methods: {
|
|
...mapActions(useCounterStore, ['increment']),
|
|
...mapActions(useMicStore, ['getMicrophone', 'getMediaStream', 'switchMicrophone', 'detachMicrophone']),
|
|
...mapActions(useUserStore, ['saveInputdevice', 'saveOutputDevice']),
|
|
checkMicrophoneReadiness (mic) {
|
|
if (mic.microphoneNode instanceof MediaStreamAudioSourceNode &&
|
|
mic.microphoneStream instanceof MediaStream) { this.micReady = true }
|
|
this.stream = mic.microphoneStream
|
|
useNuxtApp().$logger.log(this.micReady)
|
|
},
|
|
normalizeDeviceName (deviceName) {
|
|
return deviceName
|
|
.replace('Standard - ', '') // Remove 'Standard - '
|
|
.replace(/ \(.*\)$/, '') // Remove any content in parentheses
|
|
.trim()
|
|
},
|
|
async check (event) {
|
|
useNuxtApp().$logger.log({ event })
|
|
const inputDeviceId = this.audioInputDevices[(event.target).value].deviceId
|
|
useNuxtApp().$logger.log('INPUT DEVICE', { inputDeviceId })
|
|
await this.switchMicrophone(inputDeviceId)
|
|
this.checkIfSameDevice()
|
|
},
|
|
checkInputLevel () {
|
|
const audioreactivebar = AudioReactiveBar.value
|
|
useNuxtApp().$logger.log({ audioreactivebar })
|
|
this.detectedInput = true
|
|
},
|
|
getDeviceLabel (outputDevice) {
|
|
if (!outputDevice || !outputDevice.label) {
|
|
return 'Unknown Device' // Fallback-Label
|
|
}
|
|
return outputDevice.label
|
|
},
|
|
checkIfSameDevice () {
|
|
const outputDevice = this.audioOutputDevices[this.selectedOutput] || this.audioOutputDevices[this.index]
|
|
const inputDevice = this.audioInputDevices[this.selectedInput]
|
|
const inputLabel = this.getDeviceLabel(inputDevice)
|
|
const outputLabel = this.getDeviceLabel(outputDevice)
|
|
|
|
if (!inputLabel || !outputLabel) {
|
|
throw new Error('Device labels are not selected or not available')
|
|
}
|
|
const normalizedInput = this.normalizeDeviceName(inputLabel)
|
|
const normalizedOutput = this.normalizeDeviceName(outputLabel)
|
|
// useNuxtApp().$logger.log(`Normalized Input: ${normalizedInput}, Normalized Output: ${normalizedOutput}`)
|
|
return normalizedInput === normalizedOutput
|
|
},
|
|
saveDevices () {
|
|
this.saveInputdevice(this.audioInputDevices[this.selectedInput])
|
|
this.saveOutputDevice(this.audioOutputDevices[this.selectedOutput])
|
|
this.$router.push(this.localePath('/onboarding'))
|
|
},
|
|
getUserMedia () {
|
|
const constraints = {
|
|
audio: { deviceId: this.selectedDevice ? { exact: this.selectedDevice } : undefined }
|
|
}
|
|
try {
|
|
return navigator.mediaDevices.getUserMedia(constraints)
|
|
} catch (error) {
|
|
this.$toast.error('Error accessing media devices: ', error)
|
|
}
|
|
},
|
|
async handleInputChange () {
|
|
await this.switchMicrophone(this.selectedInput)
|
|
this.checkIfSameDevice()
|
|
if (this.$refs.AudioReactiveBar) {
|
|
this.$refs.AudioReactiveBar.updateMicrophone(this.selectedInput)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.progress-container {
|
|
display: flex;
|
|
flex-direction: column; /* Elemente werden vertikal angeordnet */
|
|
align-items: center; /* Zentriert die Progressbar und den Button horizontal */
|
|
}
|
|
|
|
.checklabel:hover{
|
|
border-color: #e9c046;
|
|
}
|
|
.checklabel {
|
|
color: #e9c046 !important;
|
|
border: 1px solid #e9c046;
|
|
}
|
|
.checkmark .checklabel svg path{
|
|
/*background-color: white;*/
|
|
fill: #e9c046;
|
|
}
|
|
.checkmark input:checked ~ .checklabel {
|
|
background-color: #e9c046;
|
|
border: 1px solid #e9c046;
|
|
color: #f4f5f7 !important;
|
|
}
|
|
.checkmark input:checked ~ .checklabel svg path {
|
|
fill: white;
|
|
}
|
|
.checkmark input:hover ~ .checklabel {
|
|
background-color: #e9c046;
|
|
border: 1px solid #e9c046;
|
|
color: #f4f5f7 !important;
|
|
}
|
|
.checkmark input:hover ~ .checklabel svg path {
|
|
fill: white;
|
|
}
|
|
|
|
.tooltip {
|
|
position: relative;
|
|
display: inline-block;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.tooltip .tooltiptext {
|
|
visibility: hidden;
|
|
width: 200px;
|
|
background-color: #333;
|
|
color: #fff;
|
|
text-align: center;
|
|
border-radius: 4px;
|
|
padding: 5px;
|
|
position: absolute;
|
|
z-index: 1;
|
|
bottom: 125%; /* Position above the text */
|
|
left: 50%;
|
|
margin-left: -100px;
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
}
|
|
|
|
.tooltip:hover .tooltiptext {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
}
|
|
.bar{
|
|
background-color: #e9c046 !important;
|
|
}
|
|
.checklabel{
|
|
background-color: white !important;
|
|
width: 150px ;
|
|
height: 134px ;
|
|
}
|
|
.px-4{
|
|
transition: 1s;
|
|
}
|
|
|
|
.content {
|
|
background-color: rgba(255, 255, 255, 0.6);
|
|
border-radius: 10px;
|
|
padding: 4.5em;
|
|
max-width: 1120px;
|
|
margin: 10% auto;
|
|
min-height: 50vh;
|
|
max-height: 75vh;
|
|
overflow-y: scroll;
|
|
}
|
|
|
|
@media only screen and (max-width: 768px) {
|
|
.content {
|
|
padding: 4.5em 3em;
|
|
}
|
|
}
|
|
|
|
@media only screen and (max-width: 576px) {
|
|
.content {
|
|
padding: 2em 1em;
|
|
margin: 2.5em auto;
|
|
overflow-y: scroll;
|
|
max-height: 75vh;
|
|
}
|
|
}
|
|
|
|
.nav-buttons {
|
|
position: fixed;
|
|
bottom: 0px;
|
|
left: 0;
|
|
right: 0;
|
|
background: rgb(255,255,255);
|
|
background: linear-gradient(0deg, rgba(255,255,255,1) 20%, rgba(255,255,255,0) 100%);
|
|
}
|
|
.checkmark {
|
|
background-color: transparent;
|
|
border: none;
|
|
}
|
|
</style>
|