118 lines
4.0 KiB
TypeScript
118 lines
4.0 KiB
TypeScript
import { Identity } from "spacetimedb";
|
|
import { tables, reducers } from "../../../module_bindings";
|
|
import { useTable, useReducer } from "spacetimedb/svelte";
|
|
import { LocalMediaService } from "./local-media.svelte";
|
|
import { ChannelAudioWebRTCService } from "./channel-audio-webrtc.svelte";
|
|
import { ScreenSharingWebRTCService } from "./screen-sharing-webrtc.svelte";
|
|
import * as Types from "../../../module_bindings/types";
|
|
|
|
export class WebRTCService {
|
|
identity = $state<Identity | null>(null);
|
|
connectedChannelId = $state<bigint | undefined>();
|
|
watching = $state<readonly Types.Watching[]>([]);
|
|
|
|
#startWatchingReducer = useReducer(reducers.startWatching);
|
|
#stopWatchingReducer = useReducer(reducers.stopWatching);
|
|
#setMuteReducer = useReducer(reducers.setMute);
|
|
#setDeafenReducer = useReducer(reducers.setDeafen);
|
|
|
|
localMedia: LocalMediaService;
|
|
voice: ChannelAudioWebRTCService;
|
|
screen: ScreenSharingWebRTCService;
|
|
|
|
constructor(identity: Identity | null, connectedChannelId: bigint | undefined) {
|
|
this.identity = identity;
|
|
this.connectedChannelId = connectedChannelId;
|
|
|
|
const [wStore] = useTable(tables.watching);
|
|
wStore.subscribe(v => this.watching = v);
|
|
|
|
this.localMedia = new LocalMediaService(connectedChannelId);
|
|
|
|
// Pass localMedia's stream and deafen state to voice
|
|
this.voice = new ChannelAudioWebRTCService(
|
|
identity,
|
|
connectedChannelId,
|
|
this.localMedia.localStream,
|
|
this.localMedia.isDeafened
|
|
);
|
|
|
|
this.screen = new ScreenSharingWebRTCService(
|
|
identity,
|
|
connectedChannelId,
|
|
this.localMedia.localScreenStream
|
|
);
|
|
|
|
// Sync state to sub-services
|
|
$effect(() => {
|
|
this.localMedia.connectedChannelId = this.connectedChannelId;
|
|
this.voice.identity = this.identity;
|
|
this.voice.connectedChannelId = this.connectedChannelId;
|
|
this.voice.localStream = this.localMedia.localStream;
|
|
this.voice.isDeafened = this.localMedia.isDeafened;
|
|
this.screen.identity = this.identity;
|
|
this.screen.connectedChannelId = this.connectedChannelId;
|
|
this.screen.localScreenStream = this.localMedia.localScreenStream;
|
|
});
|
|
|
|
// Sync mute/deafen to DB
|
|
$effect(() => {
|
|
if (this.connectedChannelId) {
|
|
this.#setMuteReducer({ muted: this.localMedia.isMuted });
|
|
}
|
|
});
|
|
|
|
$effect(() => {
|
|
if (this.connectedChannelId) {
|
|
this.#setDeafenReducer({ deafened: this.localMedia.isDeafened });
|
|
}
|
|
});
|
|
|
|
// Orchestration
|
|
$effect(() => {
|
|
if (this.connectedChannelId && this.identity) {
|
|
console.log(`[WebRTC] Joined channel ${this.connectedChannelId}, requesting mic...`);
|
|
this.localMedia.requestMic();
|
|
} else {
|
|
console.log("[WebRTC] Left channel, releasing mic.");
|
|
this.localMedia.releaseMic();
|
|
}
|
|
});
|
|
}
|
|
|
|
startScreenShare = () => {
|
|
this.localMedia.startScreenShare(() => {});
|
|
};
|
|
|
|
stopScreenShare = () => {
|
|
this.localMedia.stopScreenShare(() => {});
|
|
};
|
|
|
|
startWatching = (peerIdentity: Identity) => {
|
|
if (this.connectedChannelId) {
|
|
this.#startWatchingReducer({
|
|
watchee: peerIdentity,
|
|
channelId: this.connectedChannelId,
|
|
});
|
|
}
|
|
};
|
|
|
|
stopWatching = (peerIdentity: Identity) => {
|
|
this.#stopWatchingReducer({ watchee: peerIdentity });
|
|
};
|
|
|
|
// Facade getters
|
|
get localStream() { return this.localMedia.localStream; }
|
|
get localScreenStream() { return this.localMedia.localScreenStream; }
|
|
get peerStatuses() { return this.voice.peerManager.peerStatuses; }
|
|
get peers() { return this.screen.peerManager.peers; }
|
|
get isSharingScreen() { return this.localMedia.isSharingScreen; }
|
|
get isMuted() { return this.localMedia.isMuted; }
|
|
get isDeafened() { return this.localMedia.isDeafened; }
|
|
get peerStats() { return this.voice.peerManager.peerStats; }
|
|
|
|
toggleMute = () => this.localMedia.toggleMute();
|
|
toggleDeafen = () => this.localMedia.toggleDeafen();
|
|
setPeerAudioPreference = (peerIdHex: string, pref: any) => this.voice.peerManager.setPeerAudioPreference(peerIdHex, pref);
|
|
}
|