Skip to content

Commit

Permalink
Add overload for kind + identity in useRemoteParticipant hook (#893)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasIO authored Jun 17, 2024
1 parent 99b8418 commit fcc2374
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 26 deletions.
6 changes: 6 additions & 0 deletions .changeset/weak-glasses-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@livekit/components-core': patch
'@livekit/components-react': patch
---

Add overload for kind + identity in useRemoteParticipant hook
2 changes: 1 addition & 1 deletion docs/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dependencies": {
"@livekit/components-react": "workspace:*",
"@livekit/components-styles": "workspace:*",
"livekit-client": "^2.1.5",
"livekit-client": "^2.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down
2 changes: 1 addition & 1 deletion examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dependencies": {
"@livekit/components-react": "workspace:*",
"@livekit/components-styles": "workspace:*",
"livekit-client": "^2.1.5",
"livekit-client": "^2.2.0",
"livekit-server-sdk": "^1.2.7",
"next": "^12.3.4",
"react": "^18.2.0",
Expand Down
16 changes: 14 additions & 2 deletions packages/core/etc/components-core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Observable } from 'rxjs';
import type { Participant } from 'livekit-client';
import { ParticipantEvent } from 'livekit-client';
import type { ParticipantEventCallbacks } from 'livekit-client/dist/src/room/participant/Participant';
import type { ParticipantKind } from 'livekit-client';
import type { ParticipantPermission } from '@livekit/protocol';
import type { PublicationEventCallbacks } from 'livekit-client/dist/src/room/track/TrackPublication';
import { RemoteParticipant } from 'livekit-client';
Expand Down Expand Up @@ -296,6 +297,11 @@ export function observeRoomEvents(room: Room, ...events: RoomEvent[]): Observabl
// @public (undocumented)
export function observeTrackEvents(track: TrackPublication, ...events: TrackEvent_2[]): Observable<TrackPublication>;

// Warning: (ae-incompatible-release-tags) The symbol "participantByIdentifierObserver" is marked as @public, but its signature references "ParticipantIdentifier" which is marked as @beta
//
// @public (undocumented)
export function participantByIdentifierObserver(room: Room, { kind, identity }: ParticipantIdentifier, options?: ConnectedParticipantObserverOptions): Observable<RemoteParticipant | undefined>;

// Warning: (ae-internal-missing-underscore) The name "ParticipantClickEvent" should be prefixed with an underscore because the declaration is marked as @internal
//
// @internal (undocumented)
Expand All @@ -312,6 +318,14 @@ export function participantEventSelector<T extends ParticipantEvent>(participant
// @public (undocumented)
export type ParticipantFilter = Parameters<Participant[]['filter']>['0'];

// Warning: (ae-forgotten-export) The symbol "RequireAtLeastOne" needs to be exported by the entry point index.d.ts
//
// @beta (undocumented)
export type ParticipantIdentifier = RequireAtLeastOne<{
kind: ParticipantKind;
identity: string;
}, 'identity' | 'kind'>;

// @public (undocumented)
export function participantInfoObserver(participant: Participant): Observable<{
name: string | undefined;
Expand Down Expand Up @@ -345,8 +359,6 @@ export function participantPermissionObserver(participant: Participant): Observa
// @public (undocumented)
export const participantTrackEvents: ParticipantEvent[];

// Warning: (ae-forgotten-export) The symbol "RequireAtLeastOne" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type ParticipantTrackIdentifier = RequireAtLeastOne<{
sources: Track.Source[];
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"rxjs": "7.8.1"
},
"peerDependencies": {
"livekit-client": "^2.1.5",
"livekit-client": "^2.2.0",
"@livekit/protocol": "^1.16.0",
"tslib": "^2.6.2"
},
Expand Down
40 changes: 39 additions & 1 deletion packages/core/src/observables/participant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Observable, map, startWith, switchMap } from 'rxjs';
import { getTrackByIdentifier } from '../components/mediaTrack';
import { allParticipantEvents, allParticipantRoomEvents } from '../helper/eventGroups';
import type { TrackReferenceOrPlaceholder } from '../track-reference';
import type { TrackIdentifier } from '../types';
import type { ParticipantIdentifier, TrackIdentifier } from '../types';
import { observeRoomEvents } from './room';

export function observeParticipantEvents<T extends Participant>(
Expand Down Expand Up @@ -248,3 +248,41 @@ export function participantPermissionObserver(
);
return observer;
}

export function participantByIdentifierObserver(
room: Room,
{ kind, identity }: ParticipantIdentifier,
options: ConnectedParticipantObserverOptions = {},
): Observable<RemoteParticipant | undefined> {
const additionalEvents = options.additionalEvents ?? allParticipantEvents;
const matchesIdentifier = (participant: RemoteParticipant) => {
let isMatch = true;
if (kind) {
isMatch = isMatch && participant.kind === kind;
}
if (identity) {
isMatch = isMatch && participant.identity === identity;
}
return isMatch;
};
const observable = observeRoomEvents(
room,
RoomEvent.ParticipantConnected,
RoomEvent.ParticipantDisconnected,
RoomEvent.ConnectionStateChanged,
).pipe(
switchMap((r) => {
const participant = Array.from(r.remoteParticipants.values()).find((p) =>
matchesIdentifier(p),
);
if (participant) {
return observeParticipantEvents(participant, ...additionalEvents);
} else {
return new Observable<undefined>((subscribe) => subscribe.next(undefined));
}
}),
startWith(Array.from(room.remoteParticipants.values()).find((p) => matchesIdentifier(p))),
);

return observable;
}
10 changes: 9 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Participant, Track, TrackPublication } from 'livekit-client';
import type { Participant, ParticipantKind, Track, TrackPublication } from 'livekit-client';
import type { TrackReference, TrackReferenceOrPlaceholder } from './track-reference';

// ## PinState Type
Expand Down Expand Up @@ -57,6 +57,14 @@ export type ParticipantTrackIdentifier = RequireAtLeastOne<
'sources' | 'name' | 'kind'
>;

/**
* @beta
*/
export type ParticipantIdentifier = RequireAtLeastOne<
{ kind: ParticipantKind; identity: string },
'identity' | 'kind'
>;

/**
* The TrackIdentifier type is used to select Tracks either based on
* - Track.Source and/or name of the track, e.g. `{source: Track.Source.Camera}` or `{name: "my-track"}`
Expand Down
14 changes: 14 additions & 0 deletions packages/react/etc/components-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { LocalVideoTrack } from 'livekit-client';
import type { MediaDeviceFailure } from 'livekit-client';
import { Participant } from 'livekit-client';
import type { ParticipantEvent } from 'livekit-client';
import type { ParticipantKind } from 'livekit-client';
import type { ParticipantPermission } from '@livekit/protocol';
import * as React_2 from 'react';
import type { RemoteAudioTrack } from 'livekit-client';
Expand Down Expand Up @@ -449,6 +450,14 @@ export function ParticipantContextIfNeeded(props: React_2.PropsWithChildren<{
participant?: Participant;
}>): React_2.JSX.Element;

// Warning: (ae-forgotten-export) The symbol "RequireAtLeastOne" needs to be exported by the entry point index.d.ts
//
// @beta (undocumented)
export type ParticipantIdentifier = RequireAtLeastOne<{
kind: ParticipantKind;
identity: string;
}, 'identity' | 'kind'>;

// @public
export function ParticipantLoop({ participants, ...props }: ParticipantLoopProps): React_2.JSX.Element;

Expand Down Expand Up @@ -967,6 +976,11 @@ export function usePreviewDevice<T extends LocalVideoTrack | LocalAudioTrack>(en
// @alpha (undocumented)
export function usePreviewTracks(options: CreateLocalTracksOptions, onError?: (err: Error) => void): LocalTrack<Track.Kind>[] | undefined;

// Warning: (ae-incompatible-release-tags) The symbol "useRemoteParticipant" is marked as @public, but its signature references "ParticipantIdentifier" which is marked as @beta
//
// @public
export function useRemoteParticipant(identifier: ParticipantIdentifier, options?: UseRemoteParticipantOptions): RemoteParticipant | undefined;

// @public
export function useRemoteParticipant(identity: string, options?: UseRemoteParticipantOptions): RemoteParticipant | undefined;

Expand Down
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
},
"peerDependencies": {
"@livekit/protocol": "^1.16.0",
"livekit-client": "^2.1.5",
"livekit-client": "^2.2.0",
"react": ">=18",
"react-dom": ">=18",
"tslib": "^2.6.2"
Expand Down
44 changes: 37 additions & 7 deletions packages/react/src/hooks/useRemoteParticipant.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { connectedParticipantObserver } from '@livekit/components-core';
import {
type ParticipantIdentifier,
connectedParticipantObserver,
participantByIdentifierObserver,
} from '@livekit/components-core';
import type { ParticipantEvent, RemoteParticipant } from 'livekit-client';
import * as React from 'react';
import { useRoomContext } from '../context';
Expand All @@ -13,7 +17,22 @@ export interface UseRemoteParticipantOptions {
}

/**
* The `useRemoteParticipant` hook returns the RemoteParticipant with the given `identity`.
* The `useRemoteParticipant` hook returns the first RemoteParticipant by either identity and/or based on the participant kind.
* @remarks
* To optimize performance, you can use the `updateOnlyOn` property to decide on what `ParticipantEvents` the hook updates.
*
* @example
* ```tsx
* const participant = useRemoteParticipant({kind: ParticipantKind.Agent, identity: 'myAgent'});
* ```
* @public
*/
export function useRemoteParticipant(
identifier: ParticipantIdentifier,
options?: UseRemoteParticipantOptions,
): RemoteParticipant | undefined;
/**
* The `useRemoteParticipant` hook returns the first RemoteParticipant by either identity or based on the participant kind.
* @remarks
* To optimize performance, you can use the `updateOnlyOn` property to decide on what `ParticipantEvents` the hook updates.
*
Expand All @@ -25,20 +44,31 @@ export interface UseRemoteParticipantOptions {
*/
export function useRemoteParticipant(
identity: string,
options?: UseRemoteParticipantOptions,
): RemoteParticipant | undefined;
export function useRemoteParticipant(
identityOrIdentifier: string | ParticipantIdentifier,
options: UseRemoteParticipantOptions = {},
): RemoteParticipant | undefined {
const room = useRoomContext();
const [updateOnlyOn] = React.useState(options.updateOnlyOn);

const observable = React.useMemo(
() => connectedParticipantObserver(room, identity, { additionalEvents: updateOnlyOn }),
[room, identity, updateOnlyOn],
);
const observable = React.useMemo(() => {
if (typeof identityOrIdentifier === 'string') {
return connectedParticipantObserver(room, identityOrIdentifier, {
additionalEvents: updateOnlyOn,
});
} else {
return participantByIdentifierObserver(room, identityOrIdentifier, {
additionalEvents: updateOnlyOn,
});
}
}, [room, JSON.stringify(identityOrIdentifier), updateOnlyOn]);

// Using `wrapperParticipant` to ensure a new object reference,
// triggering a re-render when the participant events fire.
const [participantWrapper, setParticipantWrapper] = React.useState({
p: room.getParticipantByIdentity(identity) as RemoteParticipant | undefined,
p: undefined as RemoteParticipant | undefined,
});
React.useEffect(() => {
const listener = observable.subscribe((p) => setParticipantWrapper({ p }));
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type {
TrackReference,
TrackReferenceOrPlaceholder,
ParticipantClickEvent,
ParticipantIdentifier,
PinState,
WidgetState,
} from '@livekit/components-core';
22 changes: 11 additions & 11 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fcc2374

Please sign in to comment.