diff --git a/docs/classes/internal.NotificationService.md b/docs/classes/internal.NotificationService.md index 13449121..70e60e2a 100644 --- a/docs/classes/internal.NotificationService.md +++ b/docs/classes/internal.NotificationService.md @@ -12,6 +12,8 @@ The NotificationService allows inviting participants to a conference. ### Methods +- [subscribe](internal.NotificationService.md#subscribe) +- [unsubscribe](internal.NotificationService.md#unsubscribe) - [decline](internal.NotificationService.md#decline) - [invite](internal.NotificationService.md#invite) - [onInvitationReceived](internal.NotificationService.md#oninvitationreceived) @@ -24,6 +26,42 @@ The NotificationService allows inviting participants to a conference. ## Methods +### subscribe + +▸ **subscribe**(`events`): `Promise`<`void`\> + +Subscribes to the specified notifications. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `events` | [`Subscription`](../interfaces/internal.Subscription.md)[] | An array of the subscribed subscription types. | + +#### Returns + +`Promise`<`void`\> + +___ + +### unsubscribe + +▸ **unsubscribe**(`events`): `Promise`<`void`\> + +Unsubscribes from the specified notifications. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `events` | [`Subscription`](../interfaces/internal.Subscription.md)[] | An array of the subscribed subscription types. | + +#### Returns + +`Promise`<`void`\> + +___ + ### decline ▸ **decline**(`conference`): `Promise`<`void`\> diff --git a/docs/interfaces/internal.Subscription.md b/docs/interfaces/internal.Subscription.md new file mode 100644 index 00000000..08bdeed2 --- /dev/null +++ b/docs/interfaces/internal.Subscription.md @@ -0,0 +1,28 @@ +# Interface: Subscription + +[internal](../modules/internal.md).Subscription + +The Subscription model is an interface for all subscription types. + +## Table of contents + +### Properties + +- [type](internal.Subscription.md#type) +- [conferenceAlias](internal.Subscription.md#conferencealias) + +## Properties + +### type + +• **type**: `string` + +The subscription type. + +___ + +### conferenceAlias + +• **conferenceAlias**: `string` + +The conference alias. diff --git a/docs/modules/internal.md b/docs/modules/internal.md index 38786a47..4d8b7ed5 100644 --- a/docs/modules/internal.md +++ b/docs/modules/internal.md @@ -69,6 +69,7 @@ - [FileConverted](../interfaces/internal.FileConverted.md) - [FilePresentation](../interfaces/internal.FilePresentation.md) - [InvitationReceivedEventType](../interfaces/internal.InvitationReceivedEventType.md) +- [Subscription](../interfaces/internal.Subscription.md) - [RecordingStatusUpdatedEventType](../interfaces/internal.RecordingStatusUpdatedEventType.md) - [Recording](../interfaces/internal.Recording.md) - [VideoPresentationEventType](../interfaces/internal.VideoPresentationEventType.md) diff --git a/example/src/components/DolbyIOProvider/DolbyIOProvider.tsx b/example/src/components/DolbyIOProvider/DolbyIOProvider.tsx index 55987a2a..5a00514b 100644 --- a/example/src/components/DolbyIOProvider/DolbyIOProvider.tsx +++ b/example/src/components/DolbyIOProvider/DolbyIOProvider.tsx @@ -21,6 +21,9 @@ import type { Conference, Participant, } from '../../../../src/services/conference/models'; +import { + SubscriptionType +} from '../../../../src/services/notification/models'; export interface IDolbyIOProvider { isInitialized?: Boolean; @@ -211,6 +214,17 @@ const DolbyIOProvider: React.FC = ({ children }) => { '@conference-previous', JSON.stringify(joinedConference) ); + + await CommsAPI.notification.subscribe( + [ + SubscriptionType.ActiveParticipants, + SubscriptionType.ConferenceCreated, + SubscriptionType.ConferenceEnded, + SubscriptionType.InvitationReceived, + SubscriptionType.ParticipantJoined, + SubscriptionType.ParticipantLeft + ].map( (s) => { return { type: s, conferenceAlias: alias } }) + ); } catch (e: any) { Alert.alert('Conference not joined', e.toString()); } @@ -294,6 +308,17 @@ const DolbyIOProvider: React.FC = ({ children }) => { }; const leave = async (leaveRoom: boolean) => { try { + await CommsAPI.notification.unsubscribe( + [ + SubscriptionType.ActiveParticipants, + SubscriptionType.ConferenceCreated, + SubscriptionType.ConferenceEnded, + SubscriptionType.InvitationReceived, + SubscriptionType.ParticipantJoined, + SubscriptionType.ParticipantLeft + ].map( (s) => { return { type: s, conferenceAlias: conference?.alias ?? "" } }) + ); + const conferenceLeaveOptions = { leaveRoom, }; diff --git a/ios/Services/Models/SubscriptionDTO.swift b/ios/Services/Models/SubscriptionDTO.swift new file mode 100644 index 00000000..07adac49 --- /dev/null +++ b/ios/Services/Models/SubscriptionDTO.swift @@ -0,0 +1,60 @@ +import Foundation +import VoxeetSDK + +enum Event: String { + case invitationReceived = "SUBSCRIPTION_TYPE_INVITATION_RECEIVED" + case activeParticipants = "SUBSCRIPTION_TYPE_ACTIVE_PARTICIPANTS" + case conferenceCreated = "SUBSCRIPTION_TYPE_CONFERENCE_CREATED" + case conferenceEnded = "SUBSCRIPTION_TYPE_CONFERENCE_ENDED" + case participantJoined = "SUBSCRIPTION_TYPE_PARTICIPANT_JOINED" + case participantLeft = "SUBSCRIPTION_TYPE_PARTICIPANT_LEFT" +} + +internal struct SubscriptionDTO { + + static func create(with dictionary: [String: Any]) -> SubscriptionDTO? { + guard + let type: String = dictionary.value(for: Keys.type), + let conferenceAlias: String = dictionary.value(for: Keys.conferenceAlias) + else { return nil } + + return SubscriptionDTO(type: type, conferenceAlias: conferenceAlias) + } + + let type: String + let conferenceAlias: String + + func subscription() -> VTSubscribeBase? { + switch type { + case Event.invitationReceived.rawValue: + return VTSubscribeInvitation(conferenceAlias: conferenceAlias) + case Event.activeParticipants.rawValue: + return VTSubscribeActiveParticipants(conferenceAlias: conferenceAlias) + case Event.conferenceCreated.rawValue: + return VTSubscribeConferenceCreated(conferenceAlias: conferenceAlias) + case Event.conferenceEnded.rawValue: + return VTSubscribeConferenceEnded(conferenceAlias: conferenceAlias) + case Event.participantJoined.rawValue: + return VTSubscribeParticipantJoined(conferenceAlias: conferenceAlias) + case Event.participantLeft.rawValue: + return VTSubscribeParticipantLeft(conferenceAlias: conferenceAlias) + default: + return nil + } + } +} + +// MARK: - ReactModelMappable +extension SubscriptionDTO: ReactModelMappable { + func toReactModel() -> ReactModelType { + return [ + Keys.type: type, + Keys.conferenceAlias: conferenceAlias + ].mapKeysToRawValue() + } +} + +// MARK: - ReactModel Keys +private enum Keys: String { + case type, conferenceAlias +} diff --git a/ios/Services/NotificationService/NotificationServiceModule.swift b/ios/Services/NotificationService/NotificationServiceModule.swift index 57a0488f..c0443fc7 100644 --- a/ios/Services/NotificationService/NotificationServiceModule.swift +++ b/ios/Services/NotificationService/NotificationServiceModule.swift @@ -26,6 +26,30 @@ public class NotificationServiceModule: ReactEmitter { VoxeetSDK.shared.notification.delegate = nil; } + /// Subscribes to the specified notifications. + /// - Parameters: + /// - events: An array of the subscribed subscription types. + /// - resolve: returns on success + /// - reject: returns error on failure + @objc(subscribe:resolver:rejecter:) + public func subscribe(events: [[String: Any]], + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock) { + VoxeetSDK.shared.notification.subscribe(subscriptions: events.compactMap { SubscriptionDTO.create(with:$0)?.subscription() }) + } + + /// Unsubscribes from the specified notifications. + /// - Parameters: + /// - events: An array of the subscribed subscription types. + /// - resolve: returns on success + /// - reject: returns error on failure + @objc(unsubscribe:resolver:rejecter:) + public func unsubscribe(events: [[String: Any]], + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock) { + VoxeetSDK.shared.notification.unsubscribe(subscriptions: events.compactMap { SubscriptionDTO.create(with:$0)?.subscription() }) + } + /// Notifies conference participants about a conference invitation. /// - Parameters: /// - conference: The conference object. diff --git a/ios/Services/NotificationService/RNNotificationServiceModuleBridge.m b/ios/Services/NotificationService/RNNotificationServiceModuleBridge.m index 541fb011..55460e35 100644 --- a/ios/Services/NotificationService/RNNotificationServiceModuleBridge.m +++ b/ios/Services/NotificationService/RNNotificationServiceModuleBridge.m @@ -3,6 +3,14 @@ @interface RCT_EXTERN_REMAP_MODULE(CommsAPINotificationServiceModule, RNNotificationServiceModule, NSObject) +RCT_EXTERN_METHOD(subscribe:(NSArray * _Nonnull)events + resolver:(RCTPromiseResolveBlock _Nonnull)resolve + rejecter:(RCTPromiseRejectBlock _Nonnull)reject); + +RCT_EXTERN_METHOD(unsubscribe:(NSArray * _Nonnull)events + resolver:(RCTPromiseResolveBlock _Nonnull)resolve + rejecter:(RCTPromiseRejectBlock _Nonnull)reject); + RCT_EXTERN_METHOD(invite:(NSDictionary * _Nonnull)conference participants:(NSArray * _Nonnull)participants resolver:(RCTPromiseResolveBlock _Nonnull)resolve diff --git a/src/services/notification/NotificationService.ts b/src/services/notification/NotificationService.ts index 6add4d98..a78b2662 100644 --- a/src/services/notification/NotificationService.ts +++ b/src/services/notification/NotificationService.ts @@ -8,6 +8,7 @@ import type { } from '../conference/models'; import { NotificationServiceEventNames } from './events'; import type { InvitationReceivedEventType } from './events'; +import type { Subscription } from './models'; const { CommsAPINotificationServiceModule } = NativeModules; @@ -20,6 +21,22 @@ export class NotificationService { /** @internal */ _nativeEvents = new NativeEvents(CommsAPINotificationServiceModule); + /** + * Subscribes to the specified notifications. + * @param events An array of the subscribed subscription types. + */ + public async subscribe(events: Subscription[]): Promise { + return this._nativeModule.subscribe(events); + } + + /** + * Unsubscribes from the specified notifications. + * @param events An array of the subscribed subscription types. + */ + public async unsubscribe(events: Subscription[]): Promise { + return this._nativeModule.unsubscribe(events); + } + /** * Declines the conference invitation. * @param conference The conference object. diff --git a/src/services/notification/__tests__/index.test.ts b/src/services/notification/__tests__/index.test.ts index a31c45dd..11f35ddf 100644 --- a/src/services/notification/__tests__/index.test.ts +++ b/src/services/notification/__tests__/index.test.ts @@ -14,6 +14,28 @@ const testConference: Conference = { describe('NotificationService', () => { NotificationService._nativeEvents.addListener = jest.fn(); + describe('subscribe()', () => { + it('should invoke subscribe method with correct arguments', () => { + NotificationService.subscribe([ + { type: 'event', conferenceAlias: 'alias' }, + ]); + expect(CommsAPINotificationServiceModule.subscribe).toHaveBeenCalledWith([ + { type: 'event', conferenceAlias: 'alias' }, + ]); + }); + }); + + describe('unsubscribe()', () => { + it('should invoke unsubscribe method with correct arguments', () => { + NotificationService.unsubscribe([ + { type: 'event', conferenceAlias: 'alias' }, + ]); + expect( + CommsAPINotificationServiceModule.unsubscribe + ).toHaveBeenCalledWith([{ type: 'event', conferenceAlias: 'alias' }]); + }); + }); + describe('invite()', () => { it('should invoke exported invite method with correct arguments', () => { NotificationService.invite(testConference, [{ info: {} }]); diff --git a/src/services/notification/models.ts b/src/services/notification/models.ts new file mode 100644 index 00000000..65c3f7f3 --- /dev/null +++ b/src/services/notification/models.ts @@ -0,0 +1,17 @@ +/** The SubscriptionType model gathers the subscription types. */ +export enum SubscriptionType { + InvitationReceived = 'SUBSCRIPTION_TYPE_INVITATION_RECEIVED', + ActiveParticipants = 'SUBSCRIPTION_TYPE_ACTIVE_PARTICIPANTS', + ConferenceCreated = 'SUBSCRIPTION_TYPE_CONFERENCE_CREATED', + ConferenceEnded = 'SUBSCRIPTION_TYPE_CONFERENCE_ENDED', + ParticipantJoined = 'SUBSCRIPTION_TYPE_PARTICIPANT_JOINED', + ParticipantLeft = 'SUBSCRIPTION_TYPE_PARTICIPANT_LEFT', +} + +/** The Subscription model is an interface for all subscription types. */ +export interface Subscription { + /** The subscription type. */ + type: string; + /** The conference alias. */ + conferenceAlias: string; +} diff --git a/src/setupTest.ts b/src/setupTest.ts index b87c663b..2c605890 100644 --- a/src/setupTest.ts +++ b/src/setupTest.ts @@ -49,6 +49,8 @@ jest.mock('react-native', () => { getParticipant: jest.fn(), }; RN.NativeModules.CommsAPINotificationServiceModule = { + subscribe: jest.fn(), + unsubscribe: jest.fn(), invite: jest.fn(), decline: jest.fn(), };