@xipkg/calls-store 0.1.36 → 0.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -1
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as zustand from 'zustand';
|
|
|
3
3
|
import { LiveKitRoomProps } from '@livekit/components-react';
|
|
4
4
|
import { Track, VideoQuality } from 'livekit-client';
|
|
5
5
|
import { TrackReferenceOrPlaceholder, LocalUserChoices as LocalUserChoices$1 } from '@livekit/components-core';
|
|
6
|
-
import { NoiseCancellationMode } from '@xipkg/calls-types';
|
|
6
|
+
import { NoiseCancellationMode, VoiceEnhancementConfig } from '@xipkg/calls-types';
|
|
7
7
|
|
|
8
8
|
/** Стабильный идентификатор участника для закрепления в рамках кабинета */
|
|
9
9
|
type PinnedParticipantT = {
|
|
@@ -157,6 +157,8 @@ type LocalUserChoices = LocalUserChoices$1 & {
|
|
|
157
157
|
speakerVolume?: number;
|
|
158
158
|
/** Зеркальное отражение локального превью камеры. */
|
|
159
159
|
mirrorVideo?: boolean;
|
|
160
|
+
/** Локальная WASM-обработка голоса перед публикацией микрофона. */
|
|
161
|
+
voiceEnhancement?: VoiceEnhancementConfig;
|
|
160
162
|
};
|
|
161
163
|
declare const useUserChoicesStore: zustand.UseBoundStore<Omit<zustand.StoreApi<LocalUserChoices>, "setState" | "persist"> & {
|
|
162
164
|
setState(partial: LocalUserChoices | Partial<LocalUserChoices> | ((state: LocalUserChoices) => LocalUserChoices | Partial<LocalUserChoices>), replace?: false | undefined): unknown;
|
|
@@ -183,6 +185,8 @@ declare const useUserChoicesStore: zustand.UseBoundStore<Omit<zustand.StoreApi<L
|
|
|
183
185
|
speakerVolume?: number;
|
|
184
186
|
/** Зеркальное отражение локального превью камеры. */
|
|
185
187
|
mirrorVideo?: boolean;
|
|
188
|
+
/** Локальная WASM-обработка голоса перед публикацией микрофона. */
|
|
189
|
+
voiceEnhancement?: VoiceEnhancementConfig;
|
|
186
190
|
}, unknown>>) => void;
|
|
187
191
|
clearStorage: () => void;
|
|
188
192
|
rehydrate: () => Promise<void> | void;
|
|
@@ -210,6 +214,8 @@ declare const useUserChoicesStore: zustand.UseBoundStore<Omit<zustand.StoreApi<L
|
|
|
210
214
|
speakerVolume?: number;
|
|
211
215
|
/** Зеркальное отражение локального превью камеры. */
|
|
212
216
|
mirrorVideo?: boolean;
|
|
217
|
+
/** Локальная WASM-обработка голоса перед публикацией микрофона. */
|
|
218
|
+
voiceEnhancement?: VoiceEnhancementConfig;
|
|
213
219
|
}, unknown>>;
|
|
214
220
|
};
|
|
215
221
|
}>;
|
package/dist/index.mjs
CHANGED
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store/callStore.ts","../src/store/permissions.ts","../src/store/userChoices.ts","../src/store/pinnedTrack.ts","../src/store/useSoundEffectsStore.ts","../src/store/featuresStore.ts","../src/store/useFocusModeStore.ts","../src/store/reactionsStore.ts"],"names":["create","persist"],"mappings":"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","file":"index.mjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { LiveKitRoomProps } from '@livekit/components-react';\nimport { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport type { PinnedParticipantT } from './pinnedTrack';\n\ntype RaisedHandT = {\n participantId: string;\n participantName: string;\n timestamp: number;\n};\n\nexport type CornerT = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\nexport type CompactViewModeT = 'basic' | 'expanded' | 'audio';\n\ntype useCallStoreT = {\n // разрешение от браузера на использование камеры\n isCameraPermission: boolean | null;\n isMicroPermission: boolean | null;\n // включён ли у пользователя микро\n audioEnabled: boolean;\n videoEnabled: boolean;\n // id-выбранного устройства\n audioDeviceId: ConstrainDOMString | undefined;\n audioOutputDeviceId: ConstrainDOMString | undefined;\n videoDeviceId: ConstrainDOMString | undefined;\n // подключена ли конференция\n connect: LiveKitRoomProps['connect'];\n // началась ли ВКС для пользователя\n isStarted: boolean | undefined;\n // состояние подключения\n isConnecting: boolean;\n\n mode: 'compact' | 'full';\n carouselType: 'grid' | 'horizontal' | 'vertical';\n preferredFocusLayout: 'horizontal' | 'vertical';\n activeCorner: CornerT;\n\n /** Режим вида компакт-ВКС: одна плитка, несколько плиток или только аудио */\n compactViewMode: CompactViewModeT;\n\n // Текущая активная доска (для синхронизации с новыми участниками)\n activeBoardId: string | undefined;\n activeClassroom: string | undefined;\n\n /** Локальный переключатель: пользователь сам ушёл в полную ВКС («Только меня»), комната на сервере остаётся на доске */\n localFullView: boolean;\n\n // токен для конференции\n token: string | undefined;\n\n // Поднятые руки\n raisedHands: RaisedHandT[];\n isHandRaised: boolean;\n\n /** Закреплённые участники по кабинетам (userId + source), порядок = приоритет */\n pinnedByClassroom: Record<string, PinnedParticipantT[]>;\n\n updateStore: (type: keyof useCallStoreT, value: any) => void;\n togglePinnedParticipant: (pin: PinnedParticipantT, classroomId: string) => void;\n removePinnedParticipant: (pin: PinnedParticipantT, classroomId: string) => void;\n getPinsForClassroom: (classroomId: string) => PinnedParticipantT[];\n isParticipantPinned: (pin: PinnedParticipantT, classroomId: string) => boolean;\n addRaisedHand: (hand: RaisedHandT) => void;\n removeRaisedHand: (participantId: string) => void;\n toggleHandRaised: () => void;\n clearAllRaisedHands: () => void;\n isHandRaisedByParticipant: (participantId: string) => boolean;\n};\n\nexport const useCallStore = create<useCallStoreT>()(\n persist(\n (set, get) => ({\n isCameraPermission: null,\n isMicroPermission: null,\n audioEnabled: false,\n videoEnabled: false,\n audioDeviceId: undefined,\n audioOutputDeviceId: undefined,\n videoDeviceId: undefined,\n connect: undefined,\n isStarted: undefined,\n isConnecting: false,\n mode: 'full',\n carouselType: 'grid',\n preferredFocusLayout: 'horizontal',\n activeCorner: 'top-left',\n compactViewMode: 'basic',\n\n // Текущая активная доска\n activeBoardId: undefined,\n activeClassroom: undefined,\n\n localFullView: false,\n\n // токен для конференции\n token: undefined,\n\n // Поднятые руки\n raisedHands: [],\n isHandRaised: false,\n\n pinnedByClassroom: {},\n\n updateStore: (type: keyof useCallStoreT, value: any) => set({ [type]: value }),\n\n togglePinnedParticipant: (pin: PinnedParticipantT, classroomId: string) =>\n set((state) => {\n const current = state.pinnedByClassroom[classroomId] ?? [];\n const existingIndex = current.findIndex(\n (item) => item.userId === pin.userId && item.source === pin.source,\n );\n const next = { ...state.pinnedByClassroom };\n\n if (existingIndex >= 0) {\n const updated = current.filter((_, index) => index !== existingIndex);\n if (updated.length) {\n next[classroomId] = updated;\n } else {\n delete next[classroomId];\n }\n } else {\n next[classroomId] = [...current, pin];\n }\n\n return { pinnedByClassroom: next };\n }),\n removePinnedParticipant: (pin: PinnedParticipantT, classroomId: string) =>\n set((state) => {\n const current = state.pinnedByClassroom[classroomId];\n if (!current?.length) return state;\n\n const updated = current.filter(\n (item) => !(item.userId === pin.userId && item.source === pin.source),\n );\n const next = { ...state.pinnedByClassroom };\n if (updated.length) {\n next[classroomId] = updated;\n } else {\n delete next[classroomId];\n }\n return { pinnedByClassroom: next };\n }),\n getPinsForClassroom: (classroomId: string) => get().pinnedByClassroom[classroomId] ?? [],\n isParticipantPinned: (pin: PinnedParticipantT, classroomId: string) => {\n const pins = get().pinnedByClassroom[classroomId] ?? [];\n return pins.some((item) => item.userId === pin.userId && item.source === pin.source);\n },\n\n // Поднятые руки\n addRaisedHand: (hand: RaisedHandT) =>\n set((state) => {\n // Проверяем, есть ли уже рука от этого участника\n const existingHand = state.raisedHands.find(\n (h) => h.participantId === hand.participantId,\n );\n if (existingHand) {\n // Обновляем существующую руку\n return {\n raisedHands: state.raisedHands.map((h) =>\n h.participantId === hand.participantId ? hand : h,\n ),\n };\n }\n // Добавляем новую руку\n return { raisedHands: [...state.raisedHands, hand] };\n }),\n removeRaisedHand: (participantId: string) =>\n set((state) => ({\n raisedHands: state.raisedHands.filter((hand) => hand.participantId !== participantId),\n })),\n toggleHandRaised: () => set((state) => ({ isHandRaised: !state.isHandRaised })),\n clearAllRaisedHands: () => set({ raisedHands: [], isHandRaised: false }),\n isHandRaisedByParticipant: (participantId: string) => {\n const state = get();\n return state.raisedHands.some((hand) => hand.participantId === participantId);\n },\n }),\n {\n name: 'call-store',\n version: 5,\n migrate: (persisted, version) => {\n const state = persisted as Record<string, unknown>;\n if (version < 2) {\n const ct = state.carouselType;\n if (ct === 'focus') {\n state.carouselType = 'horizontal';\n }\n }\n if (version < 3) {\n const ct = state.carouselType;\n state.preferredFocusLayout = ct === 'horizontal' || ct === 'vertical' ? ct : 'horizontal';\n }\n if (version < 4) {\n const mode = state.compactViewMode;\n state.compactViewMode =\n mode === 'basic' || mode === 'expanded' || mode === 'audio' ? mode : 'basic';\n }\n if (version < 5) {\n state.pinnedByClassroom =\n typeof state.pinnedByClassroom === 'object' && state.pinnedByClassroom !== null\n ? state.pinnedByClassroom\n : {};\n delete state.pinnedTrack;\n }\n return state as useCallStoreT;\n },\n partialize: (state) => ({\n isCameraPermission: state.isCameraPermission,\n isMicroPermission: state.isMicroPermission,\n audioEnabled: state.audioEnabled,\n videoEnabled: state.videoEnabled,\n audioDeviceId: state.audioDeviceId,\n videoDeviceId: state.videoDeviceId,\n carouselType: state.carouselType,\n preferredFocusLayout: state.preferredFocusLayout,\n activeCorner: state.activeCorner,\n compactViewMode: state.compactViewMode,\n pinnedByClassroom: state.pinnedByClassroom,\n }),\n },\n ),\n);\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\n\nexport type PermissionStateT = 'granted' | 'prompt' | 'denied' | 'unavailable' | undefined;\n\nexport type PermissionsState = {\n cameraPermission: PermissionStateT;\n microphonePermission: PermissionStateT;\n isLoading: boolean;\n isPermissionDialogOpen: boolean;\n};\n\nconst initialState: PermissionsState = {\n cameraPermission: undefined,\n microphonePermission: undefined,\n isLoading: true,\n isPermissionDialogOpen: false,\n};\n\nexport const usePermissionsStore = create<PermissionsState>()(\n persist(() => initialState, {\n name: 'permissions-storage',\n partialize: (state) => ({\n cameraPermission: state.cameraPermission,\n microphonePermission: state.microphonePermission,\n }),\n }),\n);\n\nexport const openPermissionsDialog = () => {\n usePermissionsStore.setState({ isPermissionDialogOpen: true });\n};\n\nexport const closePermissionsDialog = () => {\n usePermissionsStore.setState({ isPermissionDialogOpen: false });\n};\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport {\n loadUserChoices,\n saveUserChoices,\n LocalUserChoices as LocalUserChoicesLK,\n} from '@livekit/components-core';\nimport { VideoQuality } from 'livekit-client';\nimport type { NoiseCancellationMode } from '@xipkg/calls-types';\n\nexport type VideoResolution = 'h720' | 'h360' | 'h180';\n\nexport type LocalUserChoices = LocalUserChoicesLK & {\n noiseReductionEnabled?: boolean;\n blurEnabled?: boolean;\n audioOutputDeviceId?: string;\n videoPublishResolution?: VideoResolution;\n videoSubscribeQuality?: VideoQuality;\n /** Включено ли шумоподавление (любой режим кроме off). */\n noiseCancellationEnabled?: boolean;\n /** Режим шумоподавления: off | webrtc | krisp. */\n noiseCancellationMode?: NoiseCancellationMode;\n /** Громкость микрофона (0..1), для preview/тестов и будущей apply в звонке. */\n microphoneVolume?: number;\n /** Громкость динамиков (0..1), для тестового воспроизведения и будущей apply в звонке. */\n speakerVolume?: number;\n /** Зеркальное отражение локального превью камеры. */\n mirrorVideo?: boolean;\n};\n\nfunction getUserChoicesState(): LocalUserChoices {\n return {\n noiseReductionEnabled: false,\n blurEnabled: false,\n audioOutputDeviceId: 'default',\n videoPublishResolution: 'h720',\n videoSubscribeQuality: VideoQuality.HIGH,\n noiseCancellationEnabled: false,\n noiseCancellationMode: 'webrtc',\n microphoneVolume: 1,\n speakerVolume: 1,\n mirrorVideo: true,\n ...loadUserChoices(),\n };\n}\n\nexport const useUserChoicesStore = create<LocalUserChoices>()(\n persist(\n () => ({\n ...getUserChoicesState(),\n }),\n {\n name: 'user-choices-storage',\n onRehydrateStorage: () => (state) => {\n if (state) {\n saveUserChoices(state, false);\n }\n },\n },\n ),\n);\n","import { Track } from 'livekit-client';\nimport type { TrackReferenceOrPlaceholder } from '@livekit/components-core';\n\n/** Стабильный идентификатор участника для закрепления в рамках кабинета */\nexport type PinnedParticipantT = {\n userId: string;\n source: Track.Source;\n};\n\n/** @deprecated Используйте PinnedParticipantT */\nexport type PinnedTrackT = PinnedParticipantT;\n\nexport function getParticipantUserId(participant: { identity: string; metadata?: string }): string {\n if (participant.metadata) {\n try {\n const meta = JSON.parse(participant.metadata) as {\n user_id?: string | number;\n id?: string | number;\n };\n if (meta?.user_id != null) return String(meta.user_id);\n if (meta?.id != null) return String(meta.id);\n } catch {\n /* metadata is not JSON */\n }\n }\n return participant.identity;\n}\n\nexport function toPinnedParticipant(track: TrackReferenceOrPlaceholder): PinnedParticipantT {\n return {\n userId: getParticipantUserId(track.participant),\n source: track.source ?? Track.Source.Camera,\n };\n}\n\n/** @deprecated Используйте toPinnedParticipant */\nexport const toPinnedTrack = toPinnedParticipant;\n\nexport function matchesPinnedParticipant(\n track: TrackReferenceOrPlaceholder,\n pinned: PinnedParticipantT,\n): boolean {\n return (\n getParticipantUserId(track.participant) === pinned.userId && track.source === pinned.source\n );\n}\n\n/** @deprecated Используйте matchesPinnedParticipant */\nexport const matchesPinnedTrack = matchesPinnedParticipant;\n\nexport function findPinnedTrackRef(\n tracks: TrackReferenceOrPlaceholder[],\n pinned: PinnedParticipantT,\n): TrackReferenceOrPlaceholder | undefined {\n return tracks.find((track) => matchesPinnedParticipant(track, pinned));\n}\n\n/** Перемещает закреплённых участников в начало списка (порядок pin сохраняется) */\nexport function applyPinsFirst(\n tracks: TrackReferenceOrPlaceholder[],\n pins: PinnedParticipantT[],\n): TrackReferenceOrPlaceholder[] {\n if (!pins.length) return tracks;\n\n const pinnedRefs: TrackReferenceOrPlaceholder[] = [];\n const seen = new Set<TrackReferenceOrPlaceholder>();\n\n for (const pin of pins) {\n const ref = findPinnedTrackRef(tracks, pin);\n if (ref && !seen.has(ref)) {\n pinnedRefs.push(ref);\n seen.add(ref);\n }\n }\n\n if (!pinnedRefs.length) return tracks;\n return [...pinnedRefs, ...tracks.filter((track) => !seen.has(track))];\n}\n\nexport function applyPinFirst(\n tracks: TrackReferenceOrPlaceholder[],\n pinned: PinnedParticipantT | null,\n): TrackReferenceOrPlaceholder[] {\n return applyPinsFirst(tracks, pinned ? [pinned] : []);\n}\n\n/** Участник для главной плитки focus-сетки (без учёта локального pin) */\nexport function pickDefaultFocusTrack(\n tracks: TrackReferenceOrPlaceholder[],\n): TrackReferenceOrPlaceholder | undefined {\n const screenShare = tracks.find(\n (track) => track.source === Track.Source.ScreenShare && track.publication?.isSubscribed,\n );\n if (screenShare) return screenShare;\n return tracks.find((track) => track.source === Track.Source.Camera);\n}\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\n\ntype SoundKey =\n 'chatMessage' | 'handRaise' | 'userJoin' | 'userLeft' | 'boardTimerEnd' | 'boardTimerWarn';\n\ninterface SoundEffectsState {\n chatMessageVolume: number;\n handRaiseVolume: number;\n userJoinVolume: number;\n userLeftVolume: number;\n boardTimerEndVolume: number;\n boardTimerWarnVolume: number;\n\n setSoundVolume: (key: SoundKey, volume: number) => void;\n getEffectiveVolume: (key: SoundKey) => number;\n}\n\nconst DEFAULTS: Record<SoundKey, number> = {\n chatMessage: 0.25,\n handRaise: 0.25,\n userJoin: 0.25,\n userLeft: 0.25,\n boardTimerEnd: 1,\n boardTimerWarn: 1,\n};\n\nconst VOLUME_KEY_MAP: Record<SoundKey, keyof SoundEffectsState> = {\n chatMessage: 'chatMessageVolume',\n handRaise: 'handRaiseVolume',\n userJoin: 'userJoinVolume',\n userLeft: 'userLeftVolume',\n boardTimerEnd: 'boardTimerEndVolume',\n boardTimerWarn: 'boardTimerWarnVolume',\n};\n\nconst clamp = (value: number, min: number, max: number) => Math.max(min, Math.min(max, value));\n\nexport const useSoundEffectsStore = create<SoundEffectsState>()(\n persist(\n (set, get) => ({\n chatMessageVolume: DEFAULTS.chatMessage,\n handRaiseVolume: DEFAULTS.handRaise,\n userJoinVolume: DEFAULTS.userJoin,\n userLeftVolume: DEFAULTS.userLeft,\n boardTimerEndVolume: DEFAULTS.boardTimerEnd,\n boardTimerWarnVolume: DEFAULTS.boardTimerWarn,\n\n setSoundVolume: (key, volume) => {\n const min = key === 'boardTimerEnd' ? 0.2 : 0;\n const clamped = clamp(volume, min, 1);\n set({ [VOLUME_KEY_MAP[key]]: clamped });\n },\n\n getEffectiveVolume: (key) => {\n const storeKey = VOLUME_KEY_MAP[key];\n return get()[storeKey] as number;\n },\n }),\n {\n name: 'sound-effects-store',\n version: 1,\n partialize: (state) => ({\n chatMessageVolume: state.chatMessageVolume,\n handRaiseVolume: state.handRaiseVolume,\n userJoinVolume: state.userJoinVolume,\n userLeftVolume: state.userLeftVolume,\n boardTimerEndVolume: state.boardTimerEndVolume,\n boardTimerWarnVolume: state.boardTimerWarnVolume,\n }),\n },\n ),\n);\n\nexport { DEFAULTS as SOUND_DEFAULTS };\nexport type { SoundKey };\n","import { create } from 'zustand';\n\nexport type FeatureKey = 'compactView' | 'whiteboard' | 'raiseHand' | 'chat' | 'reactions';\n\ntype FeatureMapT = Record<FeatureKey, boolean>;\n\ntype FeaturesState = {\n features: FeatureMapT;\n\n setFeature: (key: FeatureKey, value: boolean) => void;\n setFeatures: (features: Partial<FeatureMapT>) => void;\n};\n\nconst defaultFeatures: FeatureMapT = {\n compactView: true,\n whiteboard: true,\n raiseHand: true,\n chat: true,\n reactions: true,\n};\n\nexport const useFeaturesStore = create<FeaturesState>((set) => ({\n features: defaultFeatures,\n\n setFeature: (key, value) =>\n set((state) => ({\n features: { ...state.features, [key]: value },\n })),\n\n setFeatures: (features) =>\n set((state) => ({\n features: { ...state.features, ...features },\n })),\n}));\n","import { create } from 'zustand';\n\ninterface FocusModeState {\n focusMode: boolean;\n setFocusMode: (value: boolean) => void;\n toggleFocusMode: () => void;\n}\n\nexport const useFocusModeStore = create<FocusModeState>((set) => ({\n focusMode: false,\n setFocusMode: (value) => set({ focusMode: value }),\n toggleFocusMode: () => set((state) => ({ focusMode: !state.focusMode })),\n}));\n","import { create } from 'zustand';\n\nexport type FloatingReactionT = {\n id: string;\n emoji: string;\n senderId: string;\n senderName: string;\n timestamp: number;\n};\n\nexport type ParticipantReactionT = {\n emoji: string;\n timestamp: number;\n};\n\n// Максимум одновременных всплывающих реакций на экране: если реагирует сразу много\n// участников, лишние (самые старые) вытесняются, чтобы не разрастался DOM/анимации.\n// С учётом пачки (~4 смайлика на одну реакцию) лимит повыше, чтобы не срезать чужие пачки\nconst MAX_FLOATING_REACTIONS = 48;\n\n/** Как долго держится короткая метка реакции на плитке участника */\nexport const PARTICIPANT_REACTION_TTL_MS = 3000;\n\ntype useReactionsStoreT = {\n /** Очередь всплывающих реакций для оверлея поверх сетки видео */\n floatingReactions: FloatingReactionT[];\n /** Последняя активная реакция участника — для бейджа на плитке */\n participantReactions: Record<string, ParticipantReactionT>;\n\n addFloatingReaction: (reaction: FloatingReactionT) => void;\n /** Добавить несколько пузырьков одним апдейтом — иначе N set() рвут анимации на каждом рендере */\n addFloatingReactions: (reactions: FloatingReactionT[]) => void;\n removeFloatingReaction: (id: string) => void;\n setParticipantReaction: (participantId: string, emoji: string, timestamp: number) => void;\n clearParticipantReaction: (participantId: string) => void;\n clearAllReactions: () => void;\n};\n\nexport const useReactionsStore = create<useReactionsStoreT>()((set, get) => ({\n floatingReactions: [],\n participantReactions: {},\n\n addFloatingReaction: (reaction: FloatingReactionT) => {\n get().addFloatingReactions([reaction]);\n },\n\n addFloatingReactions: (reactions: FloatingReactionT[]) => {\n if (reactions.length === 0) return;\n\n const { floatingReactions } = get();\n const existingIds = new Set(floatingReactions.map((item) => item.id));\n const unique = reactions.filter((reaction) => !existingIds.has(reaction.id));\n if (unique.length === 0) return;\n\n const next = [...floatingReactions, ...unique];\n const trimmed =\n next.length > MAX_FLOATING_REACTIONS\n ? next.slice(next.length - MAX_FLOATING_REACTIONS)\n : next;\n\n set({ floatingReactions: trimmed });\n },\n\n removeFloatingReaction: (id: string) =>\n set((state) => ({\n floatingReactions: state.floatingReactions.filter((item) => item.id !== id),\n })),\n\n setParticipantReaction: (participantId: string, emoji: string, timestamp: number) =>\n set((state) => {\n const existing = state.participantReactions[participantId];\n // Не откатываем более свежую реакцию более старым (например, задержанным) сообщением\n if (existing && existing.timestamp > timestamp) {\n return state;\n }\n\n return {\n participantReactions: {\n ...state.participantReactions,\n [participantId]: { emoji, timestamp },\n },\n };\n }),\n\n clearParticipantReaction: (participantId: string) =>\n set((state) => {\n if (!(participantId in state.participantReactions)) return state;\n const next = { ...state.participantReactions };\n delete next[participantId];\n return { participantReactions: next };\n }),\n\n clearAllReactions: () => set({ floatingReactions: [], participantReactions: {} }),\n}));\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/store/callStore.ts","../src/store/permissions.ts","../src/store/userChoices.ts","../src/store/pinnedTrack.ts","../src/store/useSoundEffectsStore.ts","../src/store/featuresStore.ts","../src/store/useFocusModeStore.ts","../src/store/reactionsStore.ts"],"names":["create","persist"],"mappings":"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","file":"index.mjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { LiveKitRoomProps } from '@livekit/components-react';\nimport { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport type { PinnedParticipantT } from './pinnedTrack';\n\ntype RaisedHandT = {\n participantId: string;\n participantName: string;\n timestamp: number;\n};\n\nexport type CornerT = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\nexport type CompactViewModeT = 'basic' | 'expanded' | 'audio';\n\ntype useCallStoreT = {\n // разрешение от браузера на использование камеры\n isCameraPermission: boolean | null;\n isMicroPermission: boolean | null;\n // включён ли у пользователя микро\n audioEnabled: boolean;\n videoEnabled: boolean;\n // id-выбранного устройства\n audioDeviceId: ConstrainDOMString | undefined;\n audioOutputDeviceId: ConstrainDOMString | undefined;\n videoDeviceId: ConstrainDOMString | undefined;\n // подключена ли конференция\n connect: LiveKitRoomProps['connect'];\n // началась ли ВКС для пользователя\n isStarted: boolean | undefined;\n // состояние подключения\n isConnecting: boolean;\n\n mode: 'compact' | 'full';\n carouselType: 'grid' | 'horizontal' | 'vertical';\n preferredFocusLayout: 'horizontal' | 'vertical';\n activeCorner: CornerT;\n\n /** Режим вида компакт-ВКС: одна плитка, несколько плиток или только аудио */\n compactViewMode: CompactViewModeT;\n\n // Текущая активная доска (для синхронизации с новыми участниками)\n activeBoardId: string | undefined;\n activeClassroom: string | undefined;\n\n /** Локальный переключатель: пользователь сам ушёл в полную ВКС («Только меня»), комната на сервере остаётся на доске */\n localFullView: boolean;\n\n // токен для конференции\n token: string | undefined;\n\n // Поднятые руки\n raisedHands: RaisedHandT[];\n isHandRaised: boolean;\n\n /** Закреплённые участники по кабинетам (userId + source), порядок = приоритет */\n pinnedByClassroom: Record<string, PinnedParticipantT[]>;\n\n updateStore: (type: keyof useCallStoreT, value: any) => void;\n togglePinnedParticipant: (pin: PinnedParticipantT, classroomId: string) => void;\n removePinnedParticipant: (pin: PinnedParticipantT, classroomId: string) => void;\n getPinsForClassroom: (classroomId: string) => PinnedParticipantT[];\n isParticipantPinned: (pin: PinnedParticipantT, classroomId: string) => boolean;\n addRaisedHand: (hand: RaisedHandT) => void;\n removeRaisedHand: (participantId: string) => void;\n toggleHandRaised: () => void;\n clearAllRaisedHands: () => void;\n isHandRaisedByParticipant: (participantId: string) => boolean;\n};\n\nexport const useCallStore = create<useCallStoreT>()(\n persist(\n (set, get) => ({\n isCameraPermission: null,\n isMicroPermission: null,\n audioEnabled: false,\n videoEnabled: false,\n audioDeviceId: undefined,\n audioOutputDeviceId: undefined,\n videoDeviceId: undefined,\n connect: undefined,\n isStarted: undefined,\n isConnecting: false,\n mode: 'full',\n carouselType: 'grid',\n preferredFocusLayout: 'horizontal',\n activeCorner: 'top-left',\n compactViewMode: 'basic',\n\n // Текущая активная доска\n activeBoardId: undefined,\n activeClassroom: undefined,\n\n localFullView: false,\n\n // токен для конференции\n token: undefined,\n\n // Поднятые руки\n raisedHands: [],\n isHandRaised: false,\n\n pinnedByClassroom: {},\n\n updateStore: (type: keyof useCallStoreT, value: any) => set({ [type]: value }),\n\n togglePinnedParticipant: (pin: PinnedParticipantT, classroomId: string) =>\n set((state) => {\n const current = state.pinnedByClassroom[classroomId] ?? [];\n const existingIndex = current.findIndex(\n (item) => item.userId === pin.userId && item.source === pin.source,\n );\n const next = { ...state.pinnedByClassroom };\n\n if (existingIndex >= 0) {\n const updated = current.filter((_, index) => index !== existingIndex);\n if (updated.length) {\n next[classroomId] = updated;\n } else {\n delete next[classroomId];\n }\n } else {\n next[classroomId] = [...current, pin];\n }\n\n return { pinnedByClassroom: next };\n }),\n removePinnedParticipant: (pin: PinnedParticipantT, classroomId: string) =>\n set((state) => {\n const current = state.pinnedByClassroom[classroomId];\n if (!current?.length) return state;\n\n const updated = current.filter(\n (item) => !(item.userId === pin.userId && item.source === pin.source),\n );\n const next = { ...state.pinnedByClassroom };\n if (updated.length) {\n next[classroomId] = updated;\n } else {\n delete next[classroomId];\n }\n return { pinnedByClassroom: next };\n }),\n getPinsForClassroom: (classroomId: string) => get().pinnedByClassroom[classroomId] ?? [],\n isParticipantPinned: (pin: PinnedParticipantT, classroomId: string) => {\n const pins = get().pinnedByClassroom[classroomId] ?? [];\n return pins.some((item) => item.userId === pin.userId && item.source === pin.source);\n },\n\n // Поднятые руки\n addRaisedHand: (hand: RaisedHandT) =>\n set((state) => {\n // Проверяем, есть ли уже рука от этого участника\n const existingHand = state.raisedHands.find(\n (h) => h.participantId === hand.participantId,\n );\n if (existingHand) {\n // Обновляем существующую руку\n return {\n raisedHands: state.raisedHands.map((h) =>\n h.participantId === hand.participantId ? hand : h,\n ),\n };\n }\n // Добавляем новую руку\n return { raisedHands: [...state.raisedHands, hand] };\n }),\n removeRaisedHand: (participantId: string) =>\n set((state) => ({\n raisedHands: state.raisedHands.filter((hand) => hand.participantId !== participantId),\n })),\n toggleHandRaised: () => set((state) => ({ isHandRaised: !state.isHandRaised })),\n clearAllRaisedHands: () => set({ raisedHands: [], isHandRaised: false }),\n isHandRaisedByParticipant: (participantId: string) => {\n const state = get();\n return state.raisedHands.some((hand) => hand.participantId === participantId);\n },\n }),\n {\n name: 'call-store',\n version: 5,\n migrate: (persisted, version) => {\n const state = persisted as Record<string, unknown>;\n if (version < 2) {\n const ct = state.carouselType;\n if (ct === 'focus') {\n state.carouselType = 'horizontal';\n }\n }\n if (version < 3) {\n const ct = state.carouselType;\n state.preferredFocusLayout = ct === 'horizontal' || ct === 'vertical' ? ct : 'horizontal';\n }\n if (version < 4) {\n const mode = state.compactViewMode;\n state.compactViewMode =\n mode === 'basic' || mode === 'expanded' || mode === 'audio' ? mode : 'basic';\n }\n if (version < 5) {\n state.pinnedByClassroom =\n typeof state.pinnedByClassroom === 'object' && state.pinnedByClassroom !== null\n ? state.pinnedByClassroom\n : {};\n delete state.pinnedTrack;\n }\n return state as useCallStoreT;\n },\n partialize: (state) => ({\n isCameraPermission: state.isCameraPermission,\n isMicroPermission: state.isMicroPermission,\n audioEnabled: state.audioEnabled,\n videoEnabled: state.videoEnabled,\n audioDeviceId: state.audioDeviceId,\n videoDeviceId: state.videoDeviceId,\n carouselType: state.carouselType,\n preferredFocusLayout: state.preferredFocusLayout,\n activeCorner: state.activeCorner,\n compactViewMode: state.compactViewMode,\n pinnedByClassroom: state.pinnedByClassroom,\n }),\n },\n ),\n);\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\n\nexport type PermissionStateT = 'granted' | 'prompt' | 'denied' | 'unavailable' | undefined;\n\nexport type PermissionsState = {\n cameraPermission: PermissionStateT;\n microphonePermission: PermissionStateT;\n isLoading: boolean;\n isPermissionDialogOpen: boolean;\n};\n\nconst initialState: PermissionsState = {\n cameraPermission: undefined,\n microphonePermission: undefined,\n isLoading: true,\n isPermissionDialogOpen: false,\n};\n\nexport const usePermissionsStore = create<PermissionsState>()(\n persist(() => initialState, {\n name: 'permissions-storage',\n partialize: (state) => ({\n cameraPermission: state.cameraPermission,\n microphonePermission: state.microphonePermission,\n }),\n }),\n);\n\nexport const openPermissionsDialog = () => {\n usePermissionsStore.setState({ isPermissionDialogOpen: true });\n};\n\nexport const closePermissionsDialog = () => {\n usePermissionsStore.setState({ isPermissionDialogOpen: false });\n};\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport {\n loadUserChoices,\n saveUserChoices,\n LocalUserChoices as LocalUserChoicesLK,\n} from '@livekit/components-core';\nimport { VideoQuality } from 'livekit-client';\nimport type { NoiseCancellationMode, VoiceEnhancementConfig } from '@xipkg/calls-types';\n\nexport type VideoResolution = 'h720' | 'h360' | 'h180';\n\nexport type LocalUserChoices = LocalUserChoicesLK & {\n noiseReductionEnabled?: boolean;\n blurEnabled?: boolean;\n audioOutputDeviceId?: string;\n videoPublishResolution?: VideoResolution;\n videoSubscribeQuality?: VideoQuality;\n /** Включено ли шумоподавление (любой режим кроме off). */\n noiseCancellationEnabled?: boolean;\n /** Режим шумоподавления: off | webrtc | krisp. */\n noiseCancellationMode?: NoiseCancellationMode;\n /** Громкость микрофона (0..1), для preview/тестов и будущей apply в звонке. */\n microphoneVolume?: number;\n /** Громкость динамиков (0..1), для тестового воспроизведения и будущей apply в звонке. */\n speakerVolume?: number;\n /** Зеркальное отражение локального превью камеры. */\n mirrorVideo?: boolean;\n /** Локальная WASM-обработка голоса перед публикацией микрофона. */\n voiceEnhancement?: VoiceEnhancementConfig;\n};\n\nfunction getUserChoicesState(): LocalUserChoices {\n return {\n noiseReductionEnabled: false,\n blurEnabled: false,\n audioOutputDeviceId: 'default',\n videoPublishResolution: 'h720',\n videoSubscribeQuality: VideoQuality.HIGH,\n noiseCancellationEnabled: false,\n noiseCancellationMode: 'webrtc',\n microphoneVolume: 1,\n speakerVolume: 1,\n mirrorVideo: true,\n voiceEnhancement: {\n enabled: false,\n intensity: 90,\n },\n ...loadUserChoices(),\n };\n}\n\nexport const useUserChoicesStore = create<LocalUserChoices>()(\n persist(\n () => ({\n ...getUserChoicesState(),\n }),\n {\n name: 'user-choices-storage',\n onRehydrateStorage: () => (state) => {\n if (state) {\n saveUserChoices(state, false);\n }\n },\n },\n ),\n);\n","import { Track } from 'livekit-client';\nimport type { TrackReferenceOrPlaceholder } from '@livekit/components-core';\n\n/** Стабильный идентификатор участника для закрепления в рамках кабинета */\nexport type PinnedParticipantT = {\n userId: string;\n source: Track.Source;\n};\n\n/** @deprecated Используйте PinnedParticipantT */\nexport type PinnedTrackT = PinnedParticipantT;\n\nexport function getParticipantUserId(participant: { identity: string; metadata?: string }): string {\n if (participant.metadata) {\n try {\n const meta = JSON.parse(participant.metadata) as {\n user_id?: string | number;\n id?: string | number;\n };\n if (meta?.user_id != null) return String(meta.user_id);\n if (meta?.id != null) return String(meta.id);\n } catch {\n /* metadata is not JSON */\n }\n }\n return participant.identity;\n}\n\nexport function toPinnedParticipant(track: TrackReferenceOrPlaceholder): PinnedParticipantT {\n return {\n userId: getParticipantUserId(track.participant),\n source: track.source ?? Track.Source.Camera,\n };\n}\n\n/** @deprecated Используйте toPinnedParticipant */\nexport const toPinnedTrack = toPinnedParticipant;\n\nexport function matchesPinnedParticipant(\n track: TrackReferenceOrPlaceholder,\n pinned: PinnedParticipantT,\n): boolean {\n return (\n getParticipantUserId(track.participant) === pinned.userId && track.source === pinned.source\n );\n}\n\n/** @deprecated Используйте matchesPinnedParticipant */\nexport const matchesPinnedTrack = matchesPinnedParticipant;\n\nexport function findPinnedTrackRef(\n tracks: TrackReferenceOrPlaceholder[],\n pinned: PinnedParticipantT,\n): TrackReferenceOrPlaceholder | undefined {\n return tracks.find((track) => matchesPinnedParticipant(track, pinned));\n}\n\n/** Перемещает закреплённых участников в начало списка (порядок pin сохраняется) */\nexport function applyPinsFirst(\n tracks: TrackReferenceOrPlaceholder[],\n pins: PinnedParticipantT[],\n): TrackReferenceOrPlaceholder[] {\n if (!pins.length) return tracks;\n\n const pinnedRefs: TrackReferenceOrPlaceholder[] = [];\n const seen = new Set<TrackReferenceOrPlaceholder>();\n\n for (const pin of pins) {\n const ref = findPinnedTrackRef(tracks, pin);\n if (ref && !seen.has(ref)) {\n pinnedRefs.push(ref);\n seen.add(ref);\n }\n }\n\n if (!pinnedRefs.length) return tracks;\n return [...pinnedRefs, ...tracks.filter((track) => !seen.has(track))];\n}\n\nexport function applyPinFirst(\n tracks: TrackReferenceOrPlaceholder[],\n pinned: PinnedParticipantT | null,\n): TrackReferenceOrPlaceholder[] {\n return applyPinsFirst(tracks, pinned ? [pinned] : []);\n}\n\n/** Участник для главной плитки focus-сетки (без учёта локального pin) */\nexport function pickDefaultFocusTrack(\n tracks: TrackReferenceOrPlaceholder[],\n): TrackReferenceOrPlaceholder | undefined {\n const screenShare = tracks.find(\n (track) => track.source === Track.Source.ScreenShare && track.publication?.isSubscribed,\n );\n if (screenShare) return screenShare;\n return tracks.find((track) => track.source === Track.Source.Camera);\n}\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\n\ntype SoundKey =\n 'chatMessage' | 'handRaise' | 'userJoin' | 'userLeft' | 'boardTimerEnd' | 'boardTimerWarn';\n\ninterface SoundEffectsState {\n chatMessageVolume: number;\n handRaiseVolume: number;\n userJoinVolume: number;\n userLeftVolume: number;\n boardTimerEndVolume: number;\n boardTimerWarnVolume: number;\n\n setSoundVolume: (key: SoundKey, volume: number) => void;\n getEffectiveVolume: (key: SoundKey) => number;\n}\n\nconst DEFAULTS: Record<SoundKey, number> = {\n chatMessage: 0.25,\n handRaise: 0.25,\n userJoin: 0.25,\n userLeft: 0.25,\n boardTimerEnd: 1,\n boardTimerWarn: 1,\n};\n\nconst VOLUME_KEY_MAP: Record<SoundKey, keyof SoundEffectsState> = {\n chatMessage: 'chatMessageVolume',\n handRaise: 'handRaiseVolume',\n userJoin: 'userJoinVolume',\n userLeft: 'userLeftVolume',\n boardTimerEnd: 'boardTimerEndVolume',\n boardTimerWarn: 'boardTimerWarnVolume',\n};\n\nconst clamp = (value: number, min: number, max: number) => Math.max(min, Math.min(max, value));\n\nexport const useSoundEffectsStore = create<SoundEffectsState>()(\n persist(\n (set, get) => ({\n chatMessageVolume: DEFAULTS.chatMessage,\n handRaiseVolume: DEFAULTS.handRaise,\n userJoinVolume: DEFAULTS.userJoin,\n userLeftVolume: DEFAULTS.userLeft,\n boardTimerEndVolume: DEFAULTS.boardTimerEnd,\n boardTimerWarnVolume: DEFAULTS.boardTimerWarn,\n\n setSoundVolume: (key, volume) => {\n const min = key === 'boardTimerEnd' ? 0.2 : 0;\n const clamped = clamp(volume, min, 1);\n set({ [VOLUME_KEY_MAP[key]]: clamped });\n },\n\n getEffectiveVolume: (key) => {\n const storeKey = VOLUME_KEY_MAP[key];\n return get()[storeKey] as number;\n },\n }),\n {\n name: 'sound-effects-store',\n version: 1,\n partialize: (state) => ({\n chatMessageVolume: state.chatMessageVolume,\n handRaiseVolume: state.handRaiseVolume,\n userJoinVolume: state.userJoinVolume,\n userLeftVolume: state.userLeftVolume,\n boardTimerEndVolume: state.boardTimerEndVolume,\n boardTimerWarnVolume: state.boardTimerWarnVolume,\n }),\n },\n ),\n);\n\nexport { DEFAULTS as SOUND_DEFAULTS };\nexport type { SoundKey };\n","import { create } from 'zustand';\n\nexport type FeatureKey = 'compactView' | 'whiteboard' | 'raiseHand' | 'chat' | 'reactions';\n\ntype FeatureMapT = Record<FeatureKey, boolean>;\n\ntype FeaturesState = {\n features: FeatureMapT;\n\n setFeature: (key: FeatureKey, value: boolean) => void;\n setFeatures: (features: Partial<FeatureMapT>) => void;\n};\n\nconst defaultFeatures: FeatureMapT = {\n compactView: true,\n whiteboard: true,\n raiseHand: true,\n chat: true,\n reactions: true,\n};\n\nexport const useFeaturesStore = create<FeaturesState>((set) => ({\n features: defaultFeatures,\n\n setFeature: (key, value) =>\n set((state) => ({\n features: { ...state.features, [key]: value },\n })),\n\n setFeatures: (features) =>\n set((state) => ({\n features: { ...state.features, ...features },\n })),\n}));\n","import { create } from 'zustand';\n\ninterface FocusModeState {\n focusMode: boolean;\n setFocusMode: (value: boolean) => void;\n toggleFocusMode: () => void;\n}\n\nexport const useFocusModeStore = create<FocusModeState>((set) => ({\n focusMode: false,\n setFocusMode: (value) => set({ focusMode: value }),\n toggleFocusMode: () => set((state) => ({ focusMode: !state.focusMode })),\n}));\n","import { create } from 'zustand';\n\nexport type FloatingReactionT = {\n id: string;\n emoji: string;\n senderId: string;\n senderName: string;\n timestamp: number;\n};\n\nexport type ParticipantReactionT = {\n emoji: string;\n timestamp: number;\n};\n\n// Максимум одновременных всплывающих реакций на экране: если реагирует сразу много\n// участников, лишние (самые старые) вытесняются, чтобы не разрастался DOM/анимации.\n// С учётом пачки (~4 смайлика на одну реакцию) лимит повыше, чтобы не срезать чужие пачки\nconst MAX_FLOATING_REACTIONS = 48;\n\n/** Как долго держится короткая метка реакции на плитке участника */\nexport const PARTICIPANT_REACTION_TTL_MS = 3000;\n\ntype useReactionsStoreT = {\n /** Очередь всплывающих реакций для оверлея поверх сетки видео */\n floatingReactions: FloatingReactionT[];\n /** Последняя активная реакция участника — для бейджа на плитке */\n participantReactions: Record<string, ParticipantReactionT>;\n\n addFloatingReaction: (reaction: FloatingReactionT) => void;\n /** Добавить несколько пузырьков одним апдейтом — иначе N set() рвут анимации на каждом рендере */\n addFloatingReactions: (reactions: FloatingReactionT[]) => void;\n removeFloatingReaction: (id: string) => void;\n setParticipantReaction: (participantId: string, emoji: string, timestamp: number) => void;\n clearParticipantReaction: (participantId: string) => void;\n clearAllReactions: () => void;\n};\n\nexport const useReactionsStore = create<useReactionsStoreT>()((set, get) => ({\n floatingReactions: [],\n participantReactions: {},\n\n addFloatingReaction: (reaction: FloatingReactionT) => {\n get().addFloatingReactions([reaction]);\n },\n\n addFloatingReactions: (reactions: FloatingReactionT[]) => {\n if (reactions.length === 0) return;\n\n const { floatingReactions } = get();\n const existingIds = new Set(floatingReactions.map((item) => item.id));\n const unique = reactions.filter((reaction) => !existingIds.has(reaction.id));\n if (unique.length === 0) return;\n\n const next = [...floatingReactions, ...unique];\n const trimmed =\n next.length > MAX_FLOATING_REACTIONS\n ? next.slice(next.length - MAX_FLOATING_REACTIONS)\n : next;\n\n set({ floatingReactions: trimmed });\n },\n\n removeFloatingReaction: (id: string) =>\n set((state) => ({\n floatingReactions: state.floatingReactions.filter((item) => item.id !== id),\n })),\n\n setParticipantReaction: (participantId: string, emoji: string, timestamp: number) =>\n set((state) => {\n const existing = state.participantReactions[participantId];\n // Не откатываем более свежую реакцию более старым (например, задержанным) сообщением\n if (existing && existing.timestamp > timestamp) {\n return state;\n }\n\n return {\n participantReactions: {\n ...state.participantReactions,\n [participantId]: { emoji, timestamp },\n },\n };\n }),\n\n clearParticipantReaction: (participantId: string) =>\n set((state) => {\n if (!(participantId in state.participantReactions)) return state;\n const next = { ...state.participantReactions };\n delete next[participantId];\n return { participantReactions: next };\n }),\n\n clearAllReactions: () => set({ floatingReactions: [], participantReactions: {} }),\n}));\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xipkg/calls-store",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.37",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"zustand": "5.0.14",
|
|
16
|
-
"@xipkg/calls-types": "^0.1.
|
|
16
|
+
"@xipkg/calls-types": "^0.1.37"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@tanstack/react-query-devtools": "5.101.4",
|