diff --git a/GliaWidgets/Public/Glia/Glia+StartEngagement.swift b/GliaWidgets/Public/Glia/Glia+StartEngagement.swift index cc3a4eaba..e5e045dfa 100644 --- a/GliaWidgets/Public/Glia/Glia+StartEngagement.swift +++ b/GliaWidgets/Public/Glia/Glia+StartEngagement.swift @@ -6,6 +6,7 @@ extension Glia { /// /// - Parameters: /// - engagementKind: Engagement media type. + /// - in: Queue identifiers /// - theme: A custom theme to use with the engagement. /// - visitorContext: Visitor context. /// - features: Set of features to be enabled in the SDK. @@ -22,10 +23,14 @@ extension Glia { /// public func startEngagement( engagementKind: EngagementKind, + in queueIds: [String], theme: Theme = Theme(), features: Features = .all, sceneProvider: SceneProvider? = nil ) throws { + // `interactor?.queueIds.isEmpty == false` statement is needed for integrators who uses old interface + // and pass queue identifier throught `configuration` function. + guard !queueIds.isEmpty || interactor?.queueIds.isEmpty == false else { throw GliaError.startEngagementWithNoQueueIds } guard engagement == .none else { throw GliaError.engagementExists } guard let interactor = self.interactor else { throw GliaError.sdkIsNotConfigured } if let engagement = environment.coreSdk.getCurrentEngagement(), @@ -33,6 +38,14 @@ extension Glia { throw GliaError.callVisualizerEngagementExists } + // This check is needed for integrators who uses old interface + // and pass queue identifier throught `configuration` function, + // but would not pass queue ids in this method, so SDK would not override + // existed queue id. + if !queueIds.isEmpty { + interactor.queueIds = queueIds + } + let viewFactory = ViewFactory( with: theme, messageRenderer: messageRenderer, @@ -46,6 +59,7 @@ extension Glia { uiScreen: environment.uiScreen ) ) + startRootCoordinator( with: interactor, viewFactory: viewFactory, @@ -55,53 +69,7 @@ extension Glia { ) } - /// Starts the engagement. - /// - /// - Parameters: - /// - engagementKind: Engagement media type. - /// - configuration: Engagement configuration. - /// - queueID: Queue identifier. - /// - theme: A custom theme to use with the engagement. - /// - sceneProvider: Used to provide `UIWindowScene` to the framework. Defaults to the first active foreground scene. - /// - /// - throws: - /// - `GliaCoreSDK.ConfigurationError.invalidSite` - /// - `GliaCoreSDK.ConfigurationError.invalidEnvironment` - /// - `GliaCoreSDK.ConfigurationError.invalidRegionEndpoint` - /// - `GliaCoreSDK.ConfigurationError.invalidSiteApiKey` - /// - `GliaError.engagementExists` - /// - public func start( - _ engagementKind: EngagementKind, - configuration: Configuration, - queueID: String, - theme: Theme = Theme(), - features: Features = .all, - sceneProvider: SceneProvider? = nil - ) throws { - let completion = { [weak self] in - try self?.startEngagement( - engagementKind: engagementKind, - theme: theme, - features: features, - sceneProvider: sceneProvider - ) - } - do { - try configure( - with: configuration, - queueId: queueID - ) { - try? completion() - } - } catch GliaError.configuringDuringEngagementIsNotAllowed { - try completion() - } - } - - // MARK: - Private - - private func startRootCoordinator( + func startRootCoordinator( with interactor: Interactor, viewFactory: ViewFactory, sceneProvider: SceneProvider?, diff --git a/GliaWidgets/Public/Glia/Glia.Deprecated.swift b/GliaWidgets/Public/Glia/Glia.Deprecated.swift index 1e2814128..b8f1b39de 100644 --- a/GliaWidgets/Public/Glia/Glia.Deprecated.swift +++ b/GliaWidgets/Public/Glia/Glia.Deprecated.swift @@ -89,6 +89,98 @@ extension Glia { public func requestVisitorCode(completion: @escaping (Result) -> Void) { _ = environment.coreSdk.requestVisitorCode(completion) } + + /// Deprecated, use ``Glia.configure(with:uiConfig:assetsBuilder:completion)`` instead. + @available(*, deprecated, message: "Deprecated, use ``Glia.configure(with:uiConfig:assetsBuilder:completion`` instead.") + public func configure( + with configuration: Configuration, + queueId: String, + uiConfig: RemoteConfiguration? = nil, + assetsBuilder: RemoteConfiguration.AssetsBuilder = .standard, + completion: (() -> Void)? = nil + ) throws { + guard environment.coreSdk.getCurrentEngagement() == nil else { + throw GliaError.configuringDuringEngagementIsNotAllowed + } + self.uiConfig = uiConfig + self.assetsBuilder = assetsBuilder + + let createdInteractor = Interactor( + configuration: configuration, + queueIds: [queueId], + environment: .init(coreSdk: environment.coreSdk, gcd: environment.gcd) + ) + + interactor = createdInteractor + + if let callback = completion { + createdInteractor.withConfiguration { [weak createdInteractor] in + guard let interactor = createdInteractor else { return } + interactor.state = GliaCore.sharedInstance + .getCurrentEngagement()?.engagedOperator + .map(InteractorState.engaged) ?? interactor.state + callback() + } + } + + startObservingInteractorEvents() + } + + /// Deprecated, use ``Glia.startEngagement(engagementKind:in:theme:features:sceneProvider:)`` instead. + @available(*, + deprecated, + message: "Deprecated, use ``Glia.startEngagement(engagementKind:in:theme:features:sceneProvider:)`` instead." + ) + public func startEngagement( + engagementKind: EngagementKind, + theme: Theme = Theme(), + features: Features = .all, + sceneProvider: SceneProvider? = nil + ) throws { + try startEngagement( + engagementKind: engagementKind, + in: [], + theme: theme, + features: features, + sceneProvider: sceneProvider + ) + } + + /// Deprecated, use ``Deprecated, use ``Glia.configure(with:uiConfig:assetsBuilder:completion`` and ``Glia.startEngagement(engagementKind:in:theme:features:sceneProvider:)`` instead.`` instead. + @available(*, + deprecated, + message: """ + Deprecated, use ``Glia.configure(with:uiConfig:assetsBuilder:completion`` and \ + ``Glia.startEngagement(engagementKind:in:theme:features:sceneProvider:)`` instead. + """ + ) + public func start( + _ engagementKind: EngagementKind, + configuration: Configuration, + queueID: String, + theme: Theme = Theme(), + features: Features = .all, + sceneProvider: SceneProvider? = nil + ) throws { + let completion = { [weak self] in + try self?.startEngagement( + engagementKind: engagementKind, + theme: theme, + features: features, + sceneProvider: sceneProvider + ) + } + do { + try configure( + with: configuration, + queueId: queueID + ) { + try? completion() + } + } catch GliaError.configuringDuringEngagementIsNotAllowed { + try completion() + } + } } extension Glia.Authentication { diff --git a/GliaWidgets/Public/Glia/Glia.swift b/GliaWidgets/Public/Glia/Glia.swift index 38d0efd25..00e1cb32a 100644 --- a/GliaWidgets/Public/Glia/Glia.swift +++ b/GliaWidgets/Public/Glia/Glia.swift @@ -98,7 +98,6 @@ public class Glia { /// Setup SDK using specific engagement configuration without starting the engagement. /// - Parameters: /// - configuration: Engagement configuration. - /// - queueId: Queue identifier. /// - visitorContext: Visitor context. /// - uiConfig: Remote UI configuration. /// - assetsBuilder: Provides assets for remote configuration. @@ -106,7 +105,6 @@ public class Glia { /// Passing `nil` will defer configuration. Passing closure will start configuration immediately. public func configure( with configuration: Configuration, - queueId: String, uiConfig: RemoteConfiguration? = nil, assetsBuilder: RemoteConfiguration.AssetsBuilder = .standard, completion: (() -> Void)? = nil @@ -119,7 +117,7 @@ public class Glia { let createdInteractor = Interactor( configuration: configuration, - queueID: queueId, + queueIds: [], environment: .init(coreSdk: environment.coreSdk, gcd: environment.gcd) ) @@ -266,8 +264,8 @@ public class Glia { // MARK: - Private -private extension Glia { - func startObservingInteractorEvents() { +extension Glia { + internal func startObservingInteractorEvents() { interactor?.addObserver(self) { [weak self] event in guard let engagement = self?.environment.coreSdk.getCurrentEngagement(), diff --git a/GliaWidgets/Public/GliaError.swift b/GliaWidgets/Public/GliaError.swift index 6f4ea8bb8..c454b0116 100644 --- a/GliaWidgets/Public/GliaError.swift +++ b/GliaWidgets/Public/GliaError.swift @@ -6,4 +6,5 @@ public enum GliaError: Error { case callVisualizerEngagementExists case configuringDuringEngagementIsNotAllowed case clearingVisitorSessionDuringEngagementIsNotAllowed + case startEngagementWithNoQueueIds } diff --git a/GliaWidgets/Sources/Coordinators/EngagementCoordinator/EngagementCoordinator.swift b/GliaWidgets/Sources/Coordinators/EngagementCoordinator/EngagementCoordinator.swift index 20cfb40d4..e1da6b3a3 100644 --- a/GliaWidgets/Sources/Coordinators/EngagementCoordinator/EngagementCoordinator.swift +++ b/GliaWidgets/Sources/Coordinators/EngagementCoordinator/EngagementCoordinator.swift @@ -244,7 +244,7 @@ extension EngagementCoordinator { fetchChatHistory: environment.fetchChatHistory, createFileUploadListModel: environment.createFileUploadListModel, sendSecureMessage: environment.sendSecureMessage, - queueIds: [interactor.queueID], + queueIds: interactor.queueIds, listQueues: environment.listQueues, secureUploadFile: environment.uploadSecureFile, getSecureUnreadMessageCount: environment.getSecureUnreadMessageCount, @@ -439,7 +439,7 @@ extension EngagementCoordinator { viewFactory: viewFactory, navigationPresenter: navigationPresenter, environment: .init( - queueIds: [interactor.queueID], + queueIds: interactor.queueIds, listQueues: environment.listQueues, sendSecureMessage: environment.sendSecureMessage, createFileUploader: environment.createFileUploader, diff --git a/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Interface.swift b/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Interface.swift index ddc851eb6..0dcc756d2 100644 --- a/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Interface.swift +++ b/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Interface.swift @@ -37,12 +37,12 @@ struct CoreSdkClient { var listQueues: ListQueues typealias QueueForEngagement = ( - _ queueID: String, + _ queueIds: [String], _ visitorContext: Self.VisitorContext?, _ shouldCloseAllQueues: Bool, _ mediaType: Self.MediaType, _ options: Self.EngagementOptions?, - _ completion: @escaping Self.QueueTicketBlock + _ completion: @escaping (Result) -> Void ) -> Void var queueForEngagement: QueueForEngagement diff --git a/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Live.swift b/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Live.swift index 7be7fc536..e97af5cef 100644 --- a/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Live.swift +++ b/GliaWidgets/Sources/CoreSDKClient/CoreSDKClient.Live.swift @@ -12,8 +12,16 @@ extension CoreSdkClient { configureWithConfiguration: GliaCore.sharedInstance.configure(with:completion:), configureWithInteractor: GliaCore.sharedInstance.configure(interactor:), listQueues: GliaCore.sharedInstance.listQueues(completion:), - queueForEngagement: GliaCore.sharedInstance - .queueForEngagement(queueID:visitorContext:shouldCloseAllQueues:mediaType:options:completion:), + queueForEngagement: { queueIds, visitorContext, shouldCloseAllQueues, mediaType, options, completion in + let options = QueueForEngagementOptions( + queueIds: queueIds, + visitorContext: visitorContext, + shouldCloseAllQueues: shouldCloseAllQueues, + mediaType: mediaType, + engagementOptions: options + ) + GliaCore.sharedInstance.queueForEngagement(using: options, completion: completion) + }, requestMediaUpgradeWithOffer: GliaCore.sharedInstance.requestMediaUpgrade(offer:completion:), sendMessagePreview: GliaCore.sharedInstance.sendMessagePreview(message:completion:), sendMessageWithAttachment: GliaCore.sharedInstance.send(message:attachment:completion:), diff --git a/GliaWidgets/Sources/Interactor/Interactor.Mock.swift b/GliaWidgets/Sources/Interactor/Interactor.Mock.swift index e27ac7576..905574cd5 100644 --- a/GliaWidgets/Sources/Interactor/Interactor.Mock.swift +++ b/GliaWidgets/Sources/Interactor/Interactor.Mock.swift @@ -4,12 +4,12 @@ import Foundation extension Interactor { static func mock( configuration: Configuration = .mock(), - queueID: String = UUID.mock.uuidString, + queueId: String = UUID.mock.uuidString, environment: Environment = .mock ) -> Interactor { .init( configuration: configuration, - queueID: queueID, + queueIds: [queueId], environment: environment ) } diff --git a/GliaWidgets/Sources/Interactor/Interactor.swift b/GliaWidgets/Sources/Interactor/Interactor.swift index 6cf12810a..d9214a9d3 100644 --- a/GliaWidgets/Sources/Interactor/Interactor.swift +++ b/GliaWidgets/Sources/Interactor/Interactor.swift @@ -36,7 +36,7 @@ enum InteractorEvent { class Interactor { typealias EventHandler = (InteractorEvent) -> Void - let queueID: String + var queueIds: [String] var engagedOperator: CoreSdkClient.Operator? { switch state { case .engaged(let engagedOperator): @@ -75,10 +75,10 @@ class Interactor { init( configuration: Configuration, - queueID: String, + queueIds: [String], environment: Environment ) { - self.queueID = queueID + self.queueIds = queueIds self.configuration = configuration self.environment = environment } @@ -155,18 +155,19 @@ extension Interactor { .map(CoreSdkClient.VisitorContext.init(_:)) self.environment.coreSdk.queueForEngagement( - self.queueID, + self.queueIds, coreSdkVisitorContext, // shouldCloseAllQueues is `true` by default core sdk, // here it is passed explicitly true, mediaType, options - ) { [weak self] queueTicket, error in - if let error = error { + ) { [weak self] result in + switch result { + case .failure(let error): self?.state = .ended(.byError) failure(error) - } else if let ticket = queueTicket { + case .success(let ticket): if case .enqueueing = self?.state { self?.state = .enqueued(ticket) } diff --git a/GliaWidgets/Sources/ViewController/Call/CallViewController.Mock.swift b/GliaWidgets/Sources/ViewController/Call/CallViewController.Mock.swift index 328fc32e8..4af4c20d0 100644 --- a/GliaWidgets/Sources/ViewController/Call/CallViewController.Mock.swift +++ b/GliaWidgets/Sources/ViewController/Call/CallViewController.Mock.swift @@ -24,7 +24,7 @@ extension CallViewController { let interactorEnv = Interactor.Environment.mock let interactor = Interactor.mock( configuration: conf, - queueID: queueId, + queueId: queueId, environment: interactorEnv ) let alertConf = AlertConfiguration.mock() @@ -59,7 +59,7 @@ extension CallViewController { let interactorEnv = Interactor.Environment.mock let interactor = Interactor.mock( configuration: conf, - queueID: queueId, + queueId: queueId, environment: interactorEnv ) let alertConf = AlertConfiguration.mock() @@ -97,7 +97,7 @@ extension CallViewController { } let interactor = Interactor.mock( configuration: conf, - queueID: queueId, + queueId: queueId, environment: interactorEnv ) let alertConf = AlertConfiguration.mock() @@ -150,7 +150,7 @@ extension CallViewController { let interactorEnv = Interactor.Environment.mock let interactor = Interactor.mock( configuration: conf, - queueID: queueId, + queueId: queueId, environment: interactorEnv ) let alertConf = AlertConfiguration.mock() @@ -198,7 +198,7 @@ extension CallViewController { let interactorEnv = Interactor.Environment.mock let interactor = Interactor.mock( configuration: conf, - queueID: queueId, + queueId: queueId, environment: interactorEnv ) let alertConf = AlertConfiguration.mock() @@ -233,7 +233,7 @@ extension CallViewController { let interactorEnv = Interactor.Environment.mock let interactor = Interactor.mock( configuration: conf, - queueID: queueId, + queueId: queueId, environment: interactorEnv ) let alertConf = AlertConfiguration.mock() diff --git a/GliaWidgets/Sources/ViewController/Chat/ChatViewController.Mock.swift b/GliaWidgets/Sources/ViewController/Chat/ChatViewController.Mock.swift index b6107dff5..c109254cc 100644 --- a/GliaWidgets/Sources/ViewController/Chat/ChatViewController.Mock.swift +++ b/GliaWidgets/Sources/ViewController/Chat/ChatViewController.Mock.swift @@ -69,7 +69,6 @@ extension ChatViewController { let messages: [ChatMessage] = [ .mock( id: messageId(), - queueID: queueId, operator: nil, sender: .visitor, content: "Hi", @@ -78,7 +77,6 @@ extension ChatViewController { ), .mock( id: messageId(), - queueID: queueId, operator: .mock(name: "John Smith", pictureUrl: URL.mock.absoluteString), sender: .operator, content: "hello", @@ -87,7 +85,6 @@ extension ChatViewController { ), .mock( id: messageId(), - queueID: queueId, operator: nil, sender: .visitor, content: "", @@ -111,7 +108,6 @@ extension ChatViewController { ), .mock( id: messageId(), - queueID: queueId, operator: nil, sender: .visitor, content: "Message along with content", @@ -135,7 +131,6 @@ extension ChatViewController { ), .mock( id: messageId(), - queueID: queueId, operator: .mock( name: "John Doe", pictureUrl: URL.mock.absoluteString @@ -162,7 +157,6 @@ extension ChatViewController { ), .mock( id: messageId(), - queueID: queueId, operator: .mock( name: "John Smith", pictureUrl: URL.mock.appendingPathComponent("opImage").appendingPathExtension("png").absoluteString @@ -332,7 +326,6 @@ extension ChatViewController { ] let messages: [ChatMessage] = [ .mock(id: messageId(), - queueID: queueId, operator: .mock( name: "Blob", pictureUrl: "https://mock.mock/operator/234/image.png" @@ -385,7 +378,6 @@ extension ChatViewController { let messages: [ChatMessage] = [ .mock( id: messageId(), - queueID: queueId, operator: .mock( name: "Rasmus", pictureUrl: "https://mock.mock/single_choice/567/image.png" @@ -432,7 +424,6 @@ extension ChatViewController { let messages: [ChatMessage] = [ .mock( id: messageId(), - queueID: queueId, operator: .mock( name: "Rasmus", pictureUrl: "https://mock.mock/single_choice/567/image.png" @@ -479,7 +470,6 @@ extension ChatViewController { let messages: [ChatMessage] = [ .mock( id: messageId(), - queueID: queueId, operator: .mock( name: "Rasmus", pictureUrl: "https://mock.mock/single_choice/567/image.png" @@ -519,7 +509,6 @@ extension ChatViewController { (0 ..< 4).map { idx in ChatMessage.mock( id: "messageId\(idx)", - queueID: "queueId", operator: .mock( name: "John Smith", pictureUrl: URL.mock.appendingPathComponent("opImage").appendingPathExtension("png").absoluteString diff --git a/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+ChoiceCards.swift b/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+ChoiceCards.swift index 4973ab393..814d57c57 100644 --- a/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+ChoiceCards.swift +++ b/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+ChoiceCards.swift @@ -42,13 +42,11 @@ extension ChatViewModel { ) = choiceCard.kind else { return } message.attachment?.selectedOption = selection - message.queueID = interactor.queueID let item = ChatItem( kind: .visitorMessage( ChatMessage( id: message.id, - queueID: message.queueID, operator: message.operator, sender: message.sender, content: message.attachment?.selectedOption ?? "", diff --git a/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+CustomCard.swift b/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+CustomCard.swift index 58fd1892e..a013b8392 100644 --- a/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+CustomCard.swift +++ b/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+CustomCard.swift @@ -88,7 +88,6 @@ extension ChatViewModel { ) = customCardItem.kind else { return } message.attachment?.selectedOption = selectedOption?.value - message.queueID = interactor.queueID let item = ChatItem(kind: .customCard( message, showsImage: showsImage, diff --git a/GliaWidgets/Sources/ViewModel/Chat/Data/ChatMessage.Mock.swift b/GliaWidgets/Sources/ViewModel/Chat/Data/ChatMessage.Mock.swift index 6a1aa42ac..7b795a06d 100644 --- a/GliaWidgets/Sources/ViewModel/Chat/Data/ChatMessage.Mock.swift +++ b/GliaWidgets/Sources/ViewModel/Chat/Data/ChatMessage.Mock.swift @@ -2,7 +2,6 @@ extension ChatMessage { static func mock( id: String = "", - queueID: String? = nil, `operator`: ChatOperator? = nil, sender: ChatMessageSender = .visitor, content: String = "", @@ -12,7 +11,6 @@ extension ChatMessage { ) -> ChatMessage { .init( id: id, - queueID: queueID, operator: `operator`, sender: sender, content: content, diff --git a/GliaWidgets/Sources/ViewModel/Chat/Data/ChatMessage.swift b/GliaWidgets/Sources/ViewModel/Chat/Data/ChatMessage.swift index 444eae8f2..243788a0a 100644 --- a/GliaWidgets/Sources/ViewModel/Chat/Data/ChatMessage.swift +++ b/GliaWidgets/Sources/ViewModel/Chat/Data/ChatMessage.swift @@ -27,7 +27,6 @@ enum ChatMessageSender: Int, Codable { class ChatMessage: Codable { typealias MessageId = String let id: MessageId - var queueID: String? let `operator`: ChatOperator? let sender: ChatMessageSender let content: String @@ -61,7 +60,6 @@ class ChatMessage: Codable { private enum CodingKeys: String, CodingKey { case id - case queueID case `operator` case sender case content @@ -73,7 +71,6 @@ class ChatMessage: Codable { queueID: String? = nil, operator salemoveOperator: CoreSdkClient.Operator? = nil) { id = message.id - self.queueID = queueID if let salemoveOperator = salemoveOperator { self.operator = ChatOperator(with: salemoveOperator) @@ -92,7 +89,6 @@ class ChatMessage: Codable { required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decode(String.self, forKey: .id) - self.queueID = try container.decodeIfPresent(String.self, forKey: .queueID) self.operator = try container.decodeIfPresent(ChatOperator.self, forKey: .operator) self.sender = try container.decode(ChatMessageSender.self, forKey: .sender) self.content = try container.decode(String.self, forKey: .content) @@ -108,7 +104,6 @@ class ChatMessage: Codable { func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) - try container.encode(queueID, forKey: .queueID) try container.encode(`operator`, forKey: .operator) try container.encode(sender, forKey: .sender) try container.encode(content, forKey: .content) @@ -119,7 +114,6 @@ class ChatMessage: Codable { init( id: String, - queueID: String?, `operator`: ChatOperator?, sender: ChatMessageSender, content: String, @@ -128,7 +122,6 @@ class ChatMessage: Codable { metadata: MessageMetadata? = nil ) { self.id = id - self.queueID = queueID self.operator = `operator` self.sender = sender self.content = content