Skip to content

Commit

Permalink
feat: update room api methods (#70)
Browse files Browse the repository at this point in the history
* refactor: expose getBaseUrl and throw error when something not working

* feat: add setClientName, and createDataChannel methods

- add setClientName
- add createDataChannel
- update params and responses on some methods

* fix: change ordered param to mode

* feat: expose setClientName & createDataChannel

- expose setClientName & createDataChannel
- write doc for the methods

* doc: more explanation about leaveRoom method
  • Loading branch information
faiq-naufal authored Oct 25, 2023
1 parent a37c487 commit dcd81d9
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 45 deletions.
12 changes: 10 additions & 2 deletions packages/room/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,25 @@ await room.endRoom(roomData.data.roomId);

A method to create and register a new client to the room. It expects two parameters. The `roomId` is required. If the client prefers to set their own client ID, the second client ID parameter can be set. This method will return a promise.

- `room.setClientName(roomId: string, clientId: string, clientName: string)`

A method to set a client name based on `clientId`. This is useful for setting a friendly name or label on a specific client. It requires `roomId`, `clientId` and `clientName` parameters to be set. This method will return a promise.

- `room.createPeer(roomId: string, clientId: string)`

A method to create a peer that manages the WebRTC peer to peer connection. It requires `roomId` and `clientId` parameters to be set. This method will return a promise.

- `room.createDataChannel(roomId: string, name: string, ordered?: boolean)`

A method to create a data channel server on a specific room. Data channel is useful for broadcasting data to all connected clients through WebRTC connection. It requires `roomId` and channel `name` parameters to be set. When not set, by default the `ordered` value will be true. This method will return a promise.

- `room.on(eventName: string, callback: Function)`

A method to listen a specific room event. It requires `eventName` and `callback` function parameters to be set.

- `room.leaveRoom(roomId: string, clientId: string)`
- `room.leaveRoom(roomId: string, clientId: string, useBeacon?: boolean)`

A method to trigger a proper leave room functionality for client. It requires `roomId` and `clientId` parameters to be set. This method will return a promise.
A method to trigger a proper leave room functionality for a client. It requires `roomId` and `clientId` parameters to be set. When `useBeacon` option is set to true, this method will use [sendBeacon()](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) method for leaving the room. When `useBeacon` option is set to false (default), this method will use the standard fetch API. This method will return a promise.

- `room.endRoom(roomId: string)`

Expand Down
33 changes: 30 additions & 3 deletions packages/room/api/api-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,55 @@ export declare namespace RoomAPIType {
track_id: string
}

type RegisterClientRequestBody = {
uid?: string
name?: string
}

type Bitrates = {
audio: number
audio_red: number
video: number
video_high: number
video_mid: number
video_low: number
initial_bandwidth: number
}

type BaseResponseBody = {
code: number
ok: boolean
data: object
message: string
}

type CreateRoomResponseBody = BaseResponseBody & {
data: {
id: string
room_id: string
name: string
bitrates_config: Bitrates
}
}

type GetRoomResponseBody = BaseResponseBody & {
data: {
id: string
room_id: string
name: string
}
}

type RegisterClientResponseBody = BaseResponseBody & {
data: {
client_id: string
name: string
bitrates: Bitrates
}
}

type SetClientNameResponse = BaseResponseBody & {
data: {
client_id: string
name: string
bitrates: Bitrates
}
}

Expand Down
176 changes: 160 additions & 16 deletions packages/room/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,24 @@ export const createApi = ({ fetcher }) => {
})

const data = response.data || {}
const bitrates = data.bitrates_config || {}

const room = {
code: response.code || 500,
ok: response.ok || false,
message: response.message || '',
data: {
roomId: data.id || '',
roomId: data.room_id || '',
roomName: data.name || '',
bitrates: {
audio: bitrates.audio || 0,
audioRed: bitrates.audio_red || 0,
video: bitrates.video || 0,
videoHigh: bitrates.video_high || 0,
videoMid: bitrates.video_mid || 0,
videoLow: bitrates.video_low || 0,
initialBandwidth: bitrates.initial_bandwidth || 0,
},
},
}

Expand All @@ -46,8 +58,9 @@ export const createApi = ({ fetcher }) => {
const room = {
code: response.code || 500,
ok: response.ok || false,
message: response.message || '',
data: {
roomId: data.id || '',
roomId: data.room_id || '',
roomName: data.name || '',
},
}
Expand All @@ -57,21 +70,26 @@ export const createApi = ({ fetcher }) => {

/**
* @param {string} roomId
* @param {string} [clientId]
* @param {{clientId?: string, clientName?: string}} [config]
*/
registerClient = async (roomId, clientId = '') => {
registerClient = async (roomId, config = {}) => {
if (typeof roomId !== 'string' || roomId.trim().length === 0) {
throw new Error('Room ID must be a valid string')
}

if (typeof clientId !== 'string') {
throw new TypeError('Client ID must be a valid string')
/** @type {import('./api-types.js').RoomAPIType.RegisterClientRequestBody} */
const body = {}

if (config.clientId && config.clientId.trim().length > 0) {
body.uid = config.clientId
}

if (config.clientName && config.clientName.trim().length > 0) {
body.name = config.clientName
}

const options =
clientId.trim().length > 0
? { body: JSON.stringify({ uid: clientId }) }
: undefined
body.uid || body.name ? { body: JSON.stringify(body) } : undefined

/** @type {import('./api-types.js').RoomAPIType.RegisterClientResponseBody} */
const response = await this._fetcher.post(
Expand All @@ -80,18 +98,82 @@ export const createApi = ({ fetcher }) => {
)

const data = response.data || {}
const bitrates = data.bitrates || {}

const client = {
code: response.code || 500,
ok: response.ok || false,
message: response.message || '',
data: {
clientId: data.client_id || '',
clientName: data.name || '',
bitrates: {
audio: bitrates.audio || 0,
audioRed: bitrates.audio_red || 0,
video: bitrates.video || 0,
videoHigh: bitrates.video_high || 0,
videoMid: bitrates.video_mid || 0,
videoLow: bitrates.video_low || 0,
initialBandwidth: bitrates.initial_bandwidth || 0,
},
},
}

return client
}

/**
*
* @param {string} roomId
* @param {string} clientId
* @param {string} clientName
*/
setClientName = async (roomId, clientId, clientName) => {
if (roomId.trim().length === 0) {
throw new Error('Room ID must be a valid string')
}

if (clientId.trim().length === 0) {
throw new Error('Client ID must be a valid string')
}

if (clientName.trim().length === 0) {
throw new Error('Client name must be a valid string')
}

/** @type {import('./api-types.js').RoomAPIType.SetClientNameResponse} */
const response = await this._fetcher.put(
`/rooms/${roomId}/setname/${clientId}`,
{
body: JSON.stringify({
name: clientName,
}),
}
)

const data = response.data || {}
const bitrates = data.bitrates || {}

return {
code: response.code || 500,
ok: response.ok || false,
message: response.message || '',
data: {
clientId: data.client_id || '',
clientName: data.name || '',
bitrates: {
audio: bitrates.audio || 0,
audioRed: bitrates.audio_red || 0,
video: bitrates.video || 0,
videoHigh: bitrates.video_high || 0,
videoMid: bitrates.video_mid || 0,
videoLow: bitrates.video_low || 0,
initialBandwidth: bitrates.initial_bandwidth || 0,
},
},
}
}

/**
* @param {string} roomId
* @param {string} clientId
Expand All @@ -113,7 +195,8 @@ export const createApi = ({ fetcher }) => {
const result = {
code: response.code || 500,
ok: response.ok || false,
data: response.data || null,
message: response.message || '',
data: null,
}

return result
Expand All @@ -136,7 +219,8 @@ export const createApi = ({ fetcher }) => {
const result = {
code: response.code || 500,
ok: response.ok || false,
data: response.data || null,
message: response.message || '',
data: null,
}

return result
Expand Down Expand Up @@ -167,6 +251,7 @@ export const createApi = ({ fetcher }) => {
const result = {
code: response.code || 500,
ok: response.ok || false,
message: response.message || '',
data: {
answer: data.answer,
},
Expand Down Expand Up @@ -202,7 +287,8 @@ export const createApi = ({ fetcher }) => {
const result = {
code: response.code || 500,
ok: response.ok || false,
data: response.data || null,
message: response.message || '',
data: null,
}

return result
Expand Down Expand Up @@ -235,7 +321,8 @@ export const createApi = ({ fetcher }) => {
const result = {
code: response.code || 500,
ok: response.ok || false,
data: response.data || null,
message: response.message || '',
data: null,
}

return result
Expand All @@ -244,12 +331,30 @@ export const createApi = ({ fetcher }) => {
/**
* @param {string} roomId
* @param {string} clientId
* @param {boolean} useBeacon
*/
leaveRoom = async (roomId, clientId) => {
leaveRoom = async (roomId, clientId, useBeacon = false) => {
if (!roomId || !clientId) {
throw new Error('Room ID, and client ID are required')
}

const endpoint = `/rooms/${roomId}/leave/${clientId}`

if (useBeacon) {
const response = navigator.sendBeacon(
`${this._fetcher.getBaseUrl()}${endpoint}`
)

const result = {
code: response ? 200 : 500,
ok: response,
message: response ? 'OK' : '',
data: null,
}

return result
}

/** @type {import('./api-types.js').RoomAPIType.BaseResponseBody} */
const response = await this._fetcher.delete(
`/rooms/${roomId}/leave/${clientId}`,
Expand All @@ -261,7 +366,8 @@ export const createApi = ({ fetcher }) => {
const result = {
code: response.code || 500,
ok: response.ok || false,
data: response.data || null,
message: response.message || '',
data: null,
}

return result
Expand All @@ -281,11 +387,47 @@ export const createApi = ({ fetcher }) => {
const result = {
code: response.code || 500,
ok: response.ok || false,
data: response.data || null,
message: response.message || '',
data: null,
}

return result
}

/**
*
* @param {string} roomId
* @param {string} name
* @param {boolean} ordered
*/
createDataChannel = async (roomId, name, ordered = true) => {
if (typeof roomId !== 'string' || roomId.trim().length === 0) {
throw new Error('Room ID must be a valid string')
}

if (typeof roomId !== 'string' || roomId.trim().length === 0) {
throw new Error('Channel name must be a valid string')
}

if (typeof ordered !== 'boolean') {
ordered = true
}

/** @type {import('./api-types.js').RoomAPIType.BaseResponseBody} */
const response = await this._fetcher.post(
`/room/${roomId}/channel/create`,
{
body: JSON.stringify({ name: name, mode: ordered }),
}
)

return {
code: response.code || 500,
ok: response.ok || false,
message: response.message || '',
data: null,
}
}
}

return {
Expand All @@ -296,13 +438,15 @@ export const createApi = ({ fetcher }) => {
createRoom: api.createRoom,
getRoom: api.getRoom,
registerClient: api.registerClient,
setClientName: api.setClientName,
sendIceCandidate: api.sendIceCandidate,
checkNegotiateAllowed: api.checkNegotiateAllowed,
negotiateConnection: api.negotiateConnection,
setTrackSources: api.setTrackSources,
subscribeTracks: api.subscribeTracks,
leaveRoom: api.leaveRoom,
endRoom: api.endRoom,
createDataChannel: api.createDataChannel,
}
},
}
Expand Down
Loading

0 comments on commit dcd81d9

Please sign in to comment.