This commit is contained in:
parent
fc43c8df96
commit
e2064dba6c
Binary file not shown.
@ -5,6 +5,3 @@
|
|||||||
|
|
||||||
<PrimeToast position="bottom-center" />
|
<PrimeToast position="bottom-center" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
</script>
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
<span>Volume</span>
|
<span>Volume</span>
|
||||||
<span>{{ volume }}</span>
|
<span>{{ volume }}</span>
|
||||||
</div>
|
</div>
|
||||||
<PrimeSlider v-model="volume" class="mt-4" :min="0" :max="1000" :step="5" />
|
<PrimeSlider v-model="volume" class="mt-4" :min="0" :max="1000" :step="volume < 200 ? 5 : 25" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</PrimeMenu>
|
</PrimeMenu>
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
import { createGlobalState } from '@vueuse/core'
|
import { createGlobalState } from '@vueuse/core'
|
||||||
|
import { useClients } from '~/composables/use-clients'
|
||||||
|
|
||||||
export const useApp = createGlobalState(() => {
|
export const useApp = createGlobalState(() => {
|
||||||
|
const { clients } = useClients()
|
||||||
const mediasoup = useMediasoup()
|
const mediasoup = useMediasoup()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const inputMuted = ref(false)
|
const inputMuted = ref(false)
|
||||||
const outputMuted = 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() {
|
function muteInput() {
|
||||||
inputMuted.value = true
|
inputMuted.value = true
|
||||||
@ -38,15 +43,36 @@ export const useApp = createGlobalState(() => {
|
|||||||
muteOutput()
|
muteOutput()
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(inputMuted, async (state) => {
|
watch(inputMuted, async (inputMuted) => {
|
||||||
if (state)
|
if (inputMuted) {
|
||||||
await mediasoup.muteMic()
|
await mediasoup.muteMic()
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
|
if (outputMuted.value) {
|
||||||
|
outputMuted.value = false
|
||||||
|
}
|
||||||
await mediasoup.unmuteMic()
|
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 {
|
return {
|
||||||
clients: mediasoup.clients,
|
clients,
|
||||||
me,
|
me,
|
||||||
inputMuted,
|
inputMuted,
|
||||||
muteInput,
|
muteInput,
|
||||||
|
|||||||
58
client/app/composables/use-clients.ts
Normal file
58
client/app/composables/use-clients.ts
Normal file
@ -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<ChadClient[]>([])
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -18,8 +18,11 @@ const ICE_SERVERS: RTCIceServer[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export const useMediasoup = createSharedComposable(() => {
|
export const useMediasoup = createSharedComposable(() => {
|
||||||
const preferences = usePreferences()
|
const toast = useToast()
|
||||||
|
|
||||||
const signaling = useSignaling()
|
const signaling = useSignaling()
|
||||||
|
const { addClient, removeClient } = useClients()
|
||||||
|
const preferences = usePreferences()
|
||||||
|
|
||||||
const device = shallowRef<mediasoupClient.Device>()
|
const device = shallowRef<mediasoupClient.Device>()
|
||||||
const rtpCapabilities = shallowRef<mediasoupClient.types.RtpCapabilities>()
|
const rtpCapabilities = shallowRef<mediasoupClient.types.RtpCapabilities>()
|
||||||
@ -30,8 +33,6 @@ export const useMediasoup = createSharedComposable(() => {
|
|||||||
const webcamProducer = shallowRef<mediasoupClient.types.Producer>()
|
const webcamProducer = shallowRef<mediasoupClient.types.Producer>()
|
||||||
const shareProducer = shallowRef<mediasoupClient.types.Producer>()
|
const shareProducer = shallowRef<mediasoupClient.types.Producer>()
|
||||||
|
|
||||||
const clients = shallowRef<ChadClient[]>([])
|
|
||||||
|
|
||||||
const consumers = shallowRef<Map<string, mediasoupClient.types.Consumer>>(new Map())
|
const consumers = shallowRef<Map<string, mediasoupClient.types.Consumer>>(new Map())
|
||||||
const producers = shallowRef<Map<string, mediasoupClient.types.Producer>>(new Map())
|
const producers = shallowRef<Map<string, mediasoupClient.types.Producer>>(new Map())
|
||||||
|
|
||||||
@ -121,21 +122,24 @@ export const useMediasoup = createSharedComposable(() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
clients.value = (await signaling.socket.value.emitWithAck('join', {
|
const joinedClients = (await signaling.socket.value.emitWithAck('join', {
|
||||||
username: preferences.username.value,
|
username: preferences.username.value,
|
||||||
rtpCapabilities: rtpCapabilities.value,
|
rtpCapabilities: rtpCapabilities.value,
|
||||||
})).map(transformClient)
|
})).map(transformClient)
|
||||||
|
|
||||||
|
addClient(...joinedClients)
|
||||||
|
|
||||||
|
toast.add({ severity: 'success', summary: 'Joined', closable: false, life: 1000 })
|
||||||
|
|
||||||
await enableMic()
|
await enableMic()
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('newPeer', (client) => {
|
socket.on('newPeer', (client) => {
|
||||||
clients.value.push(transformClient(client))
|
addClient(transformClient(client))
|
||||||
triggerRef(clients)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('peerClosed', (id) => {
|
socket.on('peerClosed', (id) => {
|
||||||
clients.value = clients.value.filter(client => client.id !== id)
|
removeClient(id)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on(
|
socket.on(
|
||||||
@ -317,7 +321,6 @@ export const useMediasoup = createSharedComposable(() => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
init,
|
init,
|
||||||
clients,
|
|
||||||
consumers,
|
consumers,
|
||||||
producers,
|
producers,
|
||||||
sendTransport,
|
sendTransport,
|
||||||
|
|||||||
@ -3,6 +3,8 @@ import { createSharedComposable } from '@vueuse/core'
|
|||||||
import { io } from 'socket.io-client'
|
import { io } from 'socket.io-client'
|
||||||
|
|
||||||
export const useSignaling = createSharedComposable(() => {
|
export const useSignaling = createSharedComposable(() => {
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const socket = shallowRef<Socket>()
|
const socket = shallowRef<Socket>()
|
||||||
|
|
||||||
const connected = ref(false)
|
const connected = ref(false)
|
||||||
@ -35,6 +37,13 @@ export const useSignaling = createSharedComposable(() => {
|
|||||||
})
|
})
|
||||||
}, { immediate: true, flush: 'sync' })
|
}, { immediate: true, flush: 'sync' })
|
||||||
|
|
||||||
|
watch(connected, (connected) => {
|
||||||
|
if (connected)
|
||||||
|
toast.add({ severity: 'success', summary: 'Connected', closable: false, life: 1000 })
|
||||||
|
else
|
||||||
|
toast.add({ severity: 'error', summary: 'Disconnected', closable: false, life: 1000 })
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
onScopeDispose(() => {
|
onScopeDispose(() => {
|
||||||
socket.value?.close()
|
socket.value?.close()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-auto-animate class="grid grid-cols-2 h-screen">
|
<div v-auto-animate class="grid grid-cols-2 h-screen">
|
||||||
<div class="flex flex-col shadow-xl shadow-surface-950 overflow-y-hidden z-10">
|
<div class="flex flex-col shadow-xl shadow-surface-950 overflow-y-hidden">
|
||||||
<AppHeader title="Шальные сиськи 18+">
|
<AppHeader title="Шальные сиськи 18+">
|
||||||
<template #right>
|
<template #right>
|
||||||
<PrimeButtonGroup class="ml-auto">
|
<PrimeButtonGroup class="ml-auto">
|
||||||
|
|||||||
@ -18,14 +18,19 @@ definePageMeta({
|
|||||||
name: 'Preferences',
|
name: 'Preferences',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const signaling = useSignaling()
|
||||||
const { username } = usePreferences()
|
const { username } = usePreferences()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
const localUsername = ref(username.value)
|
const localUsername = ref(username.value)
|
||||||
|
|
||||||
function save() {
|
async function save() {
|
||||||
username.value = localUsername.value
|
if (localUsername.value && localUsername.value !== username.value) {
|
||||||
|
username.value = localUsername.value
|
||||||
|
|
||||||
toast.add({ severity: 'success', summary: 'Saved', life: 3000 })
|
await signaling.socket.value?.emitWithAck('updateClient', { username })
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.add({ severity: 'success', summary: 'Saved', life: 1000, closable: false })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import tailwindcss from '@tailwindcss/vite'
|
|||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2025-09-29',
|
compatibilityDate: '2025-09-29',
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true, vueDevTools: true },
|
||||||
ssr: false,
|
ssr: false,
|
||||||
modules: [
|
modules: [
|
||||||
'@nuxt/fonts',
|
'@nuxt/fonts',
|
||||||
|
|||||||
@ -8,3 +8,5 @@ export interface ChadClient extends RemoteClient {
|
|||||||
inputMuted?: boolean
|
inputMuted?: boolean
|
||||||
outputMuted?: boolean
|
outputMuted?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UpdatedClient = Omit<ChadClient, 'id' | 'isMe'>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user