@xipkg/calls-utils 0.0.0
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 +46 -0
- package/dist/index.mjs +171 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Participant, LocalParticipant } from 'livekit-client';
|
|
2
|
+
|
|
3
|
+
declare const useFullScreen: (containerId: string) => {
|
|
4
|
+
toggleFullScreen: () => void;
|
|
5
|
+
isFullScreen: boolean;
|
|
6
|
+
isFullScreenSupported: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
declare const useKeyPress: (targetKey: string, callback: (event: KeyboardEvent) => void) => void;
|
|
10
|
+
|
|
11
|
+
declare const useMedia: (query: string, defaultState?: boolean) => boolean;
|
|
12
|
+
|
|
13
|
+
declare function isFireFox(): boolean;
|
|
14
|
+
declare function isChromiumBased(): boolean;
|
|
15
|
+
declare function isSafari(): boolean;
|
|
16
|
+
declare function isLocal(p: Participant): p is LocalParticipant;
|
|
17
|
+
declare function isMacintosh(): boolean;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Утилита для воспроизведения звуков уведомлений в ВКС
|
|
21
|
+
*/
|
|
22
|
+
declare const SOUND_PATHS: {
|
|
23
|
+
readonly chatMessage: "/sounds/chat-message.wav";
|
|
24
|
+
readonly handRaise: "/sounds/hand-raise.wav";
|
|
25
|
+
readonly userJoin: "/sounds/user-join-to-call.mp3";
|
|
26
|
+
readonly userLeft: "/sounds/user-left-from-call.mp3";
|
|
27
|
+
};
|
|
28
|
+
type SoundType = keyof typeof SOUND_PATHS;
|
|
29
|
+
/**
|
|
30
|
+
* Воспроизводит звук с указанной громкостью
|
|
31
|
+
* @param soundType - тип звука (chatMessage или handRaise)
|
|
32
|
+
* @param volume - громкость от 0 до 1 (0 = беззвучно, 1 = максимальная громкость)
|
|
33
|
+
*/
|
|
34
|
+
declare const playSound: (soundType: SoundType, volume?: number) => void;
|
|
35
|
+
|
|
36
|
+
/** События телеметрии шумоподавления для umami. */
|
|
37
|
+
declare const NOISE_CANCELLATION_EVENTS: {
|
|
38
|
+
readonly MODE_CHANGED: "noise_cancellation_mode_changed";
|
|
39
|
+
readonly APPLY_FAILED: "noise_cancellation_apply_failed";
|
|
40
|
+
readonly FALLBACK_TO_WEBRTC: "noise_cancellation_fallback_to_webrtc";
|
|
41
|
+
readonly KRISP_UNSUPPORTED_BROWSER: "noise_cancellation_krisp_unsupported_browser";
|
|
42
|
+
};
|
|
43
|
+
type NoiseCancellationEvent = (typeof NOISE_CANCELLATION_EVENTS)[keyof typeof NOISE_CANCELLATION_EVENTS];
|
|
44
|
+
declare function trackNoiseCancellationEvent(event: NoiseCancellationEvent, data?: Record<string, string>): void;
|
|
45
|
+
|
|
46
|
+
export { NOISE_CANCELLATION_EVENTS, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { getBrowser, LocalParticipant } from 'livekit-client';
|
|
3
|
+
|
|
4
|
+
// src/useFullScreen.ts
|
|
5
|
+
var useFullScreen = (containerId) => {
|
|
6
|
+
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
7
|
+
const hasStandardFullscreen = typeof document !== "undefined" && document.fullscreenEnabled && typeof HTMLElement.prototype.requestFullscreen === "function";
|
|
8
|
+
const isFullScreenSupported = hasStandardFullscreen;
|
|
9
|
+
const toggleFullScreen = () => {
|
|
10
|
+
const element = document.getElementById(containerId);
|
|
11
|
+
const fullScreen = document.fullscreenElement;
|
|
12
|
+
if (fullScreen) {
|
|
13
|
+
document.exitFullscreen();
|
|
14
|
+
setIsFullScreen(false);
|
|
15
|
+
} else {
|
|
16
|
+
element?.requestFullscreen();
|
|
17
|
+
setIsFullScreen(true);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!isFullScreenSupported) return;
|
|
22
|
+
const handleFullScreenChange = () => {
|
|
23
|
+
const fullScreen = !!document.fullscreenElement;
|
|
24
|
+
setIsFullScreen(fullScreen);
|
|
25
|
+
};
|
|
26
|
+
document.addEventListener("fullscreenchange", handleFullScreenChange);
|
|
27
|
+
return () => {
|
|
28
|
+
document.removeEventListener("fullscreenchange", handleFullScreenChange);
|
|
29
|
+
};
|
|
30
|
+
}, [isFullScreenSupported]);
|
|
31
|
+
return { toggleFullScreen, isFullScreen, isFullScreenSupported };
|
|
32
|
+
};
|
|
33
|
+
var useKeyPress = (targetKey, callback) => {
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const handleKeyDown = (event) => {
|
|
36
|
+
if (event.key === targetKey) {
|
|
37
|
+
callback(event);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
41
|
+
return () => {
|
|
42
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
43
|
+
};
|
|
44
|
+
}, [targetKey, callback]);
|
|
45
|
+
};
|
|
46
|
+
var isBrowser = typeof window !== "undefined";
|
|
47
|
+
var getInitialState = (query, defaultState) => {
|
|
48
|
+
if (defaultState !== void 0) {
|
|
49
|
+
return defaultState;
|
|
50
|
+
}
|
|
51
|
+
if (isBrowser) {
|
|
52
|
+
return window.matchMedia(query).matches;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
};
|
|
56
|
+
var useMedia = (query, defaultState) => {
|
|
57
|
+
const [state, setState] = useState(getInitialState(query, defaultState));
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
let mounted = true;
|
|
60
|
+
const mql = window.matchMedia(query);
|
|
61
|
+
const onChange = () => {
|
|
62
|
+
if (!mounted) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
setState(!!mql.matches);
|
|
66
|
+
};
|
|
67
|
+
mql.addEventListener("change", onChange);
|
|
68
|
+
setState(mql.matches);
|
|
69
|
+
return () => {
|
|
70
|
+
mounted = false;
|
|
71
|
+
mql.removeEventListener("change", onChange);
|
|
72
|
+
};
|
|
73
|
+
}, [query]);
|
|
74
|
+
return state;
|
|
75
|
+
};
|
|
76
|
+
function isFireFox() {
|
|
77
|
+
return getBrowser()?.name === "Firefox";
|
|
78
|
+
}
|
|
79
|
+
function isChromiumBased() {
|
|
80
|
+
return getBrowser()?.name === "Chrome";
|
|
81
|
+
}
|
|
82
|
+
function isSafari() {
|
|
83
|
+
return getBrowser()?.name === "Safari";
|
|
84
|
+
}
|
|
85
|
+
function isLocal(p) {
|
|
86
|
+
return p instanceof LocalParticipant;
|
|
87
|
+
}
|
|
88
|
+
function isMacintosh() {
|
|
89
|
+
return navigator.platform.indexOf("Mac") > -1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/sounds.ts
|
|
93
|
+
var SOUND_PATHS = {
|
|
94
|
+
chatMessage: "/sounds/chat-message.wav",
|
|
95
|
+
handRaise: "/sounds/hand-raise.wav",
|
|
96
|
+
userJoin: "/sounds/user-join-to-call.mp3",
|
|
97
|
+
userLeft: "/sounds/user-left-from-call.mp3"
|
|
98
|
+
};
|
|
99
|
+
var JOIN_LEAVE_PLAY_MS = 500;
|
|
100
|
+
var trimTimeouts = /* @__PURE__ */ new Map();
|
|
101
|
+
var soundCache = /* @__PURE__ */ new Map();
|
|
102
|
+
var getAudio = (soundType) => {
|
|
103
|
+
if (!soundCache.has(soundType)) {
|
|
104
|
+
const audio = new Audio(SOUND_PATHS[soundType]);
|
|
105
|
+
audio.preload = "auto";
|
|
106
|
+
soundCache.set(soundType, audio);
|
|
107
|
+
}
|
|
108
|
+
return soundCache.get(soundType);
|
|
109
|
+
};
|
|
110
|
+
var clearJoinLeaveTrim = (soundType) => {
|
|
111
|
+
const id = trimTimeouts.get(soundType);
|
|
112
|
+
if (id !== void 0) {
|
|
113
|
+
clearTimeout(id);
|
|
114
|
+
trimTimeouts.delete(soundType);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var scheduleJoinLeaveTrim = (audio, soundType) => {
|
|
118
|
+
clearJoinLeaveTrim(soundType);
|
|
119
|
+
const id = setTimeout(() => {
|
|
120
|
+
try {
|
|
121
|
+
audio.pause();
|
|
122
|
+
audio.currentTime = 0;
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
trimTimeouts.delete(soundType);
|
|
126
|
+
}, JOIN_LEAVE_PLAY_MS);
|
|
127
|
+
trimTimeouts.set(soundType, id);
|
|
128
|
+
};
|
|
129
|
+
var playSound = (soundType, volume = 1) => {
|
|
130
|
+
try {
|
|
131
|
+
const audio = getAudio(soundType);
|
|
132
|
+
if (soundType === "userJoin" || soundType === "userLeft") {
|
|
133
|
+
clearJoinLeaveTrim(soundType);
|
|
134
|
+
}
|
|
135
|
+
const clampedVolume = Math.max(0, Math.min(1, volume));
|
|
136
|
+
audio.volume = clampedVolume;
|
|
137
|
+
audio.currentTime = 0;
|
|
138
|
+
void audio.play().then(() => {
|
|
139
|
+
if (soundType === "userJoin" || soundType === "userLeft") {
|
|
140
|
+
scheduleJoinLeaveTrim(audio, soundType);
|
|
141
|
+
}
|
|
142
|
+
}).catch((error) => {
|
|
143
|
+
console.warn(`\u26A0\uFE0F Failed to play sound ${soundType}:`, error);
|
|
144
|
+
});
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error(`\u274C Error playing sound ${soundType}:`, error);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// src/noiseCancellationTelemetry.ts
|
|
151
|
+
var NOISE_CANCELLATION_EVENTS = {
|
|
152
|
+
MODE_CHANGED: "noise_cancellation_mode_changed",
|
|
153
|
+
APPLY_FAILED: "noise_cancellation_apply_failed",
|
|
154
|
+
FALLBACK_TO_WEBRTC: "noise_cancellation_fallback_to_webrtc",
|
|
155
|
+
KRISP_UNSUPPORTED_BROWSER: "noise_cancellation_krisp_unsupported_browser"
|
|
156
|
+
};
|
|
157
|
+
function getUmami() {
|
|
158
|
+
if (typeof window === "undefined") return void 0;
|
|
159
|
+
const win = window;
|
|
160
|
+
return win.umami;
|
|
161
|
+
}
|
|
162
|
+
function trackNoiseCancellationEvent(event, data) {
|
|
163
|
+
const umami = getUmami();
|
|
164
|
+
if (umami) {
|
|
165
|
+
umami.track(event, data);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { NOISE_CANCELLATION_EVENTS, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia };
|
|
170
|
+
//# sourceMappingURL=index.mjs.map
|
|
171
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/useFullScreen.ts","../src/useKeyPress.ts","../src/useMedia.ts","../src/livekit.ts","../src/sounds.ts","../src/noiseCancellationTelemetry.ts"],"names":["useEffect","useState"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useEffect } from 'react';\n\nexport const useFullScreen = (containerId: string) => {\n const [isFullScreen, setIsFullScreen] = useState(false);\n\n const hasStandardFullscreen =\n typeof document !== 'undefined' &&\n document.fullscreenEnabled &&\n typeof HTMLElement.prototype.requestFullscreen === 'function';\n\n const isFullScreenSupported = hasStandardFullscreen;\n\n const toggleFullScreen = () => {\n const element = document.getElementById(containerId);\n const fullScreen = document.fullscreenElement;\n\n if (fullScreen) {\n document.exitFullscreen();\n setIsFullScreen(false);\n } else {\n element?.requestFullscreen();\n setIsFullScreen(true);\n }\n };\n\n useEffect(() => {\n if (!isFullScreenSupported) return;\n\n const handleFullScreenChange = () => {\n const fullScreen = !!document.fullscreenElement;\n setIsFullScreen(fullScreen);\n };\n\n document.addEventListener('fullscreenchange', handleFullScreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullScreenChange);\n };\n }, [isFullScreenSupported]);\n\n return { toggleFullScreen, isFullScreen, isFullScreenSupported };\n};\n","import { useEffect } from 'react';\n\nexport const useKeyPress = (targetKey: string, callback: (event: KeyboardEvent) => void) => {\n useEffect(() => {\n // console.log(`Added listener for ${targetKey}`);\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === targetKey) {\n callback(event);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => {\n // console.log(`Removed listener for ${targetKey}`);\n window.removeEventListener('keydown', handleKeyDown);\n };\n }, [targetKey, callback]);\n};\n","import { useEffect, useState } from 'react';\n\nconst isBrowser = typeof window !== 'undefined';\n\nconst getInitialState = (query: string, defaultState?: boolean) => {\n // Prevent a React hydration mismatch when a default value\n // is provided by not defaulting to window.matchMedia(query).matches.\n if (defaultState !== undefined) {\n return defaultState;\n }\n\n if (isBrowser) {\n return window.matchMedia(query).matches;\n }\n\n return false;\n};\n\nexport const useMedia = (query: string, defaultState?: boolean) => {\n const [state, setState] = useState(getInitialState(query, defaultState));\n\n useEffect(() => {\n let mounted = true;\n const mql = window.matchMedia(query);\n const onChange = () => {\n if (!mounted) {\n return;\n }\n setState(!!mql.matches);\n };\n\n mql.addEventListener('change', onChange);\n setState(mql.matches);\n\n return () => {\n mounted = false;\n mql.removeEventListener('change', onChange);\n };\n }, [query]);\n\n return state;\n};\n","import { getBrowser, LocalParticipant, LogLevel, Participant, setLogLevel } from 'livekit-client';\n\nexport const silenceLiveKitLogs = (shouldSilenceLogs: boolean) => {\n setLogLevel(shouldSilenceLogs ? LogLevel.silent : LogLevel.debug);\n};\n\nexport function isFireFox(): boolean {\n return getBrowser()?.name === 'Firefox';\n}\n\nexport function isChromiumBased(): boolean {\n return getBrowser()?.name === 'Chrome';\n}\n\nexport function isSafari(): boolean {\n return getBrowser()?.name === 'Safari';\n}\n\nexport function isLocal(p: Participant) {\n return p instanceof LocalParticipant;\n}\n\nexport function isMacintosh() {\n return navigator.platform.indexOf('Mac') > -1;\n}\n","/**\n * Утилита для воспроизведения звуков уведомлений в ВКС\n */\n\nconst SOUND_PATHS = {\n chatMessage: '/sounds/chat-message.wav',\n handRaise: '/sounds/hand-raise.wav',\n userJoin: '/sounds/user-join-to-call.mp3',\n userLeft: '/sounds/user-left-from-call.mp3',\n} as const;\n\ntype SoundType = keyof typeof SOUND_PATHS;\n\n/** Для join/left — воспроизводим только начало файла (мс). */\nconst JOIN_LEAVE_PLAY_MS = 500;\n\ntype TrimmedSoundType = Extract<SoundType, 'userJoin' | 'userLeft'>;\nconst trimTimeouts = new Map<TrimmedSoundType, ReturnType<typeof setTimeout>>();\n\n// Кэш для Audio объектов, чтобы не создавать их каждый раз\nconst soundCache = new Map<SoundType, HTMLAudioElement>();\n\n/**\n * Получает или создает Audio объект для звука\n */\nconst getAudio = (soundType: SoundType): HTMLAudioElement => {\n if (!soundCache.has(soundType)) {\n const audio = new Audio(SOUND_PATHS[soundType]);\n audio.preload = 'auto';\n soundCache.set(soundType, audio);\n }\n return soundCache.get(soundType)!;\n};\n\nconst clearJoinLeaveTrim = (soundType: TrimmedSoundType) => {\n const id = trimTimeouts.get(soundType);\n if (id !== undefined) {\n clearTimeout(id);\n trimTimeouts.delete(soundType);\n }\n};\n\nconst scheduleJoinLeaveTrim = (audio: HTMLAudioElement, soundType: TrimmedSoundType) => {\n clearJoinLeaveTrim(soundType);\n const id = setTimeout(() => {\n try {\n audio.pause();\n audio.currentTime = 0;\n } catch {\n /* ignore */\n }\n trimTimeouts.delete(soundType);\n }, JOIN_LEAVE_PLAY_MS);\n trimTimeouts.set(soundType, id);\n};\n\n/**\n * Воспроизводит звук с указанной громкостью\n * @param soundType - тип звука (chatMessage или handRaise)\n * @param volume - громкость от 0 до 1 (0 = беззвучно, 1 = максимальная громкость)\n */\nexport const playSound = (soundType: SoundType, volume: number = 1): void => {\n try {\n const audio = getAudio(soundType);\n\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n clearJoinLeaveTrim(soundType);\n }\n\n // Устанавливаем громкость (от 0 до 1)\n const clampedVolume = Math.max(0, Math.min(1, volume));\n audio.volume = clampedVolume;\n\n // Сбрасываем позицию на начало и воспроизводим\n audio.currentTime = 0;\n void audio\n .play()\n .then(() => {\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n scheduleJoinLeaveTrim(audio, soundType);\n }\n })\n .catch((error) => {\n // Игнорируем ошибки воспроизведения (например, если пользователь не взаимодействовал со страницей)\n console.warn(`⚠️ Failed to play sound ${soundType}:`, error);\n });\n } catch (error) {\n console.error(`❌ Error playing sound ${soundType}:`, error);\n }\n};\n","/** События телеметрии шумоподавления для umami. */\nexport const NOISE_CANCELLATION_EVENTS = {\n MODE_CHANGED: 'noise_cancellation_mode_changed',\n APPLY_FAILED: 'noise_cancellation_apply_failed',\n FALLBACK_TO_WEBRTC: 'noise_cancellation_fallback_to_webrtc',\n KRISP_UNSUPPORTED_BROWSER: 'noise_cancellation_krisp_unsupported_browser',\n} as const;\n\ntype NoiseCancellationEvent =\n (typeof NOISE_CANCELLATION_EVENTS)[keyof typeof NOISE_CANCELLATION_EVENTS];\n\nfunction getUmami(): { track: (name: string, data?: Record<string, string>) => void } | undefined {\n if (typeof window === 'undefined') return undefined;\n const win = window as Window & {\n umami?: { track: (name: string, data?: Record<string, string>) => void };\n };\n return win.umami;\n}\n\nexport function trackNoiseCancellationEvent(\n event: NoiseCancellationEvent,\n data?: Record<string, string>,\n): void {\n const umami = getUmami();\n if (umami) {\n umami.track(event, data);\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xipkg/calls-utils",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"development": "./index.ts",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"default": "./dist/index.mjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"lint": "eslint \"**/*.{ts,tsx}\"",
|
|
16
|
+
"dev": "tsup --watch",
|
|
17
|
+
"build": "tsup"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@tanstack/react-query-devtools": "5.73.3",
|
|
21
|
+
"@eslint/js": "^9.19.0",
|
|
22
|
+
"common.typescript": "*",
|
|
23
|
+
"@types/node": "^20.3.1",
|
|
24
|
+
"@types/react": "^19.0.2",
|
|
25
|
+
"@types/react-dom": "^19.0.2",
|
|
26
|
+
"@xipkg/eslint": "3.2.0",
|
|
27
|
+
"@xipkg/typescript": "latest",
|
|
28
|
+
"common.eslint": "*",
|
|
29
|
+
"eslint": "^9.19.0",
|
|
30
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
31
|
+
"eslint-plugin-react-refresh": "^0.4.18",
|
|
32
|
+
"globals": "^15.14.0",
|
|
33
|
+
"typescript": "~5.7.2",
|
|
34
|
+
"typescript-eslint": "^8.22.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": "19"
|
|
38
|
+
},
|
|
39
|
+
"description": "utils",
|
|
40
|
+
"author": "xi.effect",
|
|
41
|
+
"main": "./dist/index.mjs",
|
|
42
|
+
"types": "./dist/index.d.ts",
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
],
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|