zerorate-voip-sdk 0.1.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/README.md +131 -0
- package/dist/adapters/react/useVoIP.d.ts +14 -0
- package/dist/core/APIClient.d.ts +16 -0
- package/dist/core/AudioManager.d.ts +16 -0
- package/dist/core/CallManager.d.ts +32 -0
- package/dist/core/EventEmitter.d.ts +9 -0
- package/dist/core/LiveKitManager.d.ts +17 -0
- package/dist/core/VoIPClient.d.ts +43 -0
- package/dist/core/WebSocketManager.d.ts +26 -0
- package/dist/index.d.ts +9 -0
- package/dist/types.d.ts +30 -0
- package/dist/ui/UIManager.d.ts +15 -0
- package/dist/ui/components/ActiveCall.d.ts +2 -0
- package/dist/ui/components/IncomingCall.d.ts +2 -0
- package/dist/ui/components/OutgoingCall.d.ts +2 -0
- package/dist/voip-sdk.css +140 -0
- package/dist/voip-sdk.esm.js +1 -0
- package/dist/voip-sdk.js +1157 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Zerorate VoIP SDK
|
|
2
|
+
|
|
3
|
+
# [BEFORE YOU INSTALL: IMPORTANT NOTICE]: This SDK is customly built to be use by merchants on the freepass.africa platform.
|
|
4
|
+
|
|
5
|
+
Production-ready, framework-agnostic JavaScript/TypeScript SDK to add in-app VoIP calling to any web application. Handles WebSocket signaling, LiveKit WebRTC integration, call state management, and provides a simple API with optional pre-built UI and framework adapters.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @freepass/voip-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start (ESM)
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import ZerorateVoIPClient from "@freepass/voip-sdk";
|
|
17
|
+
import "@freepass/voip-sdk/dist/voip-sdk.css";
|
|
18
|
+
|
|
19
|
+
const voip = new ZerorateVoIPClient({
|
|
20
|
+
backendUrl: "https://api.example.com",
|
|
21
|
+
wsUrl: "wss://api.example.com/ws",
|
|
22
|
+
userId: "user_123",
|
|
23
|
+
userName: "John Doe",
|
|
24
|
+
authToken: "your-jwt",
|
|
25
|
+
enableUI: true,
|
|
26
|
+
theme: "light",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await voip.connect();
|
|
30
|
+
|
|
31
|
+
voip.on("incoming_call", (data) => {
|
|
32
|
+
// show incoming UI or auto accept
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await voip.initiateCall("user_456", "Jane Smith", { isVideo: false });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start (UMD)
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<script src="https://cdn.example.com/voip-sdk.min.js"></script>
|
|
42
|
+
<link rel="stylesheet" href="https://cdn.example.com/voip-sdk.css" />
|
|
43
|
+
<script>
|
|
44
|
+
const client = new ZerorateVoIPSDK.ZerorateVoIPClient({
|
|
45
|
+
backendUrl: "https://api.example.com",
|
|
46
|
+
wsUrl: "wss://api.example.com/ws",
|
|
47
|
+
userId: "user_123",
|
|
48
|
+
userName: "John Doe",
|
|
49
|
+
});
|
|
50
|
+
client.connect();
|
|
51
|
+
client.initiateCall("user_456", "Jane Smith", { isVideo: false });
|
|
52
|
+
</script>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
- backendUrl: string
|
|
58
|
+
- wsUrl: string
|
|
59
|
+
- userId: string
|
|
60
|
+
- userName: string
|
|
61
|
+
- authToken?: string
|
|
62
|
+
- enableUI?: boolean
|
|
63
|
+
- theme?: 'light' | 'dark' | 'auto'
|
|
64
|
+
- ringTones?: { incoming?: string; outgoing?: string }
|
|
65
|
+
- autoReconnect?: boolean
|
|
66
|
+
- debug?: boolean
|
|
67
|
+
|
|
68
|
+
## Public API
|
|
69
|
+
|
|
70
|
+
- connect(): Promise<void>
|
|
71
|
+
- disconnect(): void
|
|
72
|
+
- isConnected(): boolean
|
|
73
|
+
- initiateCall(userId: string, userName: string, options?: { isVideo?: boolean }): Promise<void>
|
|
74
|
+
- acceptCall(callId: string): Promise<void>
|
|
75
|
+
- declineCall(callId: string): Promise<void>
|
|
76
|
+
- endCall(callId: string): Promise<void>
|
|
77
|
+
- toggleMicrophone(): Promise<boolean>
|
|
78
|
+
- toggleVideo(): Promise<boolean>
|
|
79
|
+
- setMicrophoneEnabled(enabled: boolean): Promise<void>
|
|
80
|
+
- setVideoEnabled(enabled: boolean): Promise<void>
|
|
81
|
+
- getCallState(): 'idle' | 'calling' | 'ringing' | 'connecting' | 'connected' | 'ending'
|
|
82
|
+
- getActiveCall(): CallData | null
|
|
83
|
+
- isInCall(): boolean
|
|
84
|
+
- on(event, callback): void
|
|
85
|
+
- off(event, callback): void
|
|
86
|
+
- once(event, callback): void
|
|
87
|
+
- showUI(): void
|
|
88
|
+
- hideUI(): void
|
|
89
|
+
- setTheme(theme): void
|
|
90
|
+
- destroy(): void
|
|
91
|
+
|
|
92
|
+
## Events
|
|
93
|
+
|
|
94
|
+
- connected, disconnected, reconnecting, error
|
|
95
|
+
- incoming_call, call_accepted, call_declined, call_missed, call_accepted, call_ended, call:error
|
|
96
|
+
- media:microphone-changed, media:video-changed
|
|
97
|
+
- state:changed
|
|
98
|
+
|
|
99
|
+
## Optional UI Components
|
|
100
|
+
|
|
101
|
+
- Include CSS: `import '@freepass/voip-sdk/dist/voip-sdk.css'`
|
|
102
|
+
- Use UIManager to render:
|
|
103
|
+
- showIncomingCall(callData)
|
|
104
|
+
- showOutgoingCall(callData)
|
|
105
|
+
- showActiveCall(callData)
|
|
106
|
+
- hideAllModals(), show(), hide(), setTheme(theme)
|
|
107
|
+
|
|
108
|
+
## React Adapter
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import { useVoIP } from "@freepass/voip-sdk/src/adapters/react/useVoIP";
|
|
112
|
+
const { isConnected, callState, initiateCall } = useVoIP(config);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Vue Adapter
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
import { useVoIP } from "@freepass/voip-sdk/src/adapters/vue/useVoIP";
|
|
119
|
+
const { state, initiateCall } = useVoIP(config);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Examples
|
|
123
|
+
|
|
124
|
+
- Vanilla: see `examples/vanilla/index.html`
|
|
125
|
+
- React: see `examples/react/App.tsx`
|
|
126
|
+
|
|
127
|
+
## Build Scripts
|
|
128
|
+
|
|
129
|
+
- Build: `npm run build` (UMD + ESM + CSS copy)
|
|
130
|
+
- Typecheck: `npm run typecheck`
|
|
131
|
+
- Lint: `npm run lint`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ZerorateVoIPClient } from "../../core/VoIPClient";
|
|
2
|
+
export declare function useVoIP(config: any): {
|
|
3
|
+
client: ZerorateVoIPClient | null;
|
|
4
|
+
isConnected: boolean;
|
|
5
|
+
callState: string;
|
|
6
|
+
activeCall: any;
|
|
7
|
+
incomingCall: any;
|
|
8
|
+
initiateCall: (userId: string, userName: string, options?: any) => Promise<void> | undefined;
|
|
9
|
+
acceptCall: () => Promise<void> | undefined;
|
|
10
|
+
declineCall: () => Promise<void> | undefined;
|
|
11
|
+
endCall: () => Promise<void> | undefined;
|
|
12
|
+
toggleMicrophone: () => Promise<boolean> | undefined;
|
|
13
|
+
toggleVideo: () => Promise<boolean> | undefined;
|
|
14
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EventEmitter } from "./EventEmitter";
|
|
2
|
+
export declare class APIClient extends EventEmitter {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private authToken?;
|
|
5
|
+
private timeoutMs;
|
|
6
|
+
private retries;
|
|
7
|
+
constructor(baseUrl: string, authToken?: string);
|
|
8
|
+
setAuthToken(token?: string): void;
|
|
9
|
+
initiateCall(data: any): Promise<any>;
|
|
10
|
+
acceptCall(callId: string, userId: string): Promise<any>;
|
|
11
|
+
declineCall(callId: string, userId: string): Promise<any>;
|
|
12
|
+
endCall(callId: string, userId: string): Promise<any>;
|
|
13
|
+
getCallStatus(callId: string): Promise<any>;
|
|
14
|
+
private request;
|
|
15
|
+
private safeJson;
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class AudioManager {
|
|
2
|
+
private incoming?;
|
|
3
|
+
private outgoing?;
|
|
4
|
+
private vol;
|
|
5
|
+
private playing;
|
|
6
|
+
constructor(ringTones?: {
|
|
7
|
+
incoming?: string;
|
|
8
|
+
outgoing?: string;
|
|
9
|
+
});
|
|
10
|
+
preloadRingtones(): void;
|
|
11
|
+
playIncoming(): Promise<void>;
|
|
12
|
+
playOutgoing(): Promise<void>;
|
|
13
|
+
stop(): void;
|
|
14
|
+
setVolume(volume: number): void;
|
|
15
|
+
isPlaying(): boolean;
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EventEmitter } from "./EventEmitter";
|
|
2
|
+
import { APIClient } from "./APIClient";
|
|
3
|
+
import { WebSocketManager } from "./WebSocketManager";
|
|
4
|
+
import { LiveKitManager } from "./LiveKitManager";
|
|
5
|
+
import { AudioManager } from "./AudioManager";
|
|
6
|
+
import type { CallState, CallData } from "../types";
|
|
7
|
+
export declare class CallManager extends EventEmitter {
|
|
8
|
+
private api;
|
|
9
|
+
private ws;
|
|
10
|
+
private livekit;
|
|
11
|
+
private audio;
|
|
12
|
+
private userId;
|
|
13
|
+
private userName;
|
|
14
|
+
private state;
|
|
15
|
+
private currentCall;
|
|
16
|
+
constructor(apiClient: APIClient, webSocketManager: WebSocketManager, liveKitManager: LiveKitManager, audioManager: AudioManager, userId: string, userName: string);
|
|
17
|
+
getCallState(): CallState;
|
|
18
|
+
getCurrentCall(): CallData | null;
|
|
19
|
+
initiateCall(calleeId: string, calleeName: string, isVideo: boolean): Promise<void>;
|
|
20
|
+
acceptCall(callId: string): Promise<void>;
|
|
21
|
+
declineCall(callId: string): Promise<void>;
|
|
22
|
+
endCall(callId: string): Promise<void>;
|
|
23
|
+
private handleIncomingCall;
|
|
24
|
+
private handleAccepted;
|
|
25
|
+
private handleDeclined;
|
|
26
|
+
private handleMissed;
|
|
27
|
+
private handleEnded;
|
|
28
|
+
private handleSocketMessage;
|
|
29
|
+
private setState;
|
|
30
|
+
private finishCall;
|
|
31
|
+
private cleanupCall;
|
|
32
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Handler = (data?: any) => void;
|
|
2
|
+
export declare class EventEmitter {
|
|
3
|
+
private handlers;
|
|
4
|
+
on(event: string, callback: Handler): void;
|
|
5
|
+
off(event: string, callback: Handler): void;
|
|
6
|
+
once(event: string, callback: Handler): void;
|
|
7
|
+
emit(event: string, data?: any): void;
|
|
8
|
+
}
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EventEmitter } from "./EventEmitter";
|
|
2
|
+
export declare class LiveKitManager extends EventEmitter {
|
|
3
|
+
private room;
|
|
4
|
+
connectToRoom(livekitUrl: string, token: string, options?: any): Promise<void>;
|
|
5
|
+
disconnectFromRoom(): void;
|
|
6
|
+
enableMicrophone(): Promise<void>;
|
|
7
|
+
disableMicrophone(): Promise<void>;
|
|
8
|
+
toggleMicrophone(): Promise<boolean>;
|
|
9
|
+
enableVideo(): Promise<void>;
|
|
10
|
+
disableVideo(): Promise<void>;
|
|
11
|
+
toggleVideo(): Promise<boolean>;
|
|
12
|
+
getLocalTracks(): any[];
|
|
13
|
+
getRemoteTracks(): any[];
|
|
14
|
+
getRoom(): any;
|
|
15
|
+
attachTrack(track: any, element: HTMLElement): void;
|
|
16
|
+
detachTrack(track: any, element: HTMLElement): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { VoIPConfig, CallState, CallData, VoIPEvent, Theme } from "../types";
|
|
2
|
+
export declare class ZerorateVoIPClient {
|
|
3
|
+
private config;
|
|
4
|
+
private emitter;
|
|
5
|
+
private ws;
|
|
6
|
+
private api;
|
|
7
|
+
private livekit;
|
|
8
|
+
private audio;
|
|
9
|
+
private calls;
|
|
10
|
+
private microphoneEnabled;
|
|
11
|
+
private videoEnabled;
|
|
12
|
+
private ui?;
|
|
13
|
+
constructor(config: VoIPConfig);
|
|
14
|
+
private bindEvents;
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
disconnect(): void;
|
|
17
|
+
isConnected(): boolean;
|
|
18
|
+
initiateCall(userId: string, userName: string, options?: {
|
|
19
|
+
isVideo?: boolean;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
acceptCall(callId: string): Promise<void>;
|
|
22
|
+
declineCall(callId: string): Promise<void>;
|
|
23
|
+
endCall(callId: string): Promise<void>;
|
|
24
|
+
toggleMicrophone(): Promise<boolean>;
|
|
25
|
+
toggleVideo(): Promise<boolean>;
|
|
26
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
27
|
+
setVideoEnabled(enabled: boolean): Promise<void>;
|
|
28
|
+
getCallState(): CallState;
|
|
29
|
+
getActiveCall(): CallData | null;
|
|
30
|
+
isInCall(): boolean;
|
|
31
|
+
getLocalTracks(): any[];
|
|
32
|
+
getRemoteTracks(): any[];
|
|
33
|
+
attachTrack(track: any, element: HTMLElement): void;
|
|
34
|
+
detachTrack(track: any, element: HTMLElement): void;
|
|
35
|
+
on(event: VoIPEvent, callback: (data: any) => void): void;
|
|
36
|
+
off(event: VoIPEvent, callback: (data: any) => void): void;
|
|
37
|
+
once(event: VoIPEvent, callback: (data: any) => void): void;
|
|
38
|
+
showUI(): void;
|
|
39
|
+
hideUI(): void;
|
|
40
|
+
setTheme(theme: Theme): void;
|
|
41
|
+
destroy(): void;
|
|
42
|
+
}
|
|
43
|
+
export default ZerorateVoIPClient;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { EventEmitter } from "./EventEmitter";
|
|
2
|
+
type ConnectionState = "connecting" | "connected" | "disconnected" | "error";
|
|
3
|
+
export declare class WebSocketManager extends EventEmitter {
|
|
4
|
+
private url;
|
|
5
|
+
private authToken?;
|
|
6
|
+
private userId?;
|
|
7
|
+
private ws?;
|
|
8
|
+
private state;
|
|
9
|
+
private queue;
|
|
10
|
+
private heartbeatId?;
|
|
11
|
+
private pongTimeoutId?;
|
|
12
|
+
private reconnectAttempts;
|
|
13
|
+
private autoReconnect;
|
|
14
|
+
private debug;
|
|
15
|
+
constructor(wsUrl: string, authToken?: string, autoReconnect?: boolean, debug?: boolean, userId?: string);
|
|
16
|
+
getState(): ConnectionState;
|
|
17
|
+
isConnected(): boolean;
|
|
18
|
+
connect(): Promise<void>;
|
|
19
|
+
disconnect(): void;
|
|
20
|
+
send(message: any): void;
|
|
21
|
+
private flushQueue;
|
|
22
|
+
private startHeartbeat;
|
|
23
|
+
private stopHeartbeat;
|
|
24
|
+
private scheduleReconnect;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { ZerorateVoIPClient as default } from "./core/VoIPClient";
|
|
2
|
+
export { ZerorateVoIPClient } from "./core/VoIPClient";
|
|
3
|
+
export { EventEmitter } from "./core/EventEmitter";
|
|
4
|
+
export { WebSocketManager } from "./core/WebSocketManager";
|
|
5
|
+
export { APIClient } from "./core/APIClient";
|
|
6
|
+
export { LiveKitManager } from "./core/LiveKitManager";
|
|
7
|
+
export { AudioManager } from "./core/AudioManager";
|
|
8
|
+
export { CallManager } from "./core/CallManager";
|
|
9
|
+
export type { VoIPConfig, CallData, CallState, VoIPEvent, Theme, } from "./types";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type Theme = "light" | "dark" | "auto";
|
|
2
|
+
export interface VoIPConfig {
|
|
3
|
+
backendUrl: string;
|
|
4
|
+
wsUrl: string;
|
|
5
|
+
userId: string;
|
|
6
|
+
userName: string;
|
|
7
|
+
authToken?: string;
|
|
8
|
+
enableUI?: boolean;
|
|
9
|
+
theme?: Theme;
|
|
10
|
+
ringTones?: {
|
|
11
|
+
incoming?: string;
|
|
12
|
+
outgoing?: string;
|
|
13
|
+
};
|
|
14
|
+
autoReconnect?: boolean;
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface CallData {
|
|
18
|
+
callId: string;
|
|
19
|
+
roomName: string;
|
|
20
|
+
token: string;
|
|
21
|
+
livekitUrl: string;
|
|
22
|
+
participantId: string;
|
|
23
|
+
participantName: string;
|
|
24
|
+
calleeId?: string;
|
|
25
|
+
calleeName?: string;
|
|
26
|
+
isVideo: boolean;
|
|
27
|
+
startTime?: number;
|
|
28
|
+
}
|
|
29
|
+
export type CallState = "idle" | "calling" | "ringing" | "connecting" | "connected" | "ending";
|
|
30
|
+
export type VoIPEvent = "connected" | "disconnected" | "reconnecting" | "error" | "incoming_call" | "call_started" | "call_accepted" | "call_declined" | "call_missed" | "call_ended" | "call:error" | "media:microphone-changed" | "media:video-changed" | "state:changed";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ZerorateVoIPClient } from "../core/VoIPClient";
|
|
2
|
+
export declare class UIManager {
|
|
3
|
+
private client;
|
|
4
|
+
private root?;
|
|
5
|
+
constructor(voipClient: ZerorateVoIPClient);
|
|
6
|
+
show(): void;
|
|
7
|
+
hide(): void;
|
|
8
|
+
setTheme(theme: "light" | "dark" | "auto"): void;
|
|
9
|
+
showIncomingCall(callData: any): void;
|
|
10
|
+
showOutgoingCall(callData: any): void;
|
|
11
|
+
showActiveCall(callData: any): void;
|
|
12
|
+
hideAllModals(): void;
|
|
13
|
+
destroy(): void;
|
|
14
|
+
private ensureRoot;
|
|
15
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--voip-primary-color: #3b82f6;
|
|
3
|
+
--voip-success-color: #10b981;
|
|
4
|
+
--voip-danger-color: #ef4444;
|
|
5
|
+
--voip-background: #ffffff;
|
|
6
|
+
--voip-text-color: #1f2937;
|
|
7
|
+
--voip-overlay: rgba(0, 0, 0, 0.65);
|
|
8
|
+
--voip-border-radius: 12px;
|
|
9
|
+
--voip-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.voip-overlay {
|
|
13
|
+
position: fixed;
|
|
14
|
+
inset: 0;
|
|
15
|
+
background: var(--voip-overlay);
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
z-index: 9999;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.voip-card {
|
|
23
|
+
background: var(--voip-background);
|
|
24
|
+
color: var(--voip-text-color);
|
|
25
|
+
border-radius: var(--voip-border-radius);
|
|
26
|
+
padding: 24px;
|
|
27
|
+
width: 90%;
|
|
28
|
+
max-width: 420px;
|
|
29
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.voip-title {
|
|
34
|
+
font-family: var(--voip-font-family);
|
|
35
|
+
font-size: 20px;
|
|
36
|
+
margin-bottom: 8px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.voip-subtitle {
|
|
40
|
+
font-size: 14px;
|
|
41
|
+
opacity: 0.8;
|
|
42
|
+
margin-bottom: 16px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.voip-actions {
|
|
46
|
+
display: flex;
|
|
47
|
+
gap: 12px;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.voip-btn {
|
|
52
|
+
border: none;
|
|
53
|
+
border-radius: var(--voip-border-radius);
|
|
54
|
+
padding: 10px 16px;
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.voip-btn-accept {
|
|
60
|
+
background: var(--voip-success-color);
|
|
61
|
+
color: #fff;
|
|
62
|
+
}
|
|
63
|
+
.voip-btn-decline {
|
|
64
|
+
background: var(--voip-danger-color);
|
|
65
|
+
color: #fff;
|
|
66
|
+
}
|
|
67
|
+
.voip-btn-cancel {
|
|
68
|
+
background: var(--voip-danger-color);
|
|
69
|
+
color: #fff;
|
|
70
|
+
}
|
|
71
|
+
.voip-btn-primary {
|
|
72
|
+
background: var(--voip-primary-color);
|
|
73
|
+
color: #fff;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.voip-video-container {
|
|
77
|
+
position: relative;
|
|
78
|
+
width: 100%;
|
|
79
|
+
max-width: 960px;
|
|
80
|
+
aspect-ratio: 16 / 9;
|
|
81
|
+
background: #000;
|
|
82
|
+
border-radius: var(--voip-border-radius);
|
|
83
|
+
overflow: hidden;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.voip-local-pip {
|
|
87
|
+
position: absolute;
|
|
88
|
+
width: 180px;
|
|
89
|
+
height: 120px;
|
|
90
|
+
bottom: 16px;
|
|
91
|
+
right: 16px;
|
|
92
|
+
border-radius: var(--voip-border-radius);
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.voip-controls {
|
|
98
|
+
display: flex;
|
|
99
|
+
gap: 10px;
|
|
100
|
+
justify-content: center;
|
|
101
|
+
margin-top: 16px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Enhanced layout elements */
|
|
105
|
+
.voip-header {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
gap: 12px;
|
|
109
|
+
margin-bottom: 16px;
|
|
110
|
+
text-align: left;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.voip-avatar {
|
|
114
|
+
width: 56px;
|
|
115
|
+
height: 56px;
|
|
116
|
+
border-radius: 50%;
|
|
117
|
+
background: #111827;
|
|
118
|
+
color: #ffffff;
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
justify-content: center;
|
|
122
|
+
font-weight: 700;
|
|
123
|
+
font-family: var(--voip-font-family);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.voip-meta {
|
|
127
|
+
display: flex;
|
|
128
|
+
flex-direction: column;
|
|
129
|
+
align-items: flex-start;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.voip-timer {
|
|
133
|
+
font-variant-numeric: tabular-nums;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@media (max-width: 420px) {
|
|
137
|
+
.voip-btn {
|
|
138
|
+
padding: 10px 12px;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class t{constructor(){this.handlers=new Map}on(t,e){this.handlers.has(t)||this.handlers.set(t,new Set),this.handlers.get(t).add(e)}off(t,e){const i=this.handlers.get(t);i&&(i.delete(e),0===i.size&&this.handlers.delete(t))}once(t,e){const i=n=>{this.off(t,i),e(n)};this.on(t,i)}emit(t,e){const i=this.handlers.get(t);if(i)for(const t of Array.from(i))t(e)}}class e extends t{constructor(t,e,i=!0,n=!1,s){super(),this.state="disconnected",this.queue=[],this.reconnectAttempts=0,this.url=t,this.authToken=e,this.autoReconnect=i,this.debug=n,this.userId=s}getState(){return this.state}isConnected(){return"connected"===this.state}async connect(){if(!this.ws||"connecting"!==this.state&&"connected"!==this.state){this.state="connecting";try{this.ws=new WebSocket(this.url),this.ws.onopen=()=>{this.state="connected",this.emit("connected"),this.userId&&this.authToken?this.send({type:"authenticate",userId:this.userId,token:this.authToken}):this.authToken&&this.send({type:"authenticate",token:this.authToken}),this.flushQueue(),this.startHeartbeat(),this.reconnectAttempts=0},this.ws.onmessage=t=>{if("string"==typeof t.data)try{const e=JSON.parse(t.data);this.emit("message",e)}catch(e){this.emit("message",t.data)}else this.emit("message",t.data)},this.ws.onerror=()=>{this.state="error",this.emit("error",{type:"network"})},this.ws.onclose=()=>{this.stopHeartbeat();const t="connected"===this.state;this.state="disconnected",this.emit("disconnected"),this.autoReconnect&&this.scheduleReconnect(t)}}catch(t){this.state="error",this.emit("error",{type:"network"})}}}disconnect(){if(this.autoReconnect=!1,this.stopHeartbeat(),this.ws&&("connecting"===this.state||"connected"===this.state))try{this.ws.close()}catch(t){}this.ws=void 0,this.state="disconnected",this.emit("disconnected")}send(t){const e="string"==typeof t?t:JSON.stringify(t);if(this.ws&&"connected"===this.state)try{this.ws.send(e)}catch(t){this.queue.push(e)}else this.queue.push(e)}flushQueue(){if(this.ws&&"connected"===this.state)for(const t of this.queue.splice(0))try{this.ws.send(t)}catch(t){}}startHeartbeat(){this.stopHeartbeat(),this.heartbeatId=window.setInterval(()=>{if(this.ws&&"connected"===this.state)try{this.ws.send(JSON.stringify({type:"heartbeat"})),this.pongTimeoutId&&window.clearTimeout(this.pongTimeoutId),this.pongTimeoutId=window.setTimeout(()=>{try{this.ws?.close()}catch(t){}},5e3)}catch(t){}},3e4)}stopHeartbeat(){this.heartbeatId&&(window.clearInterval(this.heartbeatId),this.heartbeatId=void 0),this.pongTimeoutId&&(window.clearTimeout(this.pongTimeoutId),this.pongTimeoutId=void 0)}scheduleReconnect(t){this.reconnectAttempts+=1;const e=t?1:2,i=Math.min(3e4,1e3*Math.pow(2,this.reconnectAttempts-1)*e);this.emit("reconnecting",{attempt:this.reconnectAttempts,delay:i}),window.setTimeout(()=>{this.autoReconnect&&this.connect()},i)}}class i extends t{constructor(t,e){super(),this.timeoutMs=1e4,this.retries=3,this.baseUrl=t.replace(/\/+$/,""),this.authToken=e}setAuthToken(t){this.authToken=t}async initiateCall(t){return this.request("/api/webrtc/v1/calls/initiate","POST",t)}async acceptCall(t,e){return this.request(`/api/webrtc/v1/calls/${encodeURIComponent(t)}/accept`,"POST",{userId:e})}async declineCall(t,e){return this.request(`/api/webrtc/v1/calls/${encodeURIComponent(t)}/decline`,"POST",{userId:e})}async endCall(t,e){return this.request(`/api/webrtc/v1/calls/${encodeURIComponent(t)}/end`,"POST",{userId:e})}async getCallStatus(t){return this.request(`/api/webrtc/v1/calls/${encodeURIComponent(t)}`,"GET")}async request(t,e,i){const n=`${this.baseUrl}${t}`;let s,a=0;for(;a<this.retries;){a+=1;const t=new AbortController,o=window.setTimeout(()=>t.abort(),this.timeoutMs);try{const a=await fetch(n,{method:e,headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}},body:i?JSON.stringify(i):void 0,signal:t.signal});if(window.clearTimeout(o),a.ok){return await this.safeJson(a)}{const t=await this.safeJson(a);s={status:a.status,data:t},this.emit("error",{type:"network",message:"http_error",originalError:s})}}catch(t){window.clearTimeout(o),s=t,this.emit("error",{type:"network",message:"fetch_error",originalError:t})}}throw s??new Error("request_failed")}async safeJson(t){try{return await t.json()}catch(t){return null}}}class n extends t{async connectToRoom(t,e,i){const n=await import("livekit-client");console.log(t,e),console.log(n);const s=n.Room,a=n.RoomEvent;this.room=new s(i),await this.room.connect(t,e),this.room.on(a.TrackSubscribed,(t,e,i)=>{this.emit("media:track-added",{participantId:i.sid,trackType:t.kind})}),this.room.on(a.TrackUnsubscribed,(t,e,i)=>{this.emit("media:track-removed",{participantId:i.sid,trackType:t.kind})}),this.room.on(a.Disconnected,()=>{this.emit("disconnected")})}disconnectFromRoom(){this.room&&(this.room.disconnect(),this.room=void 0)}async enableMicrophone(){this.room&&(await this.room.localParticipant.setMicrophoneEnabled(!0),this.emit("media:microphone-changed",{enabled:!0}))}async disableMicrophone(){this.room&&(await this.room.localParticipant.setMicrophoneEnabled(!1),this.emit("media:microphone-changed",{enabled:!1}))}async toggleMicrophone(){if(!this.room)return!1;const t=this.room.localParticipant.isMicrophoneEnabled;return await this.room.localParticipant.setMicrophoneEnabled(!t),this.emit("media:microphone-changed",{enabled:!t}),!t}async enableVideo(){this.room&&(await this.room.localParticipant.setCameraEnabled(!0),this.emit("media:video-changed",{enabled:!0}))}async disableVideo(){this.room&&(await this.room.localParticipant.setCameraEnabled(!1),this.emit("media:video-changed",{enabled:!1}))}async toggleVideo(){if(!this.room)return!1;const t=this.room.localParticipant.isCameraEnabled;return await this.room.localParticipant.setCameraEnabled(!t),this.emit("media:video-changed",{enabled:!t}),!t}getLocalTracks(){return this.room?Array.from(this.room.localParticipant.tracks.values()).map(t=>t.track).filter(Boolean):[]}getRemoteTracks(){if(!this.room)return[];const t=[];for(const e of this.room.participants.values())for(const i of e.tracks.values())i.track&&t.push(i.track);return t}getRoom(){return this.room}attachTrack(t,e){if(t)try{t.attach(e)}catch(t){}}detachTrack(t,e){if(t)try{t.detach(e)}catch(t){}}}class s{constructor(t){this.vol=1,this.playing=!1,t?.incoming&&(this.incoming=new Audio(t.incoming),this.incoming.loop=!0,this.incoming.volume=this.vol),t?.outgoing&&(this.outgoing=new Audio(t.outgoing),this.outgoing.loop=!0,this.outgoing.volume=this.vol)}preloadRingtones(){this.incoming&&this.incoming.load(),this.outgoing&&this.outgoing.load()}async playIncoming(){if(this.incoming)try{await this.incoming.play(),this.playing=!0}catch(t){this.playing=!1}}async playOutgoing(){if(this.outgoing)try{await this.outgoing.play(),this.playing=!0}catch(t){this.playing=!1}}stop(){this.incoming&&(this.incoming.pause(),this.incoming.currentTime=0),this.outgoing&&(this.outgoing.pause(),this.outgoing.currentTime=0),this.playing=!1}setVolume(t){this.vol=Math.max(0,Math.min(1,t)),this.incoming&&(this.incoming.volume=this.vol),this.outgoing&&(this.outgoing.volume=this.vol)}isPlaying(){return this.playing}}class a extends t{constructor(t,e,i,n,s,a){super(),this.state="idle",this.currentCall=null,this.api=t,this.ws=e,this.livekit=i,this.audio=n,this.userId=s,this.userName=a,this.ws.on("message",t=>this.handleSocketMessage(t))}getCallState(){return this.state}getCurrentCall(){return this.currentCall}async initiateCall(t,e,i){if("idle"===this.state){this.setState("calling"),await this.audio.playOutgoing();try{const n=await this.api.initiateCall({callerId:this.userId,callerName:this.userName,calleeId:t,calleeName:e,isVideo:i}),s={callId:n?.callId??"",roomName:n?.roomName??"",token:n?.token??"",livekitUrl:n?.livekitUrl??"",participantId:this.userId,participantName:this.userName,calleeId:t,calleeName:e,isVideo:i,startTime:Date.now()};console.log(s),this.currentCall=s,this.emit("call_started",{callId:s.callId,participants:[this.userId,t]}),this.setState("connecting"),await this.livekit.connectToRoom(s.livekitUrl,s.token,{connect:!0}),this.audio.stop(),this.setState("connected")}catch(t){this.audio.stop(),this.emit("call:error",{error:t}),this.setState("idle")}}}async acceptCall(t){if("ringing"===this.state)try{await this.api.acceptCall(t,this.userId),this.setState("connecting")}catch(e){this.emit("call:error",{callId:t,error:e})}}async declineCall(t){if("ringing"===this.state)try{await this.api.declineCall(t,this.userId),this.audio.stop(),this.emit("call_declined",{callId:t}),this.cleanupCall()}catch(e){this.emit("call:error",{callId:t,error:e})}}async endCall(t){if(this.currentCall&&this.currentCall.callId===t&&("connected"===this.state||"connecting"===this.state)){try{await this.api.endCall(t,this.userId)}catch(t){}this.finishCall()}}async handleIncomingCall(t){if("idle"!==this.state)return;this.setState("ringing"),await this.audio.playIncoming();const e={callId:t.callId,roomName:t.roomName??"",token:t.token??"",livekitUrl:t.livekitUrl??"",participantId:t.callerId,participantName:t.callerName,isVideo:!!t.isVideo};this.currentCall=e,this.emit("incoming_call",{callId:e.callId,callerId:e.participantId,callerName:e.participantName,isVideo:e.isVideo})}async handleAccepted(){if(this.currentCall){this.setState("connecting");try{await this.livekit.connectToRoom(this.currentCall.livekitUrl,this.currentCall.token,{connect:!0}),this.audio.stop(),this.setState("connected"),this.emit("call_accepted",{callId:this.currentCall.callId})}catch(t){this.emit("call:error",{callId:this.currentCall.callId,error:t})}}}handleDeclined(){this.currentCall&&(this.audio.stop(),this.emit("call_declined",{callId:this.currentCall.callId}),this.cleanupCall())}handleMissed(){this.currentCall&&(this.audio.stop(),this.emit("call_missed",{callId:this.currentCall.callId}),this.cleanupCall())}handleEnded(){this.currentCall&&this.finishCall()}handleSocketMessage(t){const e=t?.type;"incoming_call"===e&&this.handleIncomingCall(t.data??t),"call_accepted"===e&&this.handleAccepted(),"call_declined"===e&&this.handleDeclined(),"call_missed"===e&&this.handleMissed(),"call_ended"===e&&this.handleEnded()}setState(t){const e=this.state;this.state=t,this.emit("state:changed",{oldState:e,newState:t})}finishCall(){const t=this.currentCall?.callId??"",e=this.currentCall?.startTime??Date.now(),i=Math.max(0,Math.floor((Date.now()-e)/1e3));this.livekit.disconnectFromRoom(),this.emit("call_ended",{callId:t,duration:i}),this.cleanupCall()}cleanupCall(){this.currentCall=null,this.setState("idle")}}function o(t,e){const i=document.createElement("div");i.className="voip-overlay";const n=document.createElement("div");n.className="voip-card";const s=document.createElement("div");s.className="voip-header";const a=document.createElement("div");a.className="voip-avatar";const o=e?.callerName||"Unknown";a.textContent=function(t){const e=String(t).trim().split(/\s+/),i=e[0]?.[0]||"",n=e.length>1?e[e.length-1][0]:"";return(i+n).toUpperCase()||"U"}(o);const c=document.createElement("div");c.className="voip-meta";const l=document.createElement("div");l.className="voip-title",l.textContent=e?.callerName?`${e.callerName}`:"Incoming Call";const r=document.createElement("div");r.className="voip-subtitle",r.textContent=e?.isVideo?"Incoming video call":"Incoming audio call",c.appendChild(l),c.appendChild(r),s.appendChild(a),s.appendChild(c);const h=document.createElement("div");h.className="voip-actions";const d=document.createElement("button");d.className="voip-btn voip-btn-accept",d.textContent="Accept";const m=document.createElement("button");m.className="voip-btn voip-btn-decline",m.textContent="Decline";const u=()=>{window.removeEventListener("keydown",p)},p=n=>{"Enter"===n.key&&(e?.callId&&t.acceptCall(e.callId),i.remove(),u()),"Escape"===n.key&&(e?.callId&&t.declineCall(e.callId),i.remove(),u())};return window.addEventListener("keydown",p),d.onclick=()=>{e?.callId&&t.acceptCall(e.callId),i.remove(),u()},m.onclick=()=>{e?.callId&&t.declineCall(e.callId),i.remove(),u()},h.appendChild(d),h.appendChild(m),n.appendChild(s),n.appendChild(h),i.appendChild(n),i}function c(t,e){const i=document.createElement("div");i.className="voip-overlay";const n=document.createElement("div");n.className="voip-card";const s=document.createElement("div");s.className="voip-header";const a=document.createElement("div");a.className="voip-avatar";const o=e?.calleeName||"Unknown";a.textContent=function(t){const e=String(t).trim().split(/\s+/),i=e[0]?.[0]||"",n=e.length>1?e[e.length-1][0]:"";return(i+n).toUpperCase()||"U"}(o);const c=document.createElement("div");c.className="voip-meta";const l=document.createElement("div");l.className="voip-title",l.textContent=e?.calleeName?`Calling ${e.calleeName}`:"Calling";const r=document.createElement("div");r.className="voip-subtitle",r.textContent=e?.isVideo?"Video call":"Audio call",c.appendChild(l),c.appendChild(r),s.appendChild(a),s.appendChild(c);const h=document.createElement("div");h.className="voip-actions";const d=document.createElement("button");return d.className="voip-btn voip-btn-cancel",d.textContent="Cancel",d.onclick=()=>{e?.callId&&t.endCall(e.callId),i.remove()},h.appendChild(d),n.appendChild(s),n.appendChild(h),i.appendChild(n),i}function l(t,e){const i=document.createElement("div");i.className="voip-overlay";const n=document.createElement("div");n.className="voip-card",n.style.position="relative";const s=document.createElement("div");s.className="voip-header";const a=document.createElement("div");a.className="voip-avatar";const o=e?.participantName||e?.calleeName||e?.callerName||"Participant";a.textContent=function(t){const e=String(t).trim().split(/\s+/),i=e[0]?.[0]||"",n=e.length>1?e[e.length-1][0]:"";return(i+n).toUpperCase()||"U"}(o);const c=document.createElement("div");c.className="voip-meta";const l=document.createElement("div");l.className="voip-title",l.textContent=o;const r=document.createElement("div");r.className="voip-subtitle";const h=document.createElement("span");h.className="voip-timer",h.textContent="00:00";const d="number"==typeof e?.startTime?e.startTime:Date.now(),m=()=>{const t=Math.max(0,Math.floor((Date.now()-d)/1e3)),e=String(Math.floor(t/60)).padStart(2,"0"),i=String(t%60).padStart(2,"0");h.textContent=`${e}:${i}`},u=window.setInterval(m,1e3);m(),r.textContent="In call • ",r.appendChild(h),c.appendChild(l),c.appendChild(r),s.appendChild(a),s.appendChild(c);const p=document.createElement("div");p.style.display="grid",p.style.gap="16px";const g=()=>{p.style.gridTemplateColumns=window.innerWidth>=768?"1fr 1fr":"1fr"};g();const v=()=>g();window.addEventListener("resize",v);const C=document.createElement("div"),w=document.createElement("div");w.className="voip-video-container";const y=document.createElement("video");y.autoplay=!0,y.playsInline=!0,w.appendChild(y),C.appendChild(w);const k=document.createElement("div");k.style.background="#1f2937",k.style.borderRadius="12px",k.style.padding="16px",k.style.height="260px",k.style.display="flex",k.style.alignItems="center",k.style.justifyContent="center";const f=document.createElement("video");f.autoplay=!0,f.muted=!0,f.playsInline=!0,f.style.maxWidth="100%",f.style.maxHeight="100%",k.appendChild(f),p.appendChild(C),p.appendChild(k);const I=document.createElement("div");I.className="voip-controls",I.style.position="absolute",I.style.left="0",I.style.right="0",I.style.bottom="16px",I.style.display="flex",I.style.justifyContent="center",I.style.gap="12px";let b=!0;const E=document.createElement("button");E.className="voip-btn voip-btn-primary";const T=()=>E.textContent=b?"Mute":"Unmute";T(),E.onclick=async()=>{const e=await t.toggleMicrophone();b=e,T()};let N=!1;const S=document.createElement("button");S.className="voip-btn voip-btn-primary";const M=()=>S.textContent=N?"Hide Video":"Show Video";M(),S.onclick=async()=>{const e=await t.toggleVideo();N=e,M()};const x=document.createElement("button");return x.className="voip-btn voip-btn-decline",x.textContent="End Call",x.onclick=()=>{e?.callId&&t.endCall(e.callId),i.remove(),window.clearInterval(u),window.removeEventListener("resize",v)},I.appendChild(E),I.appendChild(S),I.appendChild(x),n.appendChild(s),n.appendChild(p),n.appendChild(I),i.appendChild(n),i}class r{constructor(t){this.client=t}show(){this.ensureRoot()}hide(){this.root?.remove(),this.root=void 0}setTheme(t){this.ensureRoot(),this.root?this.root.setAttribute("data-voip-theme",t):document.documentElement.setAttribute("data-voip-theme",t)}showIncomingCall(t){this.ensureRoot();const e=o(this.client,t);this.root.appendChild(e)}showOutgoingCall(t){this.ensureRoot();const e=c(this.client,t);this.root.appendChild(e)}showActiveCall(t){this.ensureRoot();const e=l(this.client,t);this.root.appendChild(e);const i=e.querySelectorAll("video"),n=i[0],s=i[1],a=this.client.getRemoteTracks(),o=this.client.getLocalTracks(),c=a.find(t=>"video"===t?.kind)??a[0],r=o.find(t=>"video"===t?.kind)??o[0];c&&n&&this.client.attachTrack(c,n),r&&s&&this.client.attachTrack(r,s)}hideAllModals(){if(this.root)for(;this.root.firstChild;)this.root.removeChild(this.root.firstChild)}destroy(){this.hide()}ensureRoot(){this.root&&document.body.contains(this.root)||(this.root=document.createElement("div"),document.body.appendChild(this.root))}}class h{constructor(o){this.emitter=new t,this.microphoneEnabled=!0,this.videoEnabled=!1,this.config={enableUI:!0,autoReconnect:!0,debug:!1,...o},this.ws=new e(this.config.wsUrl,this.config.authToken,!!this.config.autoReconnect,!!this.config.debug,this.config.userId),this.api=new i(this.config.backendUrl,this.config.authToken),this.livekit=new n,this.audio=new s(this.config.ringTones),this.calls=new a(this.api,this.ws,this.livekit,this.audio,this.config.userId,this.config.userName),this.config.enableUI&&(this.ui=new r(this)),this.bindEvents(),this.audio.preloadRingtones()}bindEvents(){const t=t=>e=>this.emitter.emit(t,e);this.ws.on("connected",t("connected")),this.ws.on("disconnected",t("disconnected")),this.ws.on("reconnecting",t("reconnecting")),this.ws.on("error",t("error")),this.calls.on("state:changed",e=>{if(t("state:changed")(e),!this.ui)return;const i=e?.newState;"calling"===i||"connecting"===i?(this.ui.hideAllModals(),this.ui.showOutgoingCall(this.calls.getCurrentCall()??{})):"connected"===i?(this.ui.hideAllModals(),this.ui.showActiveCall(this.calls.getCurrentCall()??{})):"idle"!==i&&"ending"!==i||this.ui.hideAllModals()}),this.calls.on("incoming_call",e=>{t("incoming_call")(e),this.ui&&(this.ui.hideAllModals(),this.ui.showIncomingCall(e))}),this.calls.on("call_started",e=>{t("call_started")(e)}),this.calls.on("call_accepted",e=>{t("call_accepted")(e)}),this.calls.on("call_declined",e=>{t("call_declined")(e),this.ui&&this.ui.hideAllModals()}),this.calls.on("call_missed",e=>{t("call_missed")(e),this.ui&&this.ui.hideAllModals()}),this.calls.on("call_ended",e=>{t("call_ended")(e),this.ui&&this.ui.hideAllModals()}),this.livekit.on("media:microphone-changed",t=>{this.microphoneEnabled=!!t?.enabled,this.emitter.emit("media:microphone-changed",t)}),this.livekit.on("media:video-changed",t=>{this.videoEnabled=!!t?.enabled,this.emitter.emit("media:video-changed",t)})}async connect(){await this.ws.connect()}disconnect(){this.ws.disconnect()}isConnected(){return this.ws.isConnected()}async initiateCall(t,e,i){await this.calls.initiateCall(t,e,!!i?.isVideo)}async acceptCall(t){await this.calls.acceptCall(t)}async declineCall(t){await this.calls.declineCall(t)}async endCall(t){await this.calls.endCall(t)}async toggleMicrophone(){return await this.livekit.toggleMicrophone()}async toggleVideo(){return await this.livekit.toggleVideo()}async setMicrophoneEnabled(t){t?await this.livekit.enableMicrophone():await this.livekit.disableMicrophone()}async setVideoEnabled(t){t?await this.livekit.enableVideo():await this.livekit.disableVideo()}getCallState(){return this.calls.getCallState()}getActiveCall(){return this.calls.getCurrentCall()}isInCall(){return"connected"===this.getCallState()||"connecting"===this.getCallState()||"ringing"===this.getCallState()||"calling"===this.getCallState()}getLocalTracks(){return this.livekit.getLocalTracks()}getRemoteTracks(){return this.livekit.getRemoteTracks()}attachTrack(t,e){this.livekit.attachTrack(t,e)}detachTrack(t,e){this.livekit.detachTrack(t,e)}on(t,e){this.emitter.on(t,e)}off(t,e){this.emitter.off(t,e)}once(t,e){this.emitter.once(t,e)}showUI(){this.ui||(this.ui=new r(this)),this.ui.show()}hideUI(){this.ui?.hide()}setTheme(t){this.config.theme=t,this.ui?.setTheme(t)}destroy(){this.disconnect(),this.hideUI()}}export{i as APIClient,s as AudioManager,a as CallManager,t as EventEmitter,n as LiveKitManager,e as WebSocketManager,h as ZerorateVoIPClient,h as default};
|