108 lines
3.1 KiB
Vue
108 lines
3.1 KiB
Vue
<template>
|
|
<div class="py-3">
|
|
<div class="flex items-center gap-3">
|
|
<PrimeAvatar size="small">
|
|
<template #icon>
|
|
<User :size="20" />
|
|
</template>
|
|
</PrimeAvatar>
|
|
|
|
<div class="flex-1 overflow-hidden">
|
|
<div class="overflow-hidden text-sm leading-5 font-medium text-color whitespace-nowrap overflow-ellipsis">
|
|
{{ client.displayName }}
|
|
</div>
|
|
<div v-if="client.username !== client.displayName" class="overflow-hidden mt-1 text-xs leading-5 text-muted-color">
|
|
{{ client.username }}
|
|
</div>
|
|
</div>
|
|
|
|
<PrimeBadge v-if="client.outputMuted" severity="info" value="No sound" />
|
|
<PrimeBadge v-else-if="inputMuted" severity="info" value="Muted" />
|
|
<PrimeBadge v-if="isMe" severity="secondary" value="You" />
|
|
|
|
<template v-if="!isMe">
|
|
<PrimeButton icon="pi pi-ellipsis-h" text size="small" severity="contrast" @click="menuRef?.toggle" />
|
|
|
|
<PrimeMenu ref="menu" popup :model="menuItems" style="translate: calc(-100% + 2rem) 0.5rem">
|
|
<template #start>
|
|
<div class="px-4 py-3">
|
|
<div class="flex justify-between">
|
|
<span>Volume</span>
|
|
<span>{{ volume }}</span>
|
|
</div>
|
|
<PrimeSlider v-model="volume" class="mt-4" :min="0" :max="1000" :step="volume < 200 ? 5 : 25" />
|
|
</div>
|
|
</template>
|
|
</PrimeMenu>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { ChadClient } from '#shared/types'
|
|
import type { MenuItem } from 'primevue/menuitem'
|
|
import { useLocalStorage } from '@vueuse/core'
|
|
import { User } from 'lucide-vue-next'
|
|
|
|
const props = defineProps<{
|
|
client: ChadClient
|
|
}>()
|
|
|
|
const { outputMuted } = useApp()
|
|
const { getClientConsumers, micProducer } = useMediasoup()
|
|
const { me } = useClients()
|
|
|
|
const volume = useLocalStorage<number>(computed(() => `CLIENT_VOLUME_${props.client.userId}`), 100, { writeDefaults: false })
|
|
|
|
const menuRef = useTemplateRef<HTMLAudioElement>('menu')
|
|
|
|
const menuItems: MenuItem[] = [
|
|
// {
|
|
// label: 'Mute',
|
|
// icon: 'pi pi-headphones',
|
|
// },
|
|
// {
|
|
// label: 'DM',
|
|
// icon: 'pi pi-comment',
|
|
// disabled: true,
|
|
// },
|
|
]
|
|
|
|
const isMe = computed(() => {
|
|
return me.value && props.client.userId === me.value.userId
|
|
})
|
|
|
|
const audioConsumer = computed(() => {
|
|
if (isMe.value)
|
|
return undefined
|
|
|
|
const consumers = getClientConsumers(props.client.socketId)
|
|
|
|
return consumers.find(consumer => consumer.track.kind === 'audio')
|
|
})
|
|
|
|
const inputMuted = computed(() => {
|
|
if (isMe.value)
|
|
return micProducer.value?.paused ?? false
|
|
|
|
const consumers = getClientConsumers(props.client.socketId)
|
|
|
|
return consumers.find(consumer => consumer.track.kind === 'audio')?.paused
|
|
})
|
|
|
|
const audioTrack = computed(() => {
|
|
return audioConsumer.value?.track
|
|
})
|
|
|
|
const { setGain } = useAudioContext(audioTrack)
|
|
|
|
watch(volume, (volume) => {
|
|
setGain(outputMuted.value ? 0 : (volume * 0.01))
|
|
}, { immediate: true })
|
|
|
|
watch(outputMuted, (outputMuted) => {
|
|
setGain(outputMuted ? 0 : (volume.value * 0.01))
|
|
})
|
|
</script>
|