diff --git a/client/.yarn/install-state.gz b/client/.yarn/install-state.gz
index 219de17..3fcd296 100644
Binary files a/client/.yarn/install-state.gz and b/client/.yarn/install-state.gz differ
diff --git a/client/app/app.vue b/client/app/app.vue
index 0fce194..b804a20 100644
--- a/client/app/app.vue
+++ b/client/app/app.vue
@@ -5,6 +5,3 @@
-
-
diff --git a/client/app/components/ClientRow.vue b/client/app/components/ClientRow.vue
index 61dba62..6f0992b 100644
--- a/client/app/components/ClientRow.vue
+++ b/client/app/components/ClientRow.vue
@@ -28,7 +28,7 @@
Volume
{{ volume }}
-
+
diff --git a/client/app/composables/use-app.ts b/client/app/composables/use-app.ts
index a062db4..79c20f5 100644
--- a/client/app/composables/use-app.ts
+++ b/client/app/composables/use-app.ts
@@ -1,12 +1,17 @@
import { createGlobalState } from '@vueuse/core'
+import { useClients } from '~/composables/use-clients'
export const useApp = createGlobalState(() => {
+ const { clients } = useClients()
const mediasoup = useMediasoup()
+ const toast = useToast()
const inputMuted = ref(false)
const outputMuted = ref(false)
- const me = computed(() => mediasoup.clients.value.find(client => client.isMe))
+ const previousInputMuted = ref(inputMuted.value)
+
+ const me = computed(() => clients.value.find(client => client.isMe))
function muteInput() {
inputMuted.value = true
@@ -38,15 +43,36 @@ export const useApp = createGlobalState(() => {
muteOutput()
}
- watch(inputMuted, async (state) => {
- if (state)
+ watch(inputMuted, async (inputMuted) => {
+ if (inputMuted) {
await mediasoup.muteMic()
- else
+ }
+ else {
+ if (outputMuted.value) {
+ outputMuted.value = false
+ }
await mediasoup.unmuteMic()
+ }
+
+ const toastText = inputMuted ? 'Microphone muted' : 'Microphone activated'
+ toast.add({ severity: 'info', summary: toastText, closable: false, life: 1000 })
+ })
+
+ watch(outputMuted, (outputMuted) => {
+ if (outputMuted) {
+ previousInputMuted.value = inputMuted.value
+ muteInput()
+ }
+ else {
+ inputMuted.value = previousInputMuted.value
+ }
+
+ const toastText = outputMuted ? 'Sound muted' : 'Sound resumed'
+ toast.add({ severity: 'info', summary: toastText, closable: false, life: 1000 })
})
return {
- clients: mediasoup.clients,
+ clients,
me,
inputMuted,
muteInput,
diff --git a/client/app/composables/use-clients.ts b/client/app/composables/use-clients.ts
new file mode 100644
index 0000000..dd685ec
--- /dev/null
+++ b/client/app/composables/use-clients.ts
@@ -0,0 +1,58 @@
+import type { ChadClient, UpdatedClient } from '#shared/types'
+import { createGlobalState } from '@vueuse/core'
+
+export const useClients = createGlobalState(() => {
+ const signaling = useSignaling()
+ const toast = useToast()
+
+ const clients = shallowRef([])
+
+ watch(signaling.socket, (socket) => {
+ if (!socket)
+ return
+
+ socket.on('clientChanged', (clientId: ChadClient['id'], updatedClient: UpdatedClient) => {
+ const client = getClient(clientId)
+ updateClient(clientId, updatedClient)
+
+ if (updatedClient.username)
+ toast.add({ severity: 'info', summary: `${client?.username} is now ${updatedClient.username}`, closable: false, life: 1000 })
+ })
+ })
+
+ function getClient(clientId: ChadClient['id']) {
+ return clients.value.find(client => client.id === clientId)
+ }
+
+ function addClient(...client: ChadClient[]) {
+ clients.value.push(...client)
+
+ triggerRef(clients)
+ }
+
+ function removeClient(clientId: ChadClient['id']) {
+ clients.value = clients.value.filter(client => client.id !== clientId)
+ }
+
+ function updateClient(clientId: ChadClient['id'], updatedClient: UpdatedClient) {
+ const clientIdx = clients.value.findIndex(client => client.id === clientId)
+
+ if (clientIdx === -1)
+ return
+
+ clients.value[clientIdx] = {
+ ...clients.value[clientIdx],
+ ...updatedClient,
+ } as ChadClient
+
+ triggerRef(clients)
+ }
+
+ return {
+ clients,
+ getClient,
+ addClient,
+ removeClient,
+ updateClient,
+ }
+})
diff --git a/client/app/composables/use-mediasoup.ts b/client/app/composables/use-mediasoup.ts
index 9bf658c..deb80a8 100644
--- a/client/app/composables/use-mediasoup.ts
+++ b/client/app/composables/use-mediasoup.ts
@@ -18,8 +18,11 @@ const ICE_SERVERS: RTCIceServer[] = [
]
export const useMediasoup = createSharedComposable(() => {
- const preferences = usePreferences()
+ const toast = useToast()
+
const signaling = useSignaling()
+ const { addClient, removeClient } = useClients()
+ const preferences = usePreferences()
const device = shallowRef()
const rtpCapabilities = shallowRef()
@@ -30,8 +33,6 @@ export const useMediasoup = createSharedComposable(() => {
const webcamProducer = shallowRef()
const shareProducer = shallowRef()
- const clients = shallowRef([])
-
const consumers = shallowRef