@livekit/components-core
Advanced tools
Comparing version 0.11.5 to 0.11.6
import type { AudioCaptureOptions } from 'livekit-client'; | ||
import { BehaviorSubject } from 'rxjs'; | ||
import { ChatMessage } from 'livekit-client'; | ||
import { ConnectionQuality } from 'livekit-client'; | ||
@@ -8,3 +9,3 @@ import { ConnectionState } from 'livekit-client'; | ||
import { LocalAudioTrack } from 'livekit-client'; | ||
import type { LocalParticipant } from 'livekit-client'; | ||
import { LocalParticipant } from 'livekit-client'; | ||
import { LocalVideoTrack } from 'livekit-client'; | ||
@@ -58,14 +59,13 @@ import loglevel from 'loglevel'; | ||
/** @public */ | ||
export declare interface ChatMessage { | ||
id: string; | ||
timestamp: number; | ||
message: string; | ||
} | ||
export { ChatMessage } | ||
/** @public */ | ||
export declare type ChatOptions = { | ||
messageEncoder?: (message: ChatMessage) => Uint8Array; | ||
messageDecoder?: (message: Uint8Array) => ReceivedChatMessage; | ||
/** @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead */ | ||
messageEncoder?: (message: LegacyChatMessage) => Uint8Array; | ||
/** @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead */ | ||
messageDecoder?: (message: Uint8Array) => LegacyReceivedChatMessage; | ||
/** @deprecated the new chat API doesn't rely on topics anymore and uses a dedicated chat API instead */ | ||
channelTopic?: string; | ||
/** @deprecated the new chat API doesn't rely on topics anymore and uses a dedicated chat API instead */ | ||
updateChannelTopic?: string; | ||
@@ -95,2 +95,4 @@ }; | ||
export declare function createChatObserver(room: Room): Observable<[message: ChatMessage, participant?: LocalParticipant | RemoteParticipant | undefined]>; | ||
export declare function createConnectionQualityObserver(participant: Participant): Observable<ConnectionQuality>; | ||
@@ -266,2 +268,10 @@ | ||
export declare interface LegacyChatMessage extends ChatMessage { | ||
ignore?: true; | ||
} | ||
export declare interface LegacyReceivedChatMessage extends ReceivedChatMessage { | ||
ignore?: true; | ||
} | ||
/** | ||
@@ -327,7 +337,13 @@ * Reads the user choices from local storage, or returns the default settings if none are found. | ||
/** @public */ | ||
export declare type MessageDecoder = (message: Uint8Array) => ReceivedChatMessage; | ||
/** | ||
* @public | ||
* @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead | ||
*/ | ||
export declare type MessageDecoder = (message: Uint8Array) => LegacyReceivedChatMessage; | ||
/** @public */ | ||
export declare type MessageEncoder = (message: ChatMessage) => Uint8Array; | ||
/** | ||
* @public | ||
* @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead | ||
*/ | ||
export declare type MessageEncoder = (message: LegacyChatMessage) => Uint8Array; | ||
@@ -413,3 +429,2 @@ export declare function mutedObserver(trackRef: TrackReferenceOrPlaceholder): Observable<boolean>; | ||
from?: Participant; | ||
editTimestamp?: number; | ||
} | ||
@@ -519,5 +534,21 @@ | ||
send: (message: string) => Promise<ChatMessage>; | ||
update: (message: string, messageId: string) => Promise<ChatMessage>; | ||
update: (message: string, originalMessageOrId: string | ChatMessage) => Promise<{ | ||
readonly message: string; | ||
readonly editTimestamp: number; | ||
readonly id: string; | ||
readonly timestamp: number; | ||
}>; | ||
}; | ||
export declare function setupChatMessageHandler(room: Room): { | ||
chatObservable: Observable<[message: ChatMessage, participant?: LocalParticipant | RemoteParticipant | undefined]>; | ||
send: (text: string) => Promise<ChatMessage>; | ||
edit: (text: string, originalMsg: ChatMessage) => Promise<{ | ||
readonly message: string; | ||
readonly editTimestamp: number; | ||
readonly id: string; | ||
readonly timestamp: number; | ||
}>; | ||
}; | ||
export declare function setupChatToggle(): { | ||
@@ -524,0 +555,0 @@ className: string; |
@@ -1,23 +0,34 @@ | ||
import type { Participant, Room } from 'livekit-client'; | ||
import type { Participant, Room, ChatMessage } from 'livekit-client'; | ||
import { BehaviorSubject } from 'rxjs'; | ||
/** @public */ | ||
export interface ChatMessage { | ||
id: string; | ||
timestamp: number; | ||
message: string; | ||
} | ||
export type { ChatMessage }; | ||
/** @public */ | ||
export interface ReceivedChatMessage extends ChatMessage { | ||
from?: Participant; | ||
editTimestamp?: number; | ||
} | ||
export interface LegacyChatMessage extends ChatMessage { | ||
ignore?: true; | ||
} | ||
export interface LegacyReceivedChatMessage extends ReceivedChatMessage { | ||
ignore?: true; | ||
} | ||
/** | ||
* @public | ||
* @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead | ||
*/ | ||
export type MessageEncoder = (message: LegacyChatMessage) => Uint8Array; | ||
/** | ||
* @public | ||
* @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead | ||
*/ | ||
export type MessageDecoder = (message: Uint8Array) => LegacyReceivedChatMessage; | ||
/** @public */ | ||
export type MessageEncoder = (message: ChatMessage) => Uint8Array; | ||
/** @public */ | ||
export type MessageDecoder = (message: Uint8Array) => ReceivedChatMessage; | ||
/** @public */ | ||
export type ChatOptions = { | ||
messageEncoder?: (message: ChatMessage) => Uint8Array; | ||
messageDecoder?: (message: Uint8Array) => ReceivedChatMessage; | ||
/** @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead */ | ||
messageEncoder?: (message: LegacyChatMessage) => Uint8Array; | ||
/** @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead */ | ||
messageDecoder?: (message: Uint8Array) => LegacyReceivedChatMessage; | ||
/** @deprecated the new chat API doesn't rely on topics anymore and uses a dedicated chat API instead */ | ||
channelTopic?: string; | ||
/** @deprecated the new chat API doesn't rely on topics anymore and uses a dedicated chat API instead */ | ||
updateChannelTopic?: string; | ||
@@ -29,4 +40,9 @@ }; | ||
send: (message: string) => Promise<ChatMessage>; | ||
update: (message: string, messageId: string) => Promise<ChatMessage>; | ||
update: (message: string, originalMessageOrId: string | ChatMessage) => Promise<{ | ||
readonly message: string; | ||
readonly editTimestamp: number; | ||
readonly id: string; | ||
readonly timestamp: number; | ||
}>; | ||
}; | ||
//# sourceMappingURL=chat.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import type { DataPublishOptions, LocalParticipant, Participant, Room } from 'livekit-client'; | ||
import type { ChatMessage, DataPublishOptions, LocalParticipant, Participant, Room } from 'livekit-client'; | ||
import { Observable } from 'rxjs'; | ||
@@ -25,2 +25,12 @@ export declare const DataTopic: { | ||
}; | ||
export declare function setupChatMessageHandler(room: Room): { | ||
chatObservable: Observable<[message: ChatMessage, participant?: LocalParticipant | import("livekit-client").RemoteParticipant | undefined]>; | ||
send: (text: string) => Promise<ChatMessage>; | ||
edit: (text: string, originalMsg: ChatMessage) => Promise<{ | ||
readonly message: string; | ||
readonly editTimestamp: number; | ||
readonly id: string; | ||
readonly timestamp: number; | ||
}>; | ||
}; | ||
//# sourceMappingURL=dataChannel.d.ts.map |
import { Observable } from 'rxjs'; | ||
import type { Participant, TrackPublication } from 'livekit-client'; | ||
import { Room, RoomEvent } from 'livekit-client'; | ||
import { LocalParticipant, Room, RoomEvent } from 'livekit-client'; | ||
import type { RoomEventCallbacks } from 'livekit-client/dist/src/room/Room'; | ||
@@ -21,2 +21,3 @@ export declare function observeRoomEvents(room: Room, ...events: RoomEvent[]): Observable<Room>; | ||
export declare function createDataObserver(room: Room): Observable<[payload: Uint8Array, participant?: import("livekit-client").RemoteParticipant | undefined, kind?: import("livekit-client").DataPacket_Kind | undefined, topic?: string | undefined]>; | ||
export declare function createChatObserver(room: Room): Observable<[message: import("livekit-client").ChatMessage, participant?: LocalParticipant | import("livekit-client").RemoteParticipant | undefined]>; | ||
export declare function roomAudioPlaybackAllowedObservable(room: Room): Observable<{ | ||
@@ -23,0 +24,0 @@ canPlayAudio: boolean; |
{ | ||
"name": "@livekit/components-core", | ||
"version": "0.11.5", | ||
"version": "0.11.6", | ||
"license": "Apache-2.0", | ||
@@ -33,7 +33,7 @@ "author": "LiveKit", | ||
"peerDependencies": { | ||
"livekit-client": "^2.4.0", | ||
"@livekit/protocol": "^1.20.1", | ||
"livekit-client": "^2.5.4", | ||
"tslib": "^2.6.2" | ||
}, | ||
"devDependencies": { | ||
"@livekit/protocol": "^1.22.0", | ||
"@microsoft/api-extractor": "^7.36.0", | ||
@@ -46,3 +46,3 @@ "@size-limit/file": "^11.0.2", | ||
"vitest": "^2.0.0", | ||
"@livekit/components-styles": "1.1.2", | ||
"@livekit/components-styles": "1.1.3", | ||
"eslint-config-lk-custom": "0.1.3" | ||
@@ -49,0 +49,0 @@ }, |
/* eslint-disable camelcase */ | ||
import type { Participant, Room } from 'livekit-client'; | ||
import type { Participant, Room, ChatMessage } from 'livekit-client'; | ||
import { RoomEvent } from 'livekit-client'; | ||
import { BehaviorSubject, Subject, scan, map, takeUntil } from 'rxjs'; | ||
import { DataTopic, sendMessage, setupDataMessageHandler } from '../observables/dataChannel'; | ||
import { BehaviorSubject, Subject, scan, map, takeUntil, merge } from 'rxjs'; | ||
import { | ||
DataTopic, | ||
sendMessage, | ||
setupChatMessageHandler, | ||
setupDataMessageHandler, | ||
} from '../observables/dataChannel'; | ||
/** @public */ | ||
export interface ChatMessage { | ||
id: string; | ||
timestamp: number; | ||
message: string; | ||
} | ||
export type { ChatMessage }; | ||
@@ -17,14 +18,31 @@ /** @public */ | ||
from?: Participant; | ||
editTimestamp?: number; | ||
} | ||
export interface LegacyChatMessage extends ChatMessage { | ||
ignore?: true; | ||
} | ||
export interface LegacyReceivedChatMessage extends ReceivedChatMessage { | ||
ignore?: true; | ||
} | ||
/** | ||
* @public | ||
* @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead | ||
*/ | ||
export type MessageEncoder = (message: LegacyChatMessage) => Uint8Array; | ||
/** | ||
* @public | ||
* @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead | ||
*/ | ||
export type MessageDecoder = (message: Uint8Array) => LegacyReceivedChatMessage; | ||
/** @public */ | ||
export type MessageEncoder = (message: ChatMessage) => Uint8Array; | ||
/** @public */ | ||
export type MessageDecoder = (message: Uint8Array) => ReceivedChatMessage; | ||
/** @public */ | ||
export type ChatOptions = { | ||
messageEncoder?: (message: ChatMessage) => Uint8Array; | ||
messageDecoder?: (message: Uint8Array) => ReceivedChatMessage; | ||
/** @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead */ | ||
messageEncoder?: (message: LegacyChatMessage) => Uint8Array; | ||
/** @deprecated the new chat API doesn't rely on encoders and decoders anymore and uses a dedicated chat API instead */ | ||
messageDecoder?: (message: Uint8Array) => LegacyReceivedChatMessage; | ||
/** @deprecated the new chat API doesn't rely on topics anymore and uses a dedicated chat API instead */ | ||
channelTopic?: string; | ||
/** @deprecated the new chat API doesn't rely on topics anymore and uses a dedicated chat API instead */ | ||
updateChannelTopic?: string; | ||
@@ -44,5 +62,6 @@ }; | ||
const encode = (message: ChatMessage) => encoder.encode(JSON.stringify(message)); | ||
const encode = (message: LegacyReceivedChatMessage) => encoder.encode(JSON.stringify(message)); | ||
const decode = (message: Uint8Array) => JSON.parse(decoder.decode(message)) as ReceivedChatMessage; | ||
const decode = (message: Uint8Array) => | ||
JSON.parse(decoder.decode(message)) as LegacyReceivedChatMessage | ReceivedChatMessage; | ||
@@ -72,2 +91,3 @@ export function setupChat(room: Room, options?: ChatOptions) { | ||
} | ||
const { chatObservable, send: sendChatMessage } = setupChatMessageHandler(room); | ||
@@ -77,9 +97,24 @@ const finalMessageDecoder = messageDecoder ?? decode; | ||
/** Build up the message array over time. */ | ||
const messagesObservable = messageSubject.pipe( | ||
map((msg) => { | ||
const parsedMessage = finalMessageDecoder(msg.payload); | ||
const newMessage: ReceivedChatMessage = { ...parsedMessage, from: msg.from }; | ||
return newMessage; | ||
}), | ||
scan<ReceivedChatMessage, ReceivedChatMessage[]>((acc, value) => { | ||
const messagesObservable = merge( | ||
messageSubject.pipe( | ||
map((msg) => { | ||
const parsedMessage = finalMessageDecoder(msg.payload); | ||
const newMessage = { ...parsedMessage, from: msg.from }; | ||
if (isIgnorableChatMessage(newMessage)) { | ||
return undefined; | ||
} | ||
return newMessage; | ||
}), | ||
), | ||
chatObservable.pipe( | ||
map(([msg, participant]) => { | ||
return { ...msg, from: participant }; | ||
}), | ||
), | ||
).pipe( | ||
scan<ReceivedChatMessage | undefined, ReceivedChatMessage[]>((acc, value) => { | ||
// ignore legacy message updates | ||
if (!value) { | ||
return acc; | ||
} | ||
// handle message updates | ||
@@ -96,3 +131,3 @@ if ( | ||
timestamp: originalMsg.timestamp, | ||
editTimestamp: value.timestamp, | ||
editTimestamp: value.editTimestamp ?? value.timestamp, | ||
}; | ||
@@ -113,17 +148,10 @@ } | ||
const send = async (message: string) => { | ||
const timestamp = Date.now(); | ||
const id = crypto.randomUUID(); | ||
const chatMessage: ChatMessage = { id, message, timestamp }; | ||
const encodedMsg = finalMessageEncoder(chatMessage); | ||
isSending$.next(true); | ||
try { | ||
await sendMessage(room.localParticipant, encodedMsg, { | ||
const chatMessage = await sendChatMessage(message); | ||
const encodedLegacyMsg = finalMessageEncoder({ ...chatMessage, ignore: true }); | ||
await sendMessage(room.localParticipant, encodedLegacyMsg, { | ||
reliable: true, | ||
topic, | ||
}); | ||
messageSubject.next({ | ||
payload: encodedMsg, | ||
topic: topic, | ||
from: room.localParticipant, | ||
}); | ||
return chatMessage; | ||
@@ -135,18 +163,17 @@ } finally { | ||
const update = async (message: string, messageId: string) => { | ||
const update = async (message: string, originalMessageOrId: string | ChatMessage) => { | ||
const timestamp = Date.now(); | ||
const chatMessage: ChatMessage = { id: messageId, message, timestamp }; | ||
const encodedMsg = finalMessageEncoder(chatMessage); | ||
const originalMessage: ChatMessage = | ||
typeof originalMessageOrId === 'string' | ||
? { id: originalMessageOrId, message: '', timestamp } | ||
: originalMessageOrId; | ||
isSending$.next(true); | ||
try { | ||
await sendMessage(room.localParticipant, encodedMsg, { | ||
const editedMessage = await room.localParticipant.editChatMessage(message, originalMessage); | ||
const encodedLegacyMessage = finalMessageEncoder(editedMessage); | ||
await sendMessage(room.localParticipant, encodedLegacyMessage, { | ||
topic: updateTopic, | ||
reliable: true, | ||
}); | ||
messageSubject.next({ | ||
payload: encodedMsg, | ||
topic: topic, | ||
from: room.localParticipant, | ||
}); | ||
return chatMessage; | ||
return editedMessage; | ||
} finally { | ||
@@ -164,3 +191,14 @@ isSending$.next(false); | ||
return { messageObservable: messagesObservable, isSendingObservable: isSending$, send, update }; | ||
return { | ||
messageObservable: messagesObservable, | ||
isSendingObservable: isSending$, | ||
send, | ||
update, | ||
}; | ||
} | ||
function isIgnorableChatMessage( | ||
msg: ReceivedChatMessage | LegacyReceivedChatMessage, | ||
): msg is ReceivedChatMessage { | ||
return (msg as LegacyChatMessage).ignore == true; | ||
} |
@@ -14,2 +14,4 @@ import { ParticipantEvent, RoomEvent } from 'livekit-client'; | ||
RoomEvent.ParticipantMetadataChanged, | ||
RoomEvent.ParticipantNameChanged, | ||
RoomEvent.ParticipantAttributesChanged, | ||
@@ -16,0 +18,0 @@ RoomEvent.TrackMuted, |
@@ -1,5 +0,11 @@ | ||
import type { DataPublishOptions, LocalParticipant, Participant, Room } from 'livekit-client'; | ||
import type { | ||
ChatMessage, | ||
DataPublishOptions, | ||
LocalParticipant, | ||
Participant, | ||
Room, | ||
} from 'livekit-client'; | ||
import type { Subscriber } from 'rxjs'; | ||
import { Observable, filter, map } from 'rxjs'; | ||
import { createDataObserver } from './room'; | ||
import { createChatObserver, createDataObserver } from './room'; | ||
@@ -75,1 +81,17 @@ export const DataTopic = { | ||
} | ||
export function setupChatMessageHandler(room: Room) { | ||
const chatObservable = createChatObserver(room); | ||
const send = async (text: string) => { | ||
const msg = await room.localParticipant.sendChatMessage(text); | ||
return msg; | ||
}; | ||
const edit = async (text: string, originalMsg: ChatMessage) => { | ||
const msg = await room.localParticipant.editChatMessage(text, originalMsg); | ||
return msg; | ||
}; | ||
return { chatObservable, send, edit }; | ||
} |
@@ -222,2 +222,6 @@ import type { Subscriber, Subscription } from 'rxjs'; | ||
export function createChatObserver(room: Room) { | ||
return roomEventSelector(room, RoomEvent.ChatMessage); | ||
} | ||
export function roomAudioPlaybackAllowedObservable(room: Room) { | ||
@@ -224,0 +228,0 @@ const observable = observeRoomEvents(room, RoomEvent.AudioPlaybackStatusChanged).pipe( |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
690160
5
9428
10