130 lines
3.9 KiB
Vue
130 lines
3.9 KiB
Vue
<template>
|
|
<div
|
|
class="overflow-hidden rounded-xl transition-[background-color]"
|
|
:class="{
|
|
'hover:bg-surface-800 cursor-pointer': !isMe,
|
|
'bg-surface-800': expanded,
|
|
}"
|
|
>
|
|
<div class="p-3 flex items-center gap-3" @click="toggleExpand">
|
|
<PrimeAvatar size="small">
|
|
<template #icon>
|
|
<User :size="20" />
|
|
</template>
|
|
</PrimeAvatar>
|
|
|
|
<div class="flex-1 overflow-hidden text-sm leading-5 font-medium text-color whitespace-nowrap overflow-ellipsis">
|
|
{{ client.displayName || client.username }}
|
|
</div>
|
|
|
|
<div class="flex align-center gap-1">
|
|
<PrimeBadge v-if="!!shareConsumer" v-tooltip.top="'Watch'" severity="success" value="Streaming" size="small" @click.stop="watchStream" />
|
|
|
|
<PrimeBadge v-if="premuted" severity="danger" value="Muted" size="small" />
|
|
<PrimeBadge v-else-if="client.outputMuted" severity="info" value="No sound" size="small" />
|
|
<PrimeBadge v-else-if="inputMuted" severity="info" value="Muted" size="small" />
|
|
|
|
<PrimeBadge v-if="isMe" severity="secondary" value="You" size="small" class="shrink-0" />
|
|
</div>
|
|
|
|
<Component :is="expanded ? ChevronUp : ChevronDown" v-if="!isMe" :size="20" />
|
|
</div>
|
|
|
|
<CollapseTransition v-if="!isMe">
|
|
<div v-if="expanded">
|
|
<div class="px-3 pb-3">
|
|
<div class="flex justify-between text-sm mb-3">
|
|
<span>Volume</span>
|
|
<span>{{ volume }}</span>
|
|
</div>
|
|
<PrimeSlider v-model="volume" :min="0" :max="1000" :step="volume < 200 ? 5 : 25" />
|
|
|
|
<div class="mt-3 flex gap-1 justify-end">
|
|
<PrimeButton size="small" variant="text" @click="premuted = !premuted">
|
|
{{ premuted ? 'Unmute' : 'Mute' }}
|
|
</PrimeButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CollapseTransition>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { ChadClient } from '#shared/types'
|
|
import { useLocalStorage } from '@vueuse/core'
|
|
import { ChevronDown, ChevronUp, User } from 'lucide-vue-next'
|
|
import CollapseTransition from '~/components/CollapseTransition.vue'
|
|
|
|
const props = defineProps<{
|
|
client: ChadClient
|
|
}>()
|
|
|
|
const { outputMuted } = useApp()
|
|
const { consumers: allConsumers, micProducer } = useMediasoup()
|
|
const { me } = useClients()
|
|
const { show } = useFullscreenVideo()
|
|
|
|
const expanded = ref(false)
|
|
|
|
const volume = useLocalStorage<number>(computed(() => `CLIENT_VOLUME_${props.client.userId}`), 100, { writeDefaults: false })
|
|
const premuted = useLocalStorage<boolean>(computed(() => `CLIENT_PREMUTED_${props.client.userId}`), false, { writeDefaults: false })
|
|
|
|
const isMe = computed(() => {
|
|
return me.value && props.client.userId === me.value.userId
|
|
})
|
|
|
|
const consumers = computed(() => {
|
|
return allConsumers.value.values().filter(consumer => consumer.appData.socketId === props.client.socketId).toArray()
|
|
})
|
|
|
|
const audioConsumer = computed(() => {
|
|
return consumers.value.find(consumer => consumer.track.kind === 'audio')
|
|
})
|
|
|
|
const videoConsumers = computed(() => {
|
|
return consumers.value.filter(consumer => consumer.track.kind === 'video')
|
|
})
|
|
|
|
const shareConsumer = computed(() => {
|
|
return videoConsumers.value.find(consumer => consumer.appData.source === 'share')
|
|
})
|
|
|
|
const audioTrack = computed(() => {
|
|
return audioConsumer.value?.track
|
|
})
|
|
|
|
const audioConsumerPaused = ref(false)
|
|
|
|
const inputMuted = computed(() => {
|
|
if (isMe.value)
|
|
return micProducer.value?.paused ?? false
|
|
|
|
return premuted.value || audioConsumerPaused.value
|
|
})
|
|
|
|
watch(allConsumers, () => {
|
|
audioConsumerPaused.value = audioConsumer.value?.paused ?? false
|
|
})
|
|
|
|
const { setGain } = useAudioContext(audioTrack)
|
|
|
|
watchEffect(() => {
|
|
setGain((outputMuted.value || premuted.value) ? 0 : (volume.value * 0.01))
|
|
})
|
|
|
|
function toggleExpand() {
|
|
if (isMe.value)
|
|
return
|
|
|
|
expanded.value = !expanded.value
|
|
}
|
|
|
|
function watchStream() {
|
|
if (!shareConsumer.value)
|
|
return
|
|
|
|
show(new MediaStream([shareConsumer.value.track]))
|
|
}
|
|
</script>
|