front update
All checks were successful
Deploy / deploy (push) Successful in 38s

This commit is contained in:
Никита Круглицкий 2025-10-09 06:14:35 +06:00
parent fc43c8df96
commit e2064dba6c
11 changed files with 122 additions and 22 deletions

Binary file not shown.

View File

@ -5,6 +5,3 @@
<PrimeToast position="bottom-center" />
</template>
<script lang="ts" setup>
</script>

View File

@ -28,7 +28,7 @@
<span>Volume</span>
<span>{{ volume }}</span>
</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>
</template>
</PrimeMenu>

View File

@ -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,

View 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,
}
})

View File

@ -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<mediasoupClient.Device>()
const rtpCapabilities = shallowRef<mediasoupClient.types.RtpCapabilities>()
@ -30,8 +33,6 @@ export const useMediasoup = createSharedComposable(() => {
const webcamProducer = 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 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,
rtpCapabilities: rtpCapabilities.value,
})).map(transformClient)
addClient(...joinedClients)
toast.add({ severity: 'success', summary: 'Joined', closable: false, life: 1000 })
await enableMic()
})
socket.on('newPeer', (client) => {
clients.value.push(transformClient(client))
triggerRef(clients)
addClient(transformClient(client))
})
socket.on('peerClosed', (id) => {
clients.value = clients.value.filter(client => client.id !== id)
removeClient(id)
})
socket.on(
@ -317,7 +321,6 @@ export const useMediasoup = createSharedComposable(() => {
return {
init,
clients,
consumers,
producers,
sendTransport,

View File

@ -3,6 +3,8 @@ import { createSharedComposable } from '@vueuse/core'
import { io } from 'socket.io-client'
export const useSignaling = createSharedComposable(() => {
const toast = useToast()
const socket = shallowRef<Socket>()
const connected = ref(false)
@ -35,6 +37,13 @@ export const useSignaling = createSharedComposable(() => {
})
}, { 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(() => {
socket.value?.close()
})

View File

@ -1,6 +1,6 @@
<template>
<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+">
<template #right>
<PrimeButtonGroup class="ml-auto">

View File

@ -18,14 +18,19 @@ definePageMeta({
name: 'Preferences',
})
const signaling = useSignaling()
const { username } = usePreferences()
const toast = useToast()
const localUsername = ref(username.value)
function save() {
async function save() {
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>

View File

@ -3,7 +3,7 @@ import tailwindcss from '@tailwindcss/vite'
export default defineNuxtConfig({
compatibilityDate: '2025-09-29',
devtools: { enabled: true },
devtools: { enabled: true, vueDevTools: true },
ssr: false,
modules: [
'@nuxt/fonts',

View File

@ -8,3 +8,5 @@ export interface ChadClient extends RemoteClient {
inputMuted?: boolean
outputMuted?: boolean
}
export type UpdatedClient = Omit<ChadClient, 'id' | 'isMe'>