Skip to content

Commit

Permalink
Add RoomSession.joinAudience() method (#557)
Browse files Browse the repository at this point in the history
* add option to keep track of the session auth key

* update types

* add skeleton for joinAudience

* fix tests

* add initial implementation for the joinAudience

* update demo to use joinAudience

* fix test

* rename utils and interfaces, move to external file, pr feedback

* move values to util and always default to true

* update name

* add changesets

* mark joinAudience as internal, remove mocked flags

* restore to join
  • Loading branch information
framini authored Jun 14, 2022
1 parent d308daf commit f15032f
Show file tree
Hide file tree
Showing 18 changed files with 194 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/itchy-boxes-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@signalwire/core': patch
---

[internal] Add ability to track the Authorization state
5 changes: 5 additions & 0 deletions .changeset/strong-lobsters-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@signalwire/js': minor
---

[internal] Add RoomSession.joinAudience method
5 changes: 5 additions & 0 deletions .changeset/tiny-points-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@signalwire/webrtc': patch
---

[internal] Add ability to update the media options
4 changes: 4 additions & 0 deletions internal/playground-js/src/heroku/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ function meter(el, val) {
}

const initializeMicAnalyzer = async (stream) => {
if (!stream) {
return
}

const el = document.getElementById('mic-meter')
micAnalyzer = await createMicrophoneAnalyzer(stream)
micAnalyzer.on('volumeChanged', (vol) => {
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/BaseComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SDKWorkerDefinition,
SessionAuthStatus,
SDKWorkerHooks,
Authorization,
} from './utils/interfaces'
import { EventEmitter } from './utils/EventEmitter'
import { SDKState } from './redux/interfaces'
Expand All @@ -32,6 +33,7 @@ import {
} from './types'
import {
getAuthError,
getAuthState,
getAuthStatus,
} from './redux/features/session/sessionSelectors'
import { compoundEventAttachAction } from './redux/actions'
Expand Down Expand Up @@ -835,6 +837,11 @@ export class BaseComponent<
return getAuthStatus(this.store.getState())
}

/** @internal */
protected get _sessionAuthState(): Authorization | undefined {
return getAuthState(this.store.getState())
}

/** @internal */
protected _waitUntilSessionAuthorized(): Promise<this> {
const authStatus = getAuthStatus(this.store.getState())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('componentCleanupSaga', () => {
protocol: '',
iceServers: [],
authStatus: 'unknown',
authState: undefined,
authError: undefined,
authCount: 0,
},
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/redux/features/session/sessionSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ export const getAuthStatus = ({ session }: SDKState) => {
export const getAuthError = ({ session }: SDKState) => {
return session.authError
}

export const getAuthState = ({ session }: SDKState) => {
return session.authState
}
16 changes: 16 additions & 0 deletions packages/core/src/redux/features/session/sessionSlice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@ describe('SessionState Tests', () => {
protocol: rpcConnectResultVRT.protocol,
iceServers: rpcConnectResultVRT.ice_servers,
authStatus: 'authorized',
authState: {
audio_allowed: true,
project: '8f0a119a-cda7-4497-a47d-c81493b824d4',
resource: '9c80f1e8-9430-4070-a043-937eb3a96b38',
room: {
name: 'lobby',
scopes: ['room.self.audio_mute', 'room.self.audio_unmute'],
},
scope_id: '26675883-8499-4ee9-85eb-691c4aa209f8',
scopes: ['video'],
signature:
'SGZtkRD9fvuBAOUp1UF56zESxdEvGT6qSGZtkRD9fvuBAOUp1UF56zESxdEvGT6q',
type: 'video',
user_name: 'Joe',
video_allowed: true,
},
authError: undefined,
authCount: 1,
})
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/redux/features/session/sessionSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const initialSessionState: DeepReadonly<SessionState> = {
protocol: '',
iceServers: [],
authStatus: 'unknown',
authState: undefined,
authError: undefined,
authCount: 0,
}
Expand All @@ -25,6 +26,7 @@ const sessionSlice = createDestroyableSlice({
return {
...state,
authStatus: 'authorized',
authState: payload?.authorization,
authCount: state.authCount + 1,
protocol: payload?.protocol ?? '',
iceServers: payload?.ice_servers ?? [],
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/redux/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SessionEvents,
JSONRPCMethod,
BaseConnectionState,
Authorization,
} from '../utils/interfaces'
import type {
VideoAPIEventParams,
Expand Down Expand Up @@ -62,6 +63,7 @@ export interface SessionState {
protocol: string
iceServers?: RTCIceServer[]
authStatus: SessionAuthStatus
authState?: Authorization
authError?: SessionAuthError
authCount: number
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export const rpcConnectResultVRT: RPCConnectResult = {
},
signature:
'SGZtkRD9fvuBAOUp1UF56zESxdEvGT6qSGZtkRD9fvuBAOUp1UF56zESxdEvGT6q',
audio_allowed: true,
video_allowed: true,
},
protocol:
'signalwire_SGZtkRD9fvuBAOUp1UF56zESxdEvGT6qSGZtkRD9fvuBAOUp1UF56zESxdEvGT6q_03e8c927-8ea3-4661-86d5-778c3e03296a_8f0a119a-cda7-4497-a47d-c81493b824d4',
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/utils/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export interface SessionRequestObject {
reject: (value: unknown) => void
}

interface Authorization {
export interface Authorization {
type: 'video'
project: string
scopes: string[]
Expand All @@ -171,6 +171,8 @@ interface Authorization {
}
signature: string
expires_at?: number
audio_allowed?: boolean
video_allowed?: boolean
}

export interface RPCConnectResult {
Expand Down
4 changes: 3 additions & 1 deletion packages/js/src/BaseRoomSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export interface BaseRoomSession<T>
BaseComponentContract,
BaseConnectionContract<RoomSessionObjectEvents> {
join(): Promise<T>
/** @internal */
joinAudience(options?: { audio?: boolean; video?: boolean }): Promise<T>
leave(): Promise<void>
}

Expand Down Expand Up @@ -173,7 +175,7 @@ export class RoomSessionConnection
protected attachOnSubscribedWorkers(payload: VideoRoomEventParams) {
this.runWorker('memberPositionWorker', {
worker: workers.memberPositionWorker,
initialState: payload
initialState: payload,
})
}

Expand Down
3 changes: 3 additions & 0 deletions packages/js/src/RoomSession.docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ export interface RoomSessionDocs<T>
*/
join(): Promise<this>

/** @internal */
joinAudience(options?: { audio?: boolean; video?: boolean }): Promise<this>

/**
* Leaves the room. This detaches all the locally originating streams from the
* room.
Expand Down
63 changes: 60 additions & 3 deletions packages/js/src/RoomSession.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { UserOptions, AssertSameType, getLogger } from '@signalwire/core'
import {
UserOptions,
AssertSameType,
getLogger,
Authorization,
} from '@signalwire/core'
import { createClient } from './createClient'
import type { MakeRoomOptions } from './Client'
import { BaseRoomSession } from './BaseRoomSession'
import { RoomSessionDocs } from './RoomSession.docs'
import {
getJoinAudienceMediaParams,
isValidJoinAudienceMediaParams,
} from './utils/roomSession'
import type { MakeRoomOptions } from './Client'
import type { RoomSessionDocs } from './RoomSession.docs'
import type { RoomSessionJoinAudienceParams } from './utils/interfaces'

const VIDEO_CONSTRAINTS: MediaTrackConstraints = {
aspectRatio: { ideal: 16 / 9 },
Expand Down Expand Up @@ -149,8 +159,55 @@ export const RoomSession = function (roomOptions: RoomSessionOptions) {
})
}

const joinAudience = (
params?: RoomSessionJoinAudienceParams
) => {
return new Promise(async (resolve, reject) => {
try {
// @ts-expect-error
room.attachPreConnectWorkers()

const session = await client.connect()

// @ts-expect-error
const authState: Authorization = session._sessionAuthState
const mediaOptions = getJoinAudienceMediaParams({
authState,
...params,
})

if (!isValidJoinAudienceMediaParams(mediaOptions)) {
await session.disconnect()
return reject(
new Error(
'[joinAudience] Either (or both) `audio` and `video` must be `true` when calling this method.'
)
)
}

// @ts-expect-error
room.updateMediaOptions(mediaOptions)

room.once('room.subscribed', (payload) => {
// @ts-expect-error
room.attachOnSubscribedWorkers(payload)
resolve(room)
})

await room.join()
} catch (error) {
getLogger().error('RoomSession JoinAudience', error)
// Disconnect the underlay client in case of media/signaling errors
client.disconnect()

reject(error)
}
})
}

const interceptors = {
join,
joinAudience,
} as const

return new Proxy<Omit<RoomSession, 'new'>>(room, {
Expand Down
5 changes: 5 additions & 0 deletions packages/js/src/utils/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,8 @@ export interface RoomSessionDeviceMethods

export interface RoomScreenShareMethods
extends RoomMemberSelfMethodsInterface {}

export interface RoomSessionJoinAudienceParams {
audio?: boolean
video?: boolean
}
54 changes: 54 additions & 0 deletions packages/js/src/utils/roomSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { getLogger } from '@signalwire/core'
import type { Authorization } from '@signalwire/core'
import type { RoomSessionJoinAudienceParams } from './interfaces'

// `joinAudience` utils
const getJoinAudienceMediaParams = ({
authState,
audio = true,
video = true,
}: RoomSessionJoinAudienceParams & {
authState: Authorization
}) => {
const getMediaValue = ({
remote,
local,
kind,
}: {
remote?: boolean
local?: boolean
kind: 'audio' | 'video'
}) => {
if (!remote && local) {
getLogger().warn(
`[joinAudience] ${kind} is currently not allowed on this room.`
)
}

return !!(remote && local)
}

return {
audio: false,
video: false,
negotiateAudio: getMediaValue({
remote: authState.audio_allowed,
local: audio,
kind: 'audio',
}),
negotiateVideo: getMediaValue({
remote: authState.video_allowed,
local: video,
kind: 'video',
}),
}
}

const isValidJoinAudienceMediaParams = (
options: Record<string, boolean | undefined>
) => {
// At least one value must be true
return Object.values(options).some(Boolean)
}

export { getJoinAudienceMediaParams, isValidJoinAudienceMediaParams }
13 changes: 13 additions & 0 deletions packages/webrtc/src/BaseConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,19 @@ export class BaseConnection<EventTypes extends EventEmitter.ValidEventTypes>
}
}

/** @internal */
updateMediaOptions(options: {
audio?: boolean
video?: boolean
negotiateAudio?: boolean
negotiateVideo?: boolean
}) {
this.options = {
...this.options,
...options,
}
}

/** @internal */
private _hangup(params: any = {}) {
const {
Expand Down

0 comments on commit f15032f

Please sign in to comment.