From d9478ca7c93424385484ca1cdeea73c82b744374 Mon Sep 17 00:00:00 2001 From: mateuszglapiak-dolby Date: Fri, 10 Mar 2023 20:25:01 +0100 Subject: [PATCH] Added subscribe and unsubscribe for notification service --- .../DolbyIOProvider/DolbyIOProvider.tsx | 25 ++++++++ ios/Services/Models/SubscriptionDTO.swift | 60 +++++++++++++++++++ .../NotificationServiceModule.swift | 24 ++++++++ .../RNNotificationServiceModuleBridge.m | 8 +++ .../notification/NotificationService.ts | 22 ++++++- .../notification/__tests__/index.test.ts | 22 +++++++ src/services/notification/models.ts | 17 ++++++ 7 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 ios/Services/Models/SubscriptionDTO.swift create mode 100644 src/services/notification/models.ts diff --git a/example/src/components/DolbyIOProvider/DolbyIOProvider.tsx b/example/src/components/DolbyIOProvider/DolbyIOProvider.tsx index c0e90603..a53e8f17 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; @@ -207,6 +210,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()); } @@ -290,6 +304,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..726d6b25 100644 --- a/src/services/notification/NotificationService.ts +++ b/src/services/notification/NotificationService.ts @@ -6,8 +6,12 @@ import type { UnsubscribeFunction, ParticipantInvited, } from '../conference/models'; -import { NotificationServiceEventNames } from './events'; +import { + ConferenceStatusEventType, + NotificationServiceEventNames, +} from './events'; import type { InvitationReceivedEventType } from './events'; +import type { Subscription } from './models'; const { CommsAPINotificationServiceModule } = NativeModules; @@ -20,6 +24,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..b263337f 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 exported invite method with correct arguments', () => { + NotificationService.subscribe([ + { type: 'event', conferenceAlias: 'alias' }, + ]); + expect(CommsAPINotificationServiceModule.subscribe).toHaveBeenCalledWith([ + { type: 'event', conferenceAlias: 'alias' }, + ]); + }); + }); + + describe('unsubscribe()', () => { + it('should invoke exported invite 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; +}