Files
chad/client/app/layouts/default.vue

217 lines
5.4 KiB
Vue

<template>
<div class="grid grid-cols-[360px_1fr] gap-2 p-2 h-screen grid-rows-[auto_1fr] max-w-full">
<div
class="flex items-center justify-between gap-2 rounded-xl p-3 bg-surface-950"
>
<div class="inline-flex items-center gap-3">
<PrimeBadge class="opacity-50" severity="secondary" :value="version" size="small" />
<PrimeBadge :severity="connected ? 'success' : 'danger' " />
</div>
<PrimeButtonGroup class="ml-auto">
<PrimeButton :severity="inputMuted ? 'info' : undefined" outlined @click="toggleInput">
<template #icon>
<Component :is="inputMuted ? MicOff : Mic" />
</template>
</PrimeButton>
<PrimeButton :severity="outputMuted ? 'info' : undefined" outlined @click="toggleOutput">
<template #icon>
<Component :is="outputMuted ? VolumeOff : Volume2" />
</template>
</PrimeButton>
</PrimeButtonGroup>
<PrimeButton :severity="videoEnabled ? 'success' : undefined" outlined @click="toggleVideo">
<template #icon>
<Component :is="videoEnabled ? CameraOff : Camera" />
</template>
</PrimeButton>
<PrimeButton :severity="sharingEnabled ? 'success' : undefined" outlined @click="toggleShare">
<template #icon>
<Component :is="sharingEnabled ? ScreenShareOff : ScreenShare" />
</template>
</PrimeButton>
</div>
<div
class="flex items-center justify-center rounded-xl p-3 bg-surface-950"
>
<PrimeSelectButton
v-model="activeTab"
:options="tabs"
option-label="id"
:allow-empty="false"
style="--p-togglebutton-content-padding: 0.25rem 0.5rem"
>
<template #option="{ option }">
<Component :is="option.icon" size="24" />
</template>
</PrimeSelectButton>
</div>
<PrimeScrollPanel class="bg-surface-900 rounded-xl overflow-hidden" style="min-height: 0">
<div v-auto-animate class="p-3 space-y-1">
<template v-for="channel in channels" :key="channel.id">
<PrimeDivider>
<PrimeButton size="small" variant="text" @click="joinChannel(channel)">
{{ channel.name }}
</PrimeButton>
</PrimeDivider>
<ClientRow v-for="client in clients.filter(_client => _client.channelId === channel.id)" :key="client.socketId" :client="client">
{{ client.userId }}
</ClientRow>
</template>
<!-- <ClientRow v-for="client of clients" :key="client.userId" :client="client" /> -->
</div>
</PrimeScrollPanel>
<div class="bg-surface-900 rounded-xl overflow-hidden p-3 flex flex-col min-h-full">
<dl>
<dt>Socket ID</dt>
<dd>{{ socket?.id }}</dd>
<br>
<dt>Producers</dt>
<dd v-for="producer in producersArray" :key="producer.id">
{{ producer.id }}
{{ producer.appData }}
</dd>
<br>
<dl>Consumers</dl>
<dd v-for="consumer in consumersArray" :key="consumer.id">
{{ consumer.id }}
{{ consumer.appData }}
</dd>
</dl>
<slot />
</div>
</div>
<FullscreenGallery />
</template>
<script setup lang="ts">
import chadApi from '#shared/chad-api'
import {
Camera,
CameraOff,
MessageCircle,
Mic,
MicOff,
ScreenShare,
ScreenShareOff,
Settings,
TvMinimalPlay,
UserPen,
Volume2,
VolumeOff,
} from 'lucide-vue-next'
const channels = shallowRef<any[]>([])
;(async () => {
channels.value = await chadApi<any[]>('/channels', { method: 'GET' })
})()
const { me } = useClients()
const {
version,
clients,
inputMuted,
outputMuted,
videoEnabled,
sharingEnabled,
somebodyStreamingVideo,
toggleInput,
toggleOutput,
toggleVideo,
toggleShare,
} = useApp()
const { connect, connected, socket } = useSignaling()
const { consumersArray, producersArray } = useMediasoup()
interface Tab {
id: string
icon: Component
onClick: () => void | Promise<void>
}
const route = useRoute()
const tabs = computed<Tab[]>(() => {
const result = []
if (somebodyStreamingVideo.value) {
result.push({
id: 'Gallery',
icon: TvMinimalPlay,
onClick: () => {
navigateTo({ name: 'Gallery' })
},
})
}
result.push(
{
id: 'Index',
icon: MessageCircle,
onClick: () => {
navigateTo({ name: 'Index' })
},
},
{
id: 'Profile',
icon: UserPen,
onClick: () => {
navigateTo({ name: 'Profile' })
},
},
{
id: 'Preferences',
icon: Settings,
onClick: () => {
navigateTo({ name: 'Preferences' })
},
},
)
return result
})
const activeTab = ref<Tab>(tabs.value.find(tab => tab.id === route.name) ?? tabs.value.find(tab => tab.id === 'Index')!)
watch(activeTab, (activeTab) => {
activeTab.onClick()
})
connect()
async function joinChannel(channel) {
socket.value?.emit('join-channel', { channelId: channel.id })
}
watch(socket, (socket) => {
if (!socket)
return
socket.on('channel-removed', (channelId) => {
const idx = channels.value.findIndex(channel => channel.id === channelId)
if (idx === -1)
return
channels.value.splice(idx, 1)
triggerRef(channels)
})
socket.on('channel-created', (channel) => {
channels.value.push(channel)
triggerRef(channels)
})
}, { immediate: true })
</script>