Skip to content

Commit

Permalink
Fix handling engagement ended flow
Browse files Browse the repository at this point in the history
This commit introduces `endedEngagement` property in Interactor for storing engagement ended by operator to fetch a survey

MOB-3897
  • Loading branch information
Egor Egorov authored and ykyivskyi-gl committed Dec 27, 2024
1 parent 2245426 commit fdaf96c
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 86 deletions.
5 changes: 1 addition & 4 deletions GliaWidgets/Public/Glia/Glia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,7 @@ public class Glia {
return
}

interactor?.endSession(
success: { completion(.success(())) },
failure: { completion(.failure($0)) }
)
interactor?.endSession(completion: completion)
}

/// List all queues of the configured site. It is also possible to monitor queues changes with
Expand Down
12 changes: 3 additions & 9 deletions GliaWidgets/Sources/CallVisualizer/CallVisualizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,8 @@ extension CallVisualizer {

func endSession() {
coordinator.end()
environment.interactorProviding()?.endSession(
success: { [weak self] in
self?.delegate?(.engagementEnded)
},
failure: { [weak self] _ in
self?.delegate?(.engagementEnded)
}
)
environment.interactorProviding()?.cleanup()
delegate?(.engagementEnded)
}

func handleRestoredEngagement() {
Expand All @@ -132,7 +126,7 @@ extension CallVisualizer {
func startObservingInteractorEvents() {

Check warning on line 126 in GliaWidgets/Sources/CallVisualizer/CallVisualizer.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Function Body Length Violation: Function body should span 60 lines or less excluding comments and whitespace: currently spans 61 lines (function_body_length)
environment.interactorProviding()?.addObserver(self) { [weak self] event in
if case .stateChanged(.ended(.byOperator)) = event,
let endedEngagement = self?.environment.interactorProviding()?.currentEngagement,
let endedEngagement = self?.environment.interactorProviding()?.endedEngagement,
endedEngagement.source == .callVisualizer {
self?.endSession()
self?.environment.log.prefixed(Self.self).info("Call visualizer engagement ended")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,7 @@ extension CallVisualizer.Coordinator {

extension CallVisualizer.Coordinator {
func declineEngagement() {
environment.interactorProviding?.endEngagement(
success: {},
failure: { _ in }
)
environment.interactorProviding?.endEngagement { _ in }
end()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ extension EngagementCoordinator {
}
}

guard let engagement = interactor.currentEngagement, surveyPresentation == .presentSurvey else {
guard let engagement = interactor.endedEngagement, surveyPresentation == .presentSurvey else {
dismissGliaViewController()
return
}
Expand Down
61 changes: 34 additions & 27 deletions GliaWidgets/Sources/Interactor/Interactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class Interactor {
let visitorContext: Configuration.VisitorContext?
@Published private(set) var currentEngagement: CoreSdkClient.Engagement?

// Used to save engagement ended by operator to fetch a survey
private(set) var endedEngagement: CoreSdkClient.Engagement?

private var observers = [() -> (AnyObject?, EventHandler)]()

var state: InteractorState = .none {
Expand Down Expand Up @@ -175,64 +178,63 @@ extension Interactor {
environment.coreSdk.sendMessageWithMessagePayload(messagePayload, completion)
}

func endSession(
success: @escaping () -> Void,
failure: @escaping (CoreSdkClient.SalemoveError) -> Void
) {
func endSession(completion: @escaping (Result<Void, Error>) -> Void) {
switch state {
case .none:
success()
completion(.success(()))
case .enqueueing:
state = .ended(.byVisitor)
success()
completion(.success(()))
case .enqueued(let ticket):
exitQueue(
ticket: ticket,
success: success,
failure: failure
completion: completion
)
case .engaged:
endEngagement(
success: success,
failure: failure
)
case .ended(let reason):
state = .ended(reason)
success()
endEngagement(completion: completion)
case .ended:
completion(.success(()))

// `cleanup` is called once survey fetching is already initiated,
// so no need to store `endedEngagement` anymore.
cleanup()
}
}

func exitQueue(
ticket: CoreSdkClient.QueueTicket,
success: @escaping () -> Void,
failure: @escaping (CoreSdkClient.SalemoveError) -> Void
) {
completion: @escaping (Result<Void, Error>) -> Void
) {
environment.log.prefixed(Self.self).info("Cancel queue ticket")
environment.coreSdk.cancelQueueTicket(ticket) { [weak self] _, error in
if let error = error {
failure(error)
completion(.failure(error))
} else {
self?.state = .ended(.byVisitor)
success()
completion(.success(()))
}
}
}

func endEngagement(
success: @escaping () -> Void,
failure: @escaping (CoreSdkClient.SalemoveError) -> Void
) {
func endEngagement(completion: @escaping (Result<Void, Error>) -> Void) {
environment.coreSdk.endEngagement { [weak self] _, error in
if let error = error {
failure(error)
completion(.failure(error))
} else {
self?.state = .ended(.byVisitor)
success()
completion(.success(()))
}
}
}

func cleanup() {
state = .none
endedEngagement = nil
}
}

// MARK: - Interactable

extension Interactor: CoreSdkClient.Interactable {
var onEngagementChanged: CoreSdkClient.EngagementChangedBlock {
return { [weak self] engagement in
Expand Down Expand Up @@ -380,11 +382,12 @@ extension Interactor: CoreSdkClient.Interactable {
}

func end(with reason: CoreSdkClient.EngagementEndingReason) {
currentEngagement = environment.coreSdk.getCurrentEngagement()
switch reason {
case .visitorHungUp:
state = .ended(.byVisitor)
case .operatorHungUp, .followUp:
// Save engagement ended by operator to fetch a survey
endedEngagement = environment.coreSdk.getCurrentEngagement()
state = .ended(.byOperator)
case .error:
state = .ended(.byError)
Expand Down Expand Up @@ -423,5 +426,9 @@ extension Interactor {
func setCurrentEngagement(_ engagement: CoreSdkClient.Engagement?) {
currentEngagement = engagement
}

func setEndedEngagement(_ engagement: CoreSdkClient.Engagement?) {
endedEngagement = engagement
}
}
#endif
11 changes: 4 additions & 7 deletions GliaWidgets/Sources/ViewModel/EngagementViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class EngagementViewModel: CommonEngagementModel {

func viewWillAppear() {
if interactor.state == .ended(.byOperator) {
interactor.currentEngagement?.getSurvey { [weak self] result in
interactor.endedEngagement?.getSurvey { [weak self] result in
guard let self = self else { return }
switch result {
case .success(let survey) where survey != nil:
Expand Down Expand Up @@ -121,9 +121,8 @@ class EngagementViewModel: CommonEngagementModel {
operatorImageUrl: nil
)
)
engagementDelegate?(.finished)
case .ended(let reason) where reason == .byOperator:
interactor.currentEngagement?.getSurvey(completion: { [weak self] result in
interactor.endedEngagement?.getSurvey(completion: { [weak self] result in
guard let self = self else { return }
guard case .success(let survey) = result, survey == nil else {
self.endSession()
Expand Down Expand Up @@ -174,12 +173,10 @@ class EngagementViewModel: CommonEngagementModel {
}

func endSession() {
interactor.endSession { [weak self] in
self?.engagementDelegate?(.finished)
} failure: { [weak self] _ in
interactor.endSession { [weak self] _ in
self?.engagementDelegate?(.finished)
}
self.screenShareHandler.stop(nil)
screenShareHandler.stop(nil)
}

func conditionallyEndSession() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class EngagementCoordinatorSurveyTests: XCTestCase {
media: .init(audio: .none, video: .oneWay)
)
let interactor = Interactor.mock(environment: .init(coreSdk: coreSdkClient, queuesMonitor: .mock(), gcd: .failing, log: .failing))
interactor.setCurrentEngagement(engagement)
interactor.setEndedEngagement(engagement)
var alertManagerEnv = AlertManager.Environment.failing()
var log = CoreSdkClient.Logger.failing
log.prefixedClosure = { _ in log }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ final class EngagementCoordinatorTests: XCTestCase {
func test_endChatWithSurvey() throws {
let survey: CoreSdkClient.Survey = try .mock()
let engagement: CoreSdkClient.Engagement = .mock(fetchSurvey: { _, completion in completion(.success(survey)) })
coordinator.interactor.setCurrentEngagement(engagement)
coordinator.interactor.setEndedEngagement(engagement)
coordinator.end()

let surveyViewController = coordinator.gliaPresenter.topMostViewController as? Survey.ViewController
Expand Down
10 changes: 5 additions & 5 deletions GliaWidgetsTests/Sources/Glia/GliaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ final class GliaTests: XCTestCase {
theme: .mock()
) { _ in }

sdk.interactor?.setCurrentEngagement(.mock(source: .callVisualizer))
sdk.interactor?.setEndedEngagement(.mock(source: .callVisualizer))
sdk.interactor?.state = .ended(.byOperator)

XCTAssertEqual(calls, [.onEvent(.ended)])
Expand Down Expand Up @@ -202,7 +202,7 @@ final class GliaTests: XCTestCase {

XCTAssertEqual(calls, [])

sdk.interactor?.setCurrentEngagement(.mock(source: .callVisualizer))
sdk.interactor?.setEndedEngagement(.mock(source: .callVisualizer))
sdk.interactor?.state = .ended(.byOperator)

/// Since interactor is created only after visitor code is requested,
Expand Down Expand Up @@ -247,9 +247,9 @@ final class GliaTests: XCTestCase {
with: .mock(),
theme: .mock()
) { _ in }
sdk.interactor?.setCurrentEngagement(.mock(source: .callVisualizer))

sdk.callVisualizer.coordinator.showEndScreenSharingViewController()
sdk.interactor?.setEndedEngagement(.mock(source: .callVisualizer))
sdk.interactor?.state = .ended(.byOperator)

XCTAssertEqual(calls, [.onEvent(.minimized), .onEvent(.ended)])
Expand Down Expand Up @@ -295,9 +295,9 @@ final class GliaTests: XCTestCase {
with: .mock(),
theme: .mock()
) { _ in }
sdk.interactor?.setCurrentEngagement(.mock(source: .callVisualizer))

sdk.callVisualizer.coordinator.showVideoCallViewController()
sdk.interactor?.setEndedEngagement(.mock(source: .callVisualizer))
sdk.interactor?.state = .ended(.byOperator)

XCTAssertEqual(calls, [.onEvent(.maximized), .onEvent(.minimized), .onEvent(.ended)])
Expand Down Expand Up @@ -654,7 +654,7 @@ final class GliaTests: XCTestCase {
with: .mock(),
theme: .mock()
) { _ in }
sdk.interactor?.setCurrentEngagement(.mock(source: .callVisualizer))
sdk.interactor?.setEndedEngagement(.mock(source: .callVisualizer))
sdk.onEvent = { event in
switch event {
case .ended:
Expand Down
Loading

0 comments on commit fdaf96c

Please sign in to comment.