Skip to content

Commit

Permalink
feat: add session and livestream call durations
Browse files Browse the repository at this point in the history
  • Loading branch information
kristian-mkd committed Oct 25, 2024
1 parent 836a0f3 commit 0366dea
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ Here is an excerpt of the call state properties:
| `createdBy$` | `createdBy` | The user who created the call. |
| `custom$` | `custom` | Custom data attached to the call. |
| `dominantSpeaker$` | `dominantSpeaker` | The participant that is the current dominant speaker of the call. |
| `duration$` | `duration` | The duration since the start of the call in seconds. |
| `sessionDuration$` | `sessionDuration` | The duration since the start of the call session in seconds. |
| `liveDuration$` | `liveDuration` | The duration since the start of the call livestream in seconds. |
| `egress$` | `egress` | The egress data of the call (for broadcasting and livestreaming). |
| `endedAt$` | `endedAt` | The time the call was ended. |
| `endedBy$` | `endedBy` | The user who ended the call. |
Expand Down
105 changes: 86 additions & 19 deletions packages/client/src/store/CallState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ export class CallState {
private callStatsReportSubject = new BehaviorSubject<
CallStatsReport | undefined
>(undefined);
private durationSubject = new BehaviorSubject<number>(0);
private liveDurationSubject = new BehaviorSubject<number>(0);
private sessionDurationSubject = new BehaviorSubject<number>(0);

// These are tracks that were delivered to the Subscriber's onTrack event
// that we couldn't associate with a participant yet.
Expand Down Expand Up @@ -287,10 +288,16 @@ export class CallState {
thumbnails$: Observable<ThumbnailResponse | undefined>;

/**
* Will provide the count of seconds since the call started.
* Will provide the count of seconds since the session started.
*/
duration$: Observable<number>;
durationInterval: NodeJS.Timeout | undefined;
sessionDuration$: Observable<number>;
sessionDurationInterval: NodeJS.Timeout | undefined;

/**
* Will provide the count of seconds since the livestream started.
*/
liveDuration$: Observable<number>;
liveDurationInterval: NodeJS.Timeout | undefined;

readonly logger = getLogger(['CallState']);

Expand Down Expand Up @@ -397,8 +404,10 @@ export class CallState {
this.participantCount$ = duc(this.participantCountSubject);
this.recording$ = duc(this.recordingSubject);
this.transcribing$ = duc(this.transcribingSubject);
this.duration$ = duc(this.durationSubject);
this.durationInterval = undefined;
this.sessionDuration$ = duc(this.sessionDurationSubject);
this.sessionDurationInterval = undefined;
this.liveDuration$ = duc(this.liveDurationSubject);
this.liveDurationInterval = undefined;

this.eventHandlers = {
// these events are not updating the call state:
Expand Down Expand Up @@ -433,6 +442,7 @@ export class CallState {
'call.hls_broadcasting_started': this.updateFromHLSBroadcastStarted,
'call.hls_broadcasting_stopped': this.updateFromHLSBroadcastStopped,
'call.live_started': (e) => this.updateFromCallResponse(e.call),
'call.live_ended': (e) => this.updateFromCallResponse(e.call), // Needs to be implemented on backend
'call.member_added': this.updateFromMemberAdded,
'call.member_removed': this.updateFromMemberRemoved,
'call.member_updated_permission': this.updateMembers,
Expand Down Expand Up @@ -477,9 +487,13 @@ export class CallState {
* Runs the cleanup tasks.
*/
dispose = () => {
if (this.durationInterval) {
clearInterval(this.durationInterval);
this.durationInterval = undefined;
if (this.sessionDurationInterval) {
clearInterval(this.sessionDurationInterval);
this.sessionDurationInterval = undefined;
}
if (this.liveDurationInterval) {
clearInterval(this.liveDurationInterval);
this.liveDurationInterval = undefined;
}
};

Expand Down Expand Up @@ -551,6 +565,40 @@ export class CallState {
return this.setCurrentValue(this.durationSubject, duration);
};

/**
* The number of seconds since the start of the call session.
*/
get sessionDuration() {
return this.getCurrentValue(this.sessionDuration$);
}

/**
* Sets the number of seconds since the start of the call session.
*
* @internal
* @param sessionDuration the duration of the call session in seconds.
*/
setSessionDuration = (duration: Patch<number>) => {
return this.setCurrentValue(this.sessionDurationSubject, duration);
};

/**
* The number of seconds since the start of the livestream.
*/
get liveDuration() {
return this.getCurrentValue(this.liveDuration$);
}

/**
* Sets the number of seconds since the start of the livestream.
*
* @internal
* @param liveDuration the duration of the livestream in seconds.
*/
setLiveDuration = (duration: Patch<number>) => {
return this.setCurrentValue(this.liveDurationSubject, duration);
};

/**
* The time the call session actually started.
* Useful for displaying the call duration.
Expand Down Expand Up @@ -1207,20 +1255,39 @@ export class CallState {
this.setAnonymousParticipantCount(session.anonymous_participant_count || 0);
};

startDurationInterval = (
startTime: string,
setDurationCallback: (seconds: Patch<number>) => number,
) => {
const startedAt = new Date(startTime).getTime();
const elapsedSeconds = Math.floor((Date.now() - startedAt) / 1000);
setDurationCallback(elapsedSeconds);
return setInterval(() => setDurationCallback((prev) => prev + 1), 1000);
};

private updateDuration = (session: CallSessionResponse | undefined) => {
if (session?.live_started_at && !this.durationInterval) {
const startedAt = new Date(session.live_started_at).getTime();
const elapsedSeconds = Math.floor((Date.now() - startedAt) / 1000);
this.setDuration(elapsedSeconds);
this.durationInterval = setInterval(
() => this.setDuration((prev) => prev + 1),
1000,
// session duration
if (session?.started_at && !this.sessionDurationInterval) {
this.sessionDurationInterval = this.startDurationInterval(
session.started_at,
this.setSessionDuration,
);
}
if (session?.ended_at && this.sessionDurationInterval) {
clearInterval(this.sessionDurationInterval);
this.sessionDurationInterval = undefined;
}

if (session?.ended_at && this.durationInterval) {
clearInterval(this.durationInterval);
this.durationInterval = undefined;
// livestream duration
if (session?.live_started_at && !this.liveDurationInterval) {
this.liveDurationInterval = this.startDurationInterval(
session.live_started_at,
this.setDuration,
);
}
if (session?.live_ended_at && this.liveDurationInterval) {
clearInterval(this.liveDurationInterval);
this.liveDurationInterval = undefined;
}
};

Expand Down
18 changes: 14 additions & 4 deletions packages/react-bindings/src/hooks/callStateHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,23 @@ export const useParticipantCount = () => {
};

/**
* Returns the duration of the call in seconds.
* Returns the duration of the call session in seconds.
*
* @category Call State
*/
export const useCallDuration = () => {
const { duration$ } = useCallState();
return useObservableValue(duration$);
export const useCallSessionDuration = () => {
const { sessionDuration$ } = useCallState();
return useObservableValue(sessionDuration$);
};

/**
* Returns the duration of the livestream seconds.
*
* @category Call State
*/
export const useCallLiveDuration = () => {
const { liveDuration$ } = useCallState();
return useObservableValue(liveDuration$);
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ Here is an excerpt of the available call state hooks:
| `useCallCreatedAt` | The time the call was created. |
| `useCallCreatedBy` | The user that created the call. |
| `useCallCustomData` | The custom data attached to the call. |
| `useCallDuration` | The call duration since the start of the call in seconds. |
| `useCallSessionDuration` | The call duration since the start of the call in seconds. |
| `useCallLiveDuration` | The call duration since the start of the call in seconds. |
| `useCallEgress` | The egress information of the call. |
| `useCallEndedBy` | The user that ended the call. |
| `useCallIngress` | The ingress information of the call. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ Here is an excerpt of the available call state hooks:
| `useCallCreatedAt` | The time the call was created. |
| `useCallCreatedBy` | The user that created the call. |
| `useCallCustomData` | The custom data attached to the call. |
| `useCallSessionDuration` | The duration since the start of the call session in seconds. |
| `useCallLiveDuration` | The duration since the start of the call livestream in seconds. |
| `useCallEgress` | The egress information of the call. |
| `useCallEndedBy` | The user that ended the call. |
| `useCallIngress` | The ingress information of the call. |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useEffect, useMemo, useState } from 'react';
import {
CallingState,
CancelCallConfirmButton,
Expand Down Expand Up @@ -49,13 +48,9 @@ const formatTime = (timeInSeconds: number) => {
};

const Elapsed = () => {
const [elapsed, setElapsed] = useState<string>();
const { useCallDuration } = useCallStateHooks();
const duration = useCallDuration();

useEffect(() => {
setElapsed(formatTime(duration));
}, [duration]);
const elapsed = formatTime(duration);

return (
<div className="rd__header__elapsed">
Expand Down

0 comments on commit 0366dea

Please sign in to comment.