Skip to content

Commit

Permalink
Show only one available engagement type in Entry Widget when queueing
Browse files Browse the repository at this point in the history
When visitor is enqueued, instead of showing all the options in EntryWidget,
We show only the one that the visitor is enqueued to.

MOB-3950
  • Loading branch information
rasmustautsglia authored and github-review-helper committed Jan 22, 2025
1 parent c4310c8 commit 3506a74
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 62 deletions.
10 changes: 5 additions & 5 deletions GliaWidgets.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -5244,19 +5244,19 @@
C0F3DE352C69F4A700DE6D7B /* EntryWidget */ = {
isa = PBXGroup;
children = (
216D31192CFE25630019CA9E /* MediaTypeItems */,
216D31182CFE25570019CA9E /* MediaTypeItem */,
C0F7EA372CA1D6D40038019C /* CustomPresentationController.swift */,
216D31022CEF83A90019CA9E /* EntryWidget.Builder.swift */,
C0F3DE362C69F51D00DE6D7B /* EntryWidget.swift */,
C0F7EA392CA1D7050038019C /* EntryWidget.Configuration.swift */,
2100B47B2CB66B6500AC7527 /* EntryWidget.Environment.swift */,
C0F3DE3E2C6E176A00DE6D7B /* EntryWidget.MediaTypeItem.swift */,
C0F3DE382C69FC2100DE6D7B /* EntryWidget.Presentation.swift */,
C0F7EA392CA1D7050038019C /* EntryWidget.Configuration.swift */,
C0F3DE442C6E3D7C00DE6D7B /* EntryWidgetStyle.swift */,
C0F3DE362C69F51D00DE6D7B /* EntryWidget.swift */,
C034EEEC2CAAB525002650B8 /* EntryWidgetStyle.RemoteConfig.swift */,
C0F3DE442C6E3D7C00DE6D7B /* EntryWidgetStyle.swift */,
C0F3DE3A2C6E0DD900DE6D7B /* EntryWidgetView.swift */,
C0F3DE3C2C6E170600DE6D7B /* EntryWidgetViewModel.swift */,
216D31182CFE25570019CA9E /* MediaTypeItem */,
216D31192CFE25630019CA9E /* MediaTypeItems */,
);
path = EntryWidget;
sourceTree = "<group>";
Expand Down
161 changes: 112 additions & 49 deletions GliaWidgets/Sources/EntryWidget/EntryWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ public final class EntryWidget: NSObject {
private var embeddedView: UIView?
private var queueIds: [String]
private let configuration: Configuration
private var cancellables = CancelBag()
private let environment: Environment
@Published private var unreadSecureMessageCount: Int?
private(set) var unreadSecureMessageSubscriptionId: String?
private var engagementCancellable: AnyCancellable?
@Published var viewState: ViewState = .loading
@Published private(set) var availableEngagementTypes: [EntryWidget.EngagementType] = []
private var ongoingEngagement: Engagement?
private var interactorState: InteractorState = .none

Check warning on line 22 in GliaWidgets/Sources/EntryWidget/EntryWidget.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
private var cancellables = CancelBag()

// MARK: - Initialization

Expand All @@ -45,12 +45,21 @@ public final class EntryWidget: NSObject {
.sink(receiveValue: handleQueuesMonitorUpdates(state:unreadSecureMessagesCount:))
.store(in: &cancellables)

engagementCancellable = environment.currentInteractor()?.$currentEngagement
environment.currentInteractor()?.$currentEngagement
.sink { [weak self] engagement in
guard let self else { return }
ongoingEngagement = engagement
handleQueuesMonitorUpdates(state: environment.queuesMonitor.state, unreadSecureMessagesCount: unreadSecureMessageCount)
}
.store(in: &cancellables)

environment.currentInteractor()?.$state
.sink { [weak self] state in
guard let self else { return }
interactorState = state
handleQueuesMonitorUpdates(state: environment.queuesMonitor.state, unreadSecureMessagesCount: unreadSecureMessageCount)
}
.store(in: &cancellables)
}

deinit {
Expand Down Expand Up @@ -143,51 +152,6 @@ extension EntryWidget {

// MARK: - Private methods
private extension EntryWidget {
func handleQueuesMonitorUpdates(
state: QueuesMonitor.State,
unreadSecureMessagesCount: Int?
) {
if let ongoingEngagement {
environment.log.info("Preparing items based on ongoing engagement")
if ongoingEngagement.source == .callVisualizer {
viewState = .ongoingEngagement(.callVisualizer)
} else if ongoingEngagement.source == .coreEngagement {
if ongoingEngagement.mediaStreams.video != nil {
viewState = .ongoingEngagement(.video)
} else if ongoingEngagement.mediaStreams.audio != nil {
viewState = .ongoingEngagement(.audio)
} else {
viewState = .ongoingEngagement(.chat)
}
}
} else {
switch state {
case .idle:
viewState = .loading
case .updated(let queues):
let availableEngagementTypes = resolveAvailableEngagementTypes(from: queues)
if availableEngagementTypes.isEmpty {
viewState = .offline
} else {
let mediaTypes = availableEngagementTypes.map { type in
if type == .secureMessaging {
return EntryWidget.MediaTypeItem(
type: type,
badgeCount: unreadSecureMessagesCount ?? 0
)
}
return EntryWidget.MediaTypeItem(type: type)
}
viewState = .mediaTypes(mediaTypes)
}
self.availableEngagementTypes = availableEngagementTypes
case .failed(let error):
viewState = .error
environment.log.prefixed(Self.self).error("Setting up queues. Failed to get site queues \(error)")
}
}
}

func observeSecureUnreadMessageCount() {
self.unreadSecureMessageSubscriptionId = environment.observeSecureUnreadMessageCount { [weak self] result in
guard let self else {
Expand Down Expand Up @@ -342,6 +306,94 @@ private extension EntryWidget {
}
}

// MARK: Queue Updates handling
private extension EntryWidget {
func handleQueuesMonitorUpdates(
state: QueuesMonitor.State,
unreadSecureMessagesCount: Int?
) {
if let ongoingState = resolveViewState(for: ongoingEngagement) {
environment.log.info("Preparing items based on ongoing engagement")
viewState = ongoingState
} else if let enqueuedState = resolveViewState(for: interactorState) {
environment.log.info("Preparing items based on ongoing engagement")
viewState = enqueuedState
} else {
viewState = resolveViewState(
for: state,
unreadSecureMessagesCount: unreadSecureMessagesCount
)
}
}

func resolveViewState(for ongoingEngagement: Engagement?) -> EntryWidget.ViewState? {
guard let ongoingEngagement else {
return nil
}

if ongoingEngagement.source == .callVisualizer {
return .ongoingEngagement(.callVisualizer)
} else if ongoingEngagement.source == .coreEngagement {
if ongoingEngagement.mediaStreams.video != nil {
return .ongoingEngagement(.video)
} else if ongoingEngagement.mediaStreams.audio != nil {
return .ongoingEngagement(.audio)
} else {
return .ongoingEngagement(.chat)
}
}
return nil
}

func resolveViewState(for interactorState: InteractorState) -> EntryWidget.ViewState? {
guard let engagementKind = interactorState.enqueiengEngagementKind else {
return nil
}

switch engagementKind {
case .none:
return nil
case .chat:
return .ongoingEngagement(.chat)
case .audioCall:
return .ongoingEngagement(.audio)
case .videoCall:
return .ongoingEngagement(.video)
case .messaging:
return .ongoingEngagement(.secureMessaging)
}
}

func resolveViewState(
for queuesMonitorState: QueuesMonitor.State,
unreadSecureMessagesCount: Int?
) -> EntryWidget.ViewState {
switch queuesMonitorState {
case .idle:
return .loading
case .updated(let queues):
let availableEngagementTypes = resolveAvailableEngagementTypes(from: queues)
if availableEngagementTypes.isEmpty {
return .offline
} else {
let mediaTypes = availableEngagementTypes.map { type in
if type == .secureMessaging {
return EntryWidget.MediaTypeItem(
type: type,
badgeCount: unreadSecureMessagesCount ?? 0
)
}
return EntryWidget.MediaTypeItem(type: type)
}
return .mediaTypes(mediaTypes)
}
case .failed(let error):
environment.log.prefixed(Self.self).error("Setting up queues. Failed to get site queues \(error)")
return .error
}
}
}

// MARK: - View State
extension EntryWidget {
enum ViewState: Equatable {
Expand All @@ -353,6 +405,17 @@ extension EntryWidget {
}
}

private extension InteractorState {
var enqueiengEngagementKind: EngagementKind? {
switch self {
case .enqueued(_, let engagementKind), .enqueueing(let engagementKind):
return engagementKind
case .none, .engaged, .ended:
return nil
}
}
}

// MARK: - UIViewControllerTransitioningDelegate
extension EntryWidget: UIViewControllerTransitioningDelegate {
public func presentationController(
Expand Down
2 changes: 1 addition & 1 deletion GliaWidgets/Sources/Interactor/Interactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class Interactor {

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

var state: InteractorState = .none {
@Published var state: InteractorState = .none {
didSet {
if oldValue != state {
notify(.stateChanged(state))
Expand Down
10 changes: 9 additions & 1 deletion GliaWidgets/Sources/View/Chat/ChatView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ extension ChatView {
}
}

// swiftlint:disable file_length
class ChatView: EngagementView {
let tableAndIndicatorStack = UIStackView()
let tableView = UITableView()
Expand Down Expand Up @@ -509,7 +510,13 @@ extension ChatView {
guard let entryWidget else {
return
}
let isAnyEngagementTypeAvailable = entryWidget.$availableEngagementTypes
let isAnyEngagementTypeAvailable = entryWidget.$viewState
.map {
if case let .mediaTypes(availableMediaTypes) = $0 {
return availableMediaTypes
}
return []
}
.map { !$0.isEmpty }
isAnyEngagementTypeAvailable
.filter { !$0 }
Expand Down Expand Up @@ -1001,3 +1008,4 @@ extension ChatView {
return .gvaPersistentButton(view)
}
}
// swiftlint:enable file_length
Loading

0 comments on commit 3506a74

Please sign in to comment.