diff --git a/packages/restapi/src/lib/spaceV2/SpaceV2.ts b/packages/restapi/src/lib/spaceV2/SpaceV2.ts index 367a94412..fe64f1fbe 100644 --- a/packages/restapi/src/lib/spaceV2/SpaceV2.ts +++ b/packages/restapi/src/lib/spaceV2/SpaceV2.ts @@ -1,20 +1,33 @@ -import Constants, { ENV } from "../constants"; -import { VIDEO_CALL_TYPE } from "../payloads/constants"; -import { SignerType, VideoCallData, VideoCallStatus } from "../types"; +import { produce } from "immer"; -export const initVideoCallData: VideoCallData = { - meta: { - chatId: '', - initiator: { - address: '', - signal: null, - }, - broadcast: { - livepeerInfo: null, - hostAddress: '', - coHostAddress: '', - }, - }, +import { join } from "./join"; + +import Constants, { ENV } from "../constants"; +import { EnvOptionsType, SignerType, SpaceDTO, SpaceV2Data, VideoCallStatus } from "../types"; +import { pCAIP10ToWallet } from "../helpers"; + +export const initSpaceInfo: SpaceDTO = { + members: [], + pendingMembers: [], + contractAddressERC20: null, + numberOfERC20: -1, + contractAddressNFT: null, + numberOfNFTTokens: -1, + verificationProof: '', + spaceImage: null, + spaceName: '', + isPublic: false, + spaceDescription: '', + spaceCreator: '', + spaceId: '', + scheduleAt: null, + scheduleEnd: null, + status: null, + inviteeDetails: {} + }; + +export const initSpaceV2Data: SpaceV2Data = { + spaceInfo: initSpaceInfo, local: { stream: null, audio: null, @@ -33,50 +46,67 @@ export const initVideoCallData: VideoCallData = { ], }; +export interface SpaceV2ConstructorType extends EnvOptionsType { + signer: SignerType; + pgpPrivateKey: string; + chainId: number; + address: string; + setSpaceV2Data: (fn: (data: SpaceV2Data) => SpaceV2Data) => void; +} + export class SpaceV2 { // user, call related info protected signer: SignerType; protected chainId: number; protected pgpPrivateKey: string; protected env: ENV; - protected callType: VIDEO_CALL_TYPE; private peerConnections: Map = new Map(); - protected data!: VideoCallData; - setData: (fn: (data: VideoCallData) => VideoCallData) => void; - - constructor({ - signer, - chainId, - pgpPrivateKey, - env = Constants.ENV.PROD, - callType = VIDEO_CALL_TYPE.PUSH_VIDEO, - setData, - }: { - signer: SignerType; - chainId: number; - pgpPrivateKey: string; - env?: ENV; - callType?: VIDEO_CALL_TYPE; - setData: (fn: (data: VideoCallData) => VideoCallData) => void; - }) { + protected data!: SpaceV2Data; + + setSpaceV2Data: (fn: (data: SpaceV2Data) => SpaceV2Data) => void; + + constructor(options: SpaceV2ConstructorType) { + const { + signer, + pgpPrivateKey, + address, + chainId, + env = Constants.ENV.PROD, + setSpaceV2Data, // to update the 'spaceData' state maintained by the developer + } = options || {}; + this.signer = signer; this.chainId = chainId; this.pgpPrivateKey = pgpPrivateKey; this.env = env; - this.callType = callType; - setData(() => initVideoCallData); + setSpaceV2Data(() => initSpaceV2Data); // set the state updating function - this.setData = function (fn) { + this.setSpaceV2Data = function (fn) { // update the react state - setData(fn); + setSpaceV2Data(fn); // update the class variable this.data = fn(this.data); }; + + + // initializing state + // set the local address inside video call 'data' + this.setSpaceV2Data((oldSpaceV2Data) => { + return produce(oldSpaceV2Data, (draft) => { + draft.local.address = pCAIP10ToWallet(address); + }); + }); + + // init the state maintained by the developer + setSpaceV2Data(() => initSpaceV2Data); + + // init the spaceSpecificData class variable + this.data = initSpaceV2Data; } // Add a connected peer to the space @@ -125,15 +155,11 @@ export class SpaceV2 { */ } - async join(options: any) { - /** - * will contain logic to handle joining of speakers and listeners based on role - */ - } - async invite(options: any) { /** * will contain logic to handle invites made by host to listener */ } + + public join = join; } diff --git a/packages/restapi/src/lib/spaceV2/approve.ts b/packages/restapi/src/lib/spaceV2/approve.ts new file mode 100644 index 000000000..d203236bb --- /dev/null +++ b/packages/restapi/src/lib/spaceV2/approve.ts @@ -0,0 +1,59 @@ +import { + isValidETHAddress, +} from '../helpers'; +import Constants from '../constants'; +import { + EnvOptionsType, + SignerType +} from '../types'; +import { + approve as approveRequest +} from '../chat/approveRequest'; + +interface ApproveRequestOptionsType extends EnvOptionsType { + senderAddress: string; + pgpPrivateKey ? : string | null; + status ? : 'Approved'; + account ? : string | null; + signer ? : SignerType | null; +} + +export const approve = async ( + options: ApproveRequestOptionsType +): Promise < string > => { + const { + status = 'Approved', + account = null, + signer = null, + senderAddress, // space id + env = Constants.ENV.PROD, + pgpPrivateKey = null, + } = options || {}; + + try { + + if (account == null && signer == null) { + throw new Error(`At least one from account or signer is necessary!`); + } + + if (!isValidETHAddress(senderAddress) && !senderAddress.startsWith("spaces:")) { + throw new Error("Not a valid spaceId or ETH address"); + } + return await approveRequest({ + status: status, + account: account, + signer: signer, + senderAddress: senderAddress, + env: env, + pgpPrivateKey: pgpPrivateKey + }) + } catch (err) { + console.error( + `[Push SDK] - API - Error - API ${approve.name} -: `, + err + ); + throw Error( + `[Push SDK] - API - Error - API ${approve.name} -: ${err}` + ); + } +}; diff --git a/packages/restapi/src/lib/spaceV2/get.ts b/packages/restapi/src/lib/spaceV2/get.ts new file mode 100644 index 000000000..17bb06527 --- /dev/null +++ b/packages/restapi/src/lib/spaceV2/get.ts @@ -0,0 +1,39 @@ +import Constants, { + ENV +} from '../constants'; +import { + SpaceDTO +} from '../types'; +import { + groupDtoToSpaceDto +} from './../chat/helpers'; +import { + getGroup +} from '../chat/getGroup'; + +export interface GetSpaceType { + spaceId: string, + env ? : ENV +} + +export const get = async ( + options: GetSpaceType +): Promise < SpaceDTO > => { + const { + spaceId, + env = Constants.ENV.PROD + } = options || {}; + try { + if (spaceId == null || spaceId.length == 0) { + throw new Error(`spaceId cannot be null or empty`); + } + const group = await getGroup({ + chatId: spaceId, + env + }) + return groupDtoToSpaceDto(group); + } catch (err) { + console.error(`[Push SDK] - API - Error - API ${get.name} -: `, err); + throw Error(`[Push SDK] - API - Error - API ${get.name} -: ${err}`); + } +}; diff --git a/packages/restapi/src/lib/spaceV2/helpers/getPlainAddress.ts b/packages/restapi/src/lib/spaceV2/helpers/getPlainAddress.ts new file mode 100644 index 000000000..d24da128a --- /dev/null +++ b/packages/restapi/src/lib/spaceV2/helpers/getPlainAddress.ts @@ -0,0 +1,5 @@ +const getPlainAddress = (prefixedAddress: string) => { + return prefixedAddress.replace('eip155:', ''); +}; + +export default getPlainAddress; diff --git a/packages/restapi/src/lib/spaceV2/join.ts b/packages/restapi/src/lib/spaceV2/join.ts new file mode 100644 index 000000000..bd6ff86b4 --- /dev/null +++ b/packages/restapi/src/lib/spaceV2/join.ts @@ -0,0 +1,87 @@ +import { ChatStatus } from '../types'; +import { approve } from './approve'; +import { get } from './get'; +import getPlainAddress from './helpers/getPlainAddress'; +import { SpaceV2 } from './SpaceV2'; + +export async function join(this: SpaceV2) { + try { + const space = await get({ + spaceId: this.data.spaceInfo.spaceId, + env: this.env, + }); + + if (space.status !== ChatStatus.ACTIVE) + throw new Error('Space not active yet'); + + // checking what is the current role of caller address + + let isSpeaker = false; + let isListener = false; + const localAddress = getPlainAddress(this.data.local.address); + space.members.forEach((member) => { + if (getPlainAddress(member.wallet) === localAddress) { + if (member.isSpeaker) { + isSpeaker = true; + return; + } else { + isListener = true; + return; + } + } + }); + let isSpeakerPending = false; + space.pendingMembers.forEach((pendingMember) => { + if ( + getPlainAddress(pendingMember.wallet) === localAddress && + pendingMember.isSpeaker + ) { + isSpeakerPending = true; + return; + } + }); + + console.log( + 'ISSPEAKER', + isSpeaker, + 'isListener', + isListener, + 'isSpeakerPending', + isSpeakerPending + ); + + // acc to the found role (speaker or listner), executing req logic + + // if speaker is pending then approve first or if listner is pending/not found then approve first + if (!isSpeaker && !isListener) { + console.log('CALLING APPROVE'); + await approve({ + signer: this.signer, + pgpPrivateKey: this.pgpPrivateKey, + senderAddress: this.data.spaceInfo.spaceId, + env: this.env, + }); + } + + if (isSpeaker || isSpeakerPending) { + // Call joinSpeaker + } else { + // Call joinListener + } + + const updatedSpaceInfo = await get({ + spaceId: this.data.spaceInfo.spaceId, + env: this.env, + }); + + console.log('UPDATED SPACE', updatedSpaceInfo); + // update space specific data + this.setSpaceV2Data(() => ({ + ...this.data, + spaceInfo: updatedSpaceInfo + })); + } catch (err) { + console.error(`[Push SDK] - API - Error - API ${join.name} -: `, err); + throw Error(`[Push SDK] - API - Error - API ${join.name} -: ${err}`); + } +} diff --git a/packages/restapi/src/lib/types/index.ts b/packages/restapi/src/lib/types/index.ts index 3394cdbec..293b95c88 100644 --- a/packages/restapi/src/lib/types/index.ts +++ b/packages/restapi/src/lib/types/index.ts @@ -678,6 +678,17 @@ export type VideoCallData = { incoming: PeerData[]; }; +export type SpaceV2Data = { + spaceInfo: SpaceDTO; + local: { + stream: IMediaStream; + audio: boolean | null; + video: boolean | null; + address: string; + }; + incoming: PeerData[]; +} + export type VideoCreateInputOptions = { video?: boolean; audio?: boolean;