Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] Introduce new error event completion handler #212

Merged
merged 15 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,23 @@ struct RemoteParticipantAvatarHelper {
let nameIdValue = id != nil ? "\(id?.suffix(4) ?? "")" : ""
var avatarImage: UIImage?
var selectedAvatarName = ""
if let lastSymbol = id?.last,
let index = Int(String(lastSymbol)),
index < avatars.count {
if let lastSymbol = id?.last {
let index = Int((lastSymbol.asciiValue ?? 0 ) % 6)
selectedAvatarName = avatars[index]
avatarImage = UIImage(named: selectedAvatarName)
}
let renderDisplayName = selectedAvatarName.isEmpty ? nameIdValue : "\(selectedAvatarName) \(nameIdValue)"
let displayName = selectedAvatarName.isEmpty ? nameIdValue : "\(selectedAvatarName) \(nameIdValue)"
let participantViewData = ParticipantViewData(avatar: avatarImage,
renderDisplayName: renderDisplayName)
callComposite.setRemoteParticipantViewData(for: identifier,
participantViewData: participantViewData)
displayName: displayName)
callComposite.set(remoteParticipantViewData: participantViewData,
for: identifier) { result in
switch result {
case .success:
break
case .failure(let error):
print("::::RemoteParticipantAvatarHelper::didRemoteParticipantsJoin::failure \(error)")
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ extension SwiftUIDemoView {
let renderDisplayName = envConfigSubject.renderedDisplayName.isEmpty ?
nil:envConfigSubject.renderedDisplayName
let participantViewData = ParticipantViewData(avatar: UIImage(named: envConfigSubject.avatarImageName),
renderDisplayName: renderDisplayName)
displayName: renderDisplayName)
let localSettings = LocalSettings(participantViewData)
if let credential = try? getTokenCredential() {
switch envConfigSubject.selectedMeetingType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ class UIKitDemoViewController: UIViewController {
let renderDisplayName = envConfigSubject.renderedDisplayName.isEmpty ?
nil : envConfigSubject.renderedDisplayName
let participantViewData = ParticipantViewData(avatar: UIImage(named: envConfigSubject.avatarImageName),
renderDisplayName: renderDisplayName)
displayName: renderDisplayName)
let localSettings = LocalSettings(participantViewData)

if let communicationTokenCredential = try? getTokenCredential() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@
FA03CC8527C8189900986D4E /* ErrorInfoViewModelMocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA03CC7A27C8189900986D4E /* ErrorInfoViewModelMocking.swift */; };
FA03CC8927C8189900986D4E /* PrimaryButtonViewModelMocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA03CC7E27C8189900986D4E /* PrimaryButtonViewModelMocking.swift */; };
FA03CC8D27C8420300986D4E /* IconWithLabelButtonViewModelMocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA03CC8C27C8420300986D4E /* IconWithLabelButtonViewModelMocking.swift */; };
FA378672284152CA005B6288 /* ParticipantViewDataSetError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA378671284152CA005B6288 /* ParticipantViewDataSetError.swift */; };
FA40424A27E3E86700875368 /* ParticipantsListViewModelMocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA40424927E3E86700875368 /* ParticipantsListViewModelMocking.swift */; };
FA62233327DAC7BC008B5466 /* AccessibilityProviderNotificationsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA62233227DAC7BC008B5466 /* AccessibilityProviderNotificationsObserver.swift */; };
FA62233727DAD9AF008B5466 /* AccessibilityProviderMocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA62233627DAD9AF008B5466 /* AccessibilityProviderMocking.swift */; };
Expand Down Expand Up @@ -491,6 +492,7 @@
FA03CC7A27C8189900986D4E /* ErrorInfoViewModelMocking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorInfoViewModelMocking.swift; sourceTree = "<group>"; };
FA03CC7E27C8189900986D4E /* PrimaryButtonViewModelMocking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimaryButtonViewModelMocking.swift; sourceTree = "<group>"; };
FA03CC8C27C8420300986D4E /* IconWithLabelButtonViewModelMocking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconWithLabelButtonViewModelMocking.swift; sourceTree = "<group>"; };
FA378671284152CA005B6288 /* ParticipantViewDataSetError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantViewDataSetError.swift; sourceTree = "<group>"; };
FA40424927E3E86700875368 /* ParticipantsListViewModelMocking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantsListViewModelMocking.swift; sourceTree = "<group>"; };
FA62233227DAC7BC008B5466 /* AccessibilityProviderNotificationsObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityProviderNotificationsObserver.swift; sourceTree = "<group>"; };
FA62233627DAD9AF008B5466 /* AccessibilityProviderMocking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityProviderMocking.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1076,6 +1078,7 @@
1F48B400274879F000B6E5F9 /* DiagnosticConfig.swift */,
5054610327C59AD700040467 /* LocalizationOptions.swift */,
881D8F7127FB6992008EC897 /* LocalSettings.swift */,
FA378671284152CA005B6288 /* ParticipantViewDataSetError.swift */,
);
path = CallCompositeOptions;
sourceTree = "<group>";
Expand Down Expand Up @@ -1691,6 +1694,7 @@
1F2BED6C262F8B7000D98266 /* Store.swift in Sources */,
881D8F7227FB6992008EC897 /* LocalSettings.swift in Sources */,
50E2D69126EAAB33006B0EF4 /* CompositeParticipantsList.swift in Sources */,
FA378672284152CA005B6288 /* ParticipantViewDataSetError.swift in Sources */,
985DF378269E07F400CFDA55 /* PrimaryButtonViewModel.swift in Sources */,
1FC30846266949E8007EC537 /* ColorExtension.swift in Sources */,
1F6470AC2639FE670008B9E9 /* CancelBag.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import SwiftUI
import FluentUI
import AzureCommunicationCalling

public typealias CompositeErrorHandler = (CommunicationUIErrorEvent) -> Void
public typealias RemoteParticipantsJoinedHandler = ([CommunicationIdentifier]) -> Void

/// The main class representing the entry point for the Call Composite.
public class CallComposite {
private var logger: Logger?
Expand All @@ -34,13 +31,13 @@ public class CallComposite {

/// Assign closures to execute when error event occurs inside Call Composite.
/// - Parameter didFailAction: The closure returning the error thrown from Call Composite.
public func setDidFailHandler(with didFailAction: CompositeErrorHandler?) {
public func setDidFailHandler(with didFailAction: ((CommunicationUIErrorEvent) -> Void)?) {
vhuseinova-msft marked this conversation as resolved.
Show resolved Hide resolved
callCompositeEventsHandler.didFail = didFailAction
}

/// Assign closures to execute when participant has joined a call inside Call Composite.
/// - Parameter participantsJoinedAction: The closure returning identifiers for joined remote participants.
public func setRemoteParticipantJoinHandler(with participantsJoinedAction: RemoteParticipantsJoinedHandler?) {
public func setRemoteParticipantJoinHandler(with participantsJoinedAction: (([CommunicationIdentifier]) -> Void)?) {
vhuseinova-msft marked this conversation as resolved.
Show resolved Hide resolved
callCompositeEventsHandler.didRemoteParticipantsJoin = participantsJoinedAction
}

Expand Down Expand Up @@ -96,22 +93,22 @@ public class CallComposite {
launch(callConfiguration, localSettings: localSettings)
}

/// Set ParticipantViewData for the remote participant.
/// Set ParticipantViewData to be displayed for the remote participant. This is data is not sent up to ACS.
/// - Parameters:
/// - remoteParticipantViewData: ParticipantViewData used to set the participant's information for the call.
/// - identifier: The communication identifier for the remote participant.
/// - participantViewData: ParticipantViewData used to set the user participants information for the call.
/// This is data is not sent up to ACS.
/// - Returns: The `Result` enum value with either a `Void` or an `Error`.
@discardableResult
public func setRemoteParticipantViewData(
for identifier: CommunicationIdentifier,
participantViewData: ParticipantViewData) -> Result<Void, CommunicationUIErrorEvent> {
/// - completionHandler: The completion handler that receives `Result` enum value with either
/// a `Void` or an `ParticipantViewDataSetError`.
public func set(remoteParticipantViewData: ParticipantViewData,
for identifier: CommunicationIdentifier,
completionHandler: ((Result<Void, ParticipantViewDataSetError>) -> Void)? = nil) {
vhuseinova-msft marked this conversation as resolved.
Show resolved Hide resolved
guard let avatarManager = avatarViewManager else {
return .failure(CommunicationUIErrorEvent(code: CallCompositeErrorCode.remoteParticipantNotFound))
completionHandler?(.failure(ParticipantViewDataSetError.remoteParticipantNotFound))
return
}

return avatarManager.setRemoteParticipantViewData(for: identifier,
participantViewData: participantViewData)
avatarManager.set(remoteParticipantViewData: remoteParticipantViewData,
for: identifier,
completionHandler: completionHandler)
}

private func setupManagers(with dependencyContainer: DependencyContainer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import UIKit
import AzureCommunicationCalling

protocol CallCompositeEventsHandling: AnyObject {
var didFail: CompositeErrorHandler? { get set }
var didRemoteParticipantsJoin: RemoteParticipantsJoinedHandler? { get set }
var didFail: ((CommunicationUIErrorEvent) -> Void)? { get set }
var didRemoteParticipantsJoin: (([CommunicationIdentifier]) -> Void)? { get set }
}

class CallCompositeEventsHandler: CallCompositeEventsHandling {
var didFail: CompositeErrorHandler?
var didRemoteParticipantsJoin: RemoteParticipantsJoinedHandler?
var didFail: ((CommunicationUIErrorEvent) -> Void)?
var didRemoteParticipantsJoin: (([CommunicationIdentifier]) -> Void)?
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ public struct CallCompositeErrorCode {
/// Error when the input token is expired.
public static let tokenExpired: String = "tokenExpired"

/// Error when the remote participant is not found in the call.
public static let remoteParticipantNotFound: String = "RemoteParticipantNotFound"

/// Error when a participant is evicted from the call by another participant
static let callEvicted: String = "callEvicted"

Expand All @@ -27,7 +24,7 @@ public struct CallCompositeErrorCode {
}

/// The error thrown after Call Composite launching.
public struct CommunicationUIErrorEvent: Error {
vhuseinova-msft marked this conversation as resolved.
Show resolved Hide resolved
public struct CommunicationUIErrorEvent {

/// The string representing the CallCompositeErrorCode.
public let code: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ public struct ParticipantViewData {
/// The image that will be drawn on the avatar view
let avatarImage: UIImage?
/// The display name that will be locally rendered for this participant
let renderDisplayName: String?
let displayName: String?
/// Create an instance of a ParticipantViewData.
/// All information in this object is only stored locally in the composite.
/// - Parameters:
/// - avatar: The UIImage that will be displayer in the avatar view
/// - renderDisplayName: The display name to be rendered.
/// If this is `nil` the display name provided in the Call Options will be used instead.
public init(avatar: UIImage?,
renderDisplayName: String? = nil) {
/// - avatar: The UIImage that will be displayer in the avatar view.
/// If this is `nil` the default avatar with user's initials will be used instead.
/// - displayName: The display name to be rendered.
/// If this is `nil` the display name provided in the Call Options will be used instead.
public init(avatar: UIImage? = nil,
displayName: String? = nil) {
self.avatarImage = avatar
self.renderDisplayName = renderDisplayName
self.displayName = displayName
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//

import Foundation

public enum ParticipantViewDataSetError: String, Error {
/// Error when the remote participant is not found in the call.
case remoteParticipantNotFound = "RemoteParticipantNotFound"
vhuseinova-msft marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class DependencyContainer {
register(VideoViewManager(callingSDKWrapper: resolve(), logger: resolve()) as VideoViewManager)
register(CallingService(logger: resolve(),
callingSDKWrapper: resolve()) as CallingServiceProtocol)
let displayName = localSettings?.participantViewData.renderDisplayName ?? callConfiguration.displayName
let displayName = localSettings?.participantViewData.displayName ?? callConfiguration.displayName
register(makeStore(displayName: displayName) as Store<AppState>)
register(NavigationRouter(store: resolve(),
logger: resolve()) as NavigationRouter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import AzureCommunicationCommon
import Combine

protocol AvatarViewManagerProtocol {
func setRemoteParticipantViewData(
for identifier: CommunicationIdentifier,
participantViewData: ParticipantViewData) -> Result<Void, CommunicationUIErrorEvent>
func set(remoteParticipantViewData: ParticipantViewData,
for identifier: CommunicationIdentifier,
completionHandler: ((Result<Void, ParticipantViewDataSetError>) -> Void)?)
}

class AvatarViewManager: AvatarViewManagerProtocol, ObservableObject {
Expand Down Expand Up @@ -50,22 +50,23 @@ class AvatarViewManager: AvatarViewManagerProtocol, ObservableObject {
}
}

func setRemoteParticipantViewData(
for identifier: CommunicationIdentifier,
participantViewData: ParticipantViewData) -> Result<Void, CommunicationUIErrorEvent> {
func set(remoteParticipantViewData: ParticipantViewData,
for identifier: CommunicationIdentifier,
completionHandler: ((Result<Void, ParticipantViewDataSetError>) -> Void)? = nil) {
let participantsList = store.state.remoteParticipantsState.participantInfoList
guard let idStringValue = identifier.stringValue,
participantsList.contains(where: { $0.userIdentifier == idStringValue })
else {
return .failure(CommunicationUIErrorEvent(code: CallCompositeErrorCode.remoteParticipantNotFound))
completionHandler?(.failure(ParticipantViewDataSetError.remoteParticipantNotFound))
return
}

if avatarStorage.value(forKey: idStringValue) != nil {
avatarStorage.removeValue(forKey: idStringValue)
}
avatarStorage.append(forKey: idStringValue,
value: participantViewData)
value: remoteParticipantViewData)
updatedId = idStringValue
return .success(Void())
completionHandler?(.success(Void()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class ParticipantsListCellViewModel {

func getParticipantName(with participantViewData: ParticipantViewData?) -> String {
let name: String
if let data = participantViewData, let renderDisplayName = data.renderDisplayName {
if let data = participantViewData, let renderDisplayName = data.displayName {
let isRendererNameEmpty = renderDisplayName.trimmingCharacters(in: .whitespaces).isEmpty
name = isRendererNameEmpty ? displayName : renderDisplayName
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ struct ParticipantGridCellView: View {
avatarImage = participantViewData.avatarImage
}

viewModel.updateParticipantNameIfNeeded(with: participantViewData.renderDisplayName)
viewModel.updateParticipantNameIfNeeded(with: participantViewData.displayName)
}

private func getRemoteParticipantVideoViewId() -> RemoteParticipantVideoViewId? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class DependencyContainerTests: XCTestCase {
let callConfiguration = CallConfiguration(credential: communicationTokenCredential!,
groupId: groupId,
displayName: displayName)
let participantViewData = ParticipantViewData(avatar: nil, renderDisplayName: nil)
let participantViewData = ParticipantViewData(avatar: nil, displayName: nil)
let localSettings = LocalSettings(participantViewData)
let callCompositeEventsHandler = CallCompositeEventsHandler()

Expand Down
Loading