192 lines
5.8 KiB
Vue
192 lines
5.8 KiB
Vue
<template>
|
|
<div>
|
|
<div class="row text-center">
|
|
<div class="col-12">
|
|
<h1 class="h3 fw-bold mb-4">
|
|
{{ t("How is your audio hardware connected?") }}
|
|
</h1>
|
|
<p class="text-muted mx-auto">
|
|
{{ t('Onboarding-input') }}<br>
|
|
{{ t('Onboarding-output') }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="row justify-content-center pt-4">
|
|
<div class="col-11">
|
|
<form v-if="!loading">
|
|
<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="checkIfSameDevice">
|
|
<option v-for="(item,index) in audioInputDevices" :key="index" :value="index">
|
|
{{ item.label || `Eingabegerät ${index + 1}` }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row justify-content-center mb-4">
|
|
<div class="col-md-5 text-center">
|
|
<label for="outputSelect" class="fw-semibold">{{ t('Output device:') }}</label>
|
|
<p class="pt-0 mt-0 text-muted pb-0 mb-0" style="font-size: 14px;font-weight: 500">
|
|
{{ t('select headphones or headphone output') }}
|
|
</p>
|
|
<select id="outputSelect" v-model="selectedOutput" class="form-select pt-1 mt-0 select-box" @change="checkIfSameDevice">
|
|
<option v-for="(item,index) in audioOutputDevices" :key="index" :value="index">
|
|
{{ item.label || `Ausgabegerät ${index + 1}` }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row justify-content-center pt-4">
|
|
<div class="col-md-3 text-center" style="z-index: 1000000;">
|
|
<button
|
|
type="button"
|
|
class="btn col-4 btn-primary-custom"
|
|
:disabled="audioInputDevices.length === 0 || audioOutputDevices.length === 0"
|
|
@click.prevent="saveDevices"
|
|
>
|
|
{{ t("Next") }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<div v-else class="text-center">
|
|
<p>Lade Geräte...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import { mapState, mapActions } from 'pinia'
|
|
import { useUserStore } from '~/stores/user'
|
|
|
|
export default {
|
|
emits: ['next-step'],
|
|
setup () {
|
|
const { t } = useI18n()
|
|
const localePath = useLocalePath()
|
|
|
|
return {
|
|
t,
|
|
localePath
|
|
}
|
|
},
|
|
data () {
|
|
return {
|
|
loading: true,
|
|
audioInputDevices: [],
|
|
audioOutputDevices: [],
|
|
selectedInput: null,
|
|
selectedOutput: null,
|
|
stream: null
|
|
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
...mapState(useUserStore, ['audioInputDevice', 'audioOutputDevice'])
|
|
|
|
},
|
|
async created () {
|
|
try {
|
|
// Zuerst Media-Zugriff einholen (sonst keine Labels)
|
|
await this.getUserMedia()
|
|
|
|
const devices = await navigator.mediaDevices.enumerateDevices()
|
|
|
|
this.audioInputDevices = devices.filter(d => d.kind === 'audioinput')
|
|
this.audioOutputDevices = devices.filter(d => d.kind === 'audiooutput')
|
|
|
|
// Setze Indexe basierend auf gespeicherten IDs
|
|
this.selectedInput = this.audioInputDevices.findIndex(d => d.deviceId === this.audioInputDevice.deviceId)
|
|
this.selectedOutput = this.audioOutputDevices.findIndex(d => d.deviceId === this.audioOutputDevice.deviceId)
|
|
|
|
// Fallback auf erstes Gerät
|
|
if (this.selectedInput < 0) { this.selectedInput = 0 }
|
|
if (this.selectedOutput < 0) { this.selectedOutput = 0 }
|
|
|
|
// Warnung, wenn gleiche Geräte gewählt sind
|
|
if (this.checkIfSameDevice()) {
|
|
this.$toast.warning(this.t('bluetoothWarning'), {
|
|
duration: 6000,
|
|
pauseOnHover: true,
|
|
dismissible: true,
|
|
queue: true,
|
|
position: 'bottom-left'
|
|
})
|
|
}
|
|
} catch (error) {
|
|
this.$toast.error(this.t('Could not access audio devices.'))
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
methods: {
|
|
...mapActions(useUserStore, ['saveInputdevice', 'saveOutputDevice']),
|
|
|
|
normalizeDeviceName (deviceName) {
|
|
return deviceName
|
|
.replace('Standard - ', '') // Remove 'Standard - '
|
|
.replace(/ \(.*\)$/, '') // Remove any content in parentheses
|
|
.trim()
|
|
},
|
|
checkIfSameDevice () {
|
|
const input = this.audioInputDevices[this.selectedInput]
|
|
const output = this.audioOutputDevices[this.selectedOutput]
|
|
|
|
if (!input?.label || !output?.label) { return false }
|
|
|
|
return this.normalizeDeviceName(input.label) === this.normalizeDeviceName(output.label)
|
|
},
|
|
saveDevices () {
|
|
this.saveInputdevice(this.audioInputDevices[this.selectedInput])
|
|
this.saveOutputDevice(this.audioOutputDevices[this.selectedOutput])
|
|
this.$emit('next-step')
|
|
},
|
|
async getUserMedia () {
|
|
try {
|
|
return await navigator.mediaDevices.getUserMedia({ audio: true })
|
|
} catch (error) {
|
|
this.$logger('keine Mikrofon-Berechtigung erteilt.')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.bar{
|
|
background-color: #e9c046;
|
|
}
|
|
|
|
.select-box{
|
|
border: 2px solid #e9c046;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.select-box:focus{
|
|
border: 2px solid #e9c046;
|
|
box-shadow: none;
|
|
|
|
}
|
|
|
|
.btn-primary-custom {
|
|
background-color: #e9c046;
|
|
border-color: #e9c046;
|
|
color: white;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.btn-primary-custom:hover, .btn-primary-custom:focus{
|
|
background-color: transparent;
|
|
border-color: #e9c046;
|
|
color: #e9c046;
|
|
}
|
|
</style>
|