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

Integrate queues monitor and engagement launcher into EntryWidget #1066

Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 22 additions & 4 deletions GliaWidgets.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,16 @@
1AFB1E7425F8B00B00CA460D /* ChatTextContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AFB1E7325F8B00B00CA460D /* ChatTextContentView.swift */; };
1AFB1E7825F8B26800CA460D /* ChatTextContentStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AFB1E7725F8B26800CA460D /* ChatTextContentStyle.swift */; };
2100B4802CB6B5A400AC7527 /* LockIsolated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2100B47F2CB6B5A400AC7527 /* LockIsolated.swift */; };
2100B47C2CB66B6500AC7527 /* EntryWidget.Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2100B47B2CB66B6500AC7527 /* EntryWidget.Environment.swift */; };
2100B47E2CB6A37A00AC7527 /* QueuesMonitor.Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2100B47D2CB6A37A00AC7527 /* QueuesMonitor.Mock.swift */; };
2100B4802CB6B5A400AC7527 /* LockIsolated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2100B47F2CB6B5A400AC7527 /* LockIsolated.swift */; };
2100B4842CB8143400AC7527 /* QueuesMonitor.Failing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2100B4832CB8143400AC7527 /* QueuesMonitor.Failing.swift */; };
2100B4872CB91E7B00AC7527 /* CancelBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2100B4862CB91E7B00AC7527 /* CancelBag.swift */; };
215A25902CA44D8A0013023E /* Glia+EngagementLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215A258F2CA44D8A0013023E /* Glia+EngagementLauncher.swift */; };
215A25932CA44D900013023E /* EngagementLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215A25912CA44D900013023E /* EngagementLauncher.swift */; };
215A25982CABC7DF0013023E /* EngagementLauncherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215A25972CABC7DF0013023E /* EngagementLauncherTests.swift */; };
215A259A2CAC19780013023E /* GliaTests+EngagementLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215A25992CAC19780013023E /* GliaTests+EngagementLauncher.swift */; };
2198B7AC2CAEB14D002C442B /* QueuesMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2198B7AB2CAEB14D002C442B /* QueuesMonitor.swift */; };
2198B7AC2CAEB14D002C442B /* QueuesMonitor.Live.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2198B7AB2CAEB14D002C442B /* QueuesMonitor.Live.swift */; };
2198B7AE2CB035A6002C442B /* QueuesMonitor.Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2198B7AD2CB035A6002C442B /* QueuesMonitor.Environment.swift */; };
23D69155F4F4C5043173EF05 /* Pods_GliaWidgets.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7A5CDD05FB57D55971AA68A /* Pods_GliaWidgets.framework */; };
3100D929296E946600DEC9CE /* SecureConversations.ConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3100D924296E946600DEC9CE /* SecureConversations.ConfirmationView.swift */; };
Expand Down Expand Up @@ -1210,11 +1215,16 @@
1AFB1E7325F8B00B00CA460D /* ChatTextContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTextContentView.swift; sourceTree = "<group>"; };
1AFB1E7725F8B26800CA460D /* ChatTextContentStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTextContentStyle.swift; sourceTree = "<group>"; };
2100B47F2CB6B5A400AC7527 /* LockIsolated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockIsolated.swift; sourceTree = "<group>"; };
2100B47B2CB66B6500AC7527 /* EntryWidget.Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryWidget.Environment.swift; sourceTree = "<group>"; };
2100B47D2CB6A37A00AC7527 /* QueuesMonitor.Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueuesMonitor.Mock.swift; sourceTree = "<group>"; };
2100B47F2CB6B5A400AC7527 /* LockIsolated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockIsolated.swift; sourceTree = "<group>"; };
2100B4832CB8143400AC7527 /* QueuesMonitor.Failing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueuesMonitor.Failing.swift; sourceTree = "<group>"; };
2100B4862CB91E7B00AC7527 /* CancelBag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelBag.swift; sourceTree = "<group>"; };
215A258F2CA44D8A0013023E /* Glia+EngagementLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Glia+EngagementLauncher.swift"; sourceTree = "<group>"; };
215A25912CA44D900013023E /* EngagementLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EngagementLauncher.swift; sourceTree = "<group>"; };
215A25972CABC7DF0013023E /* EngagementLauncherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EngagementLauncherTests.swift; sourceTree = "<group>"; };
215A25992CAC19780013023E /* GliaTests+EngagementLauncher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GliaTests+EngagementLauncher.swift"; sourceTree = "<group>"; };
2198B7AB2CAEB14D002C442B /* QueuesMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueuesMonitor.swift; sourceTree = "<group>"; };
2198B7AB2CAEB14D002C442B /* QueuesMonitor.Live.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueuesMonitor.Live.swift; sourceTree = "<group>"; };
2198B7AD2CB035A6002C442B /* QueuesMonitor.Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueuesMonitor.Environment.swift; sourceTree = "<group>"; };
235300A49A5836A51EB1C4E8 /* Pods-GliaWidgets.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GliaWidgets.release.xcconfig"; path = "Target Support Files/Pods-GliaWidgets/Pods-GliaWidgets.release.xcconfig"; sourceTree = "<group>"; };
2797F86D83B9055FAD6E596E /* Pods-SnapshotTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SnapshotTests.debug.xcconfig"; path = "Target Support Files/Pods-SnapshotTests/Pods-SnapshotTests.debug.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2349,6 +2359,7 @@
9A3E1D9C27BA7741005634EB /* FoundationBased.Failing.swift */,
84D5B9652A15204400807F92 /* QuickLookBased.Failing.swift */,
9A1992E627D66C7400161AAE /* UIKitBased.Failing.swift */,
2100B4832CB8143400AC7527 /* QueuesMonitor.Failing.swift */,
AF6291142B0818DE00D3D76B /* SwiftBased.Failing.swift */,
AFF9542B2ADDA10600C277E0 /* CoreSDKConfigurator.Failing.swift */,
AF1C197F2B14FE9F00F8810F /* ConditionalCompilationClient.Failing.swift */,
Expand Down Expand Up @@ -3226,6 +3237,7 @@
isa = PBXGroup;
children = (
2100B47F2CB6B5A400AC7527 /* LockIsolated.swift */,
2100B4862CB91E7B00AC7527 /* CancelBag.swift */,
);
path = Utilities;
sourceTree = "<group>";
Expand All @@ -3249,7 +3261,8 @@
2198B7AA2CAEB13E002C442B /* QueuesMonitor */ = {
isa = PBXGroup;
children = (
2198B7AB2CAEB14D002C442B /* QueuesMonitor.swift */,
2198B7AB2CAEB14D002C442B /* QueuesMonitor.Live.swift */,
2100B47D2CB6A37A00AC7527 /* QueuesMonitor.Mock.swift */,
2198B7AD2CB035A6002C442B /* QueuesMonitor.Environment.swift */,
);
path = QueuesMonitor;
Expand Down Expand Up @@ -5038,6 +5051,7 @@
children = (
C0F7EA372CA1D6D40038019C /* CustomPresentationController.swift */,
C0F3DE362C69F51D00DE6D7B /* EntryWidget.swift */,
2100B47B2CB66B6500AC7527 /* EntryWidget.Environment.swift */,
C0F3DE3E2C6E176A00DE6D7B /* EntryWidget.Channel.swift */,
C0F3DE382C69FC2100DE6D7B /* EntryWidget.Presentation.swift */,
C0F7EA392CA1D7050038019C /* EntryWidget.SizeConstraints.swift */,
Expand Down Expand Up @@ -5638,7 +5652,7 @@
1A6EBB0325ADB82000EE325D /* MediaUpgradeActionView.swift in Sources */,
9AB196E627C510DA00FD60AB /* ChatMessage.Mock.swift in Sources */,
C08D776428F5910A000461E5 /* UIView+Extensions.swift in Sources */,
2198B7AC2CAEB14D002C442B /* QueuesMonitor.swift in Sources */,
2198B7AC2CAEB14D002C442B /* QueuesMonitor.Live.swift in Sources */,
C090467F2B7D022C003C437C /* WelcomeStyle.FilePickerButtonStyle.RemoteConfig.swift in Sources */,
C0D6CA4F2C19B9A300D4709B /* GliaPresenter.Environment.swift in Sources */,
9A19926A27D3BA8700161AAE /* ViewFactory.Environment.Interface.swift in Sources */,
Expand Down Expand Up @@ -5767,6 +5781,7 @@
C0F3DE3B2C6E0DD900DE6D7B /* EntryWidgetView.swift in Sources */,
C090474C2B7E210C003C437C /* FileUploadStyle.Equatable.swift in Sources */,
C09047402B7E1FBC003C437C /* MessageCenterFileUploadStyle.swift in Sources */,
2100B47E2CB6A37A00AC7527 /* QueuesMonitor.Mock.swift in Sources */,
9A8130BB27D7A41000220BBD /* FileUpload.Environment.Interface.swift in Sources */,
C09046AB2B7D0967003C437C /* WelcomeStyle.SendButton.DisabledStyle.Accessibility.swift in Sources */,
84520BE72B1769AB00F97617 /* ChatViewController.Environment.swift in Sources */,
Expand Down Expand Up @@ -5811,6 +5826,7 @@
C06A7588296ECD75006B69A2 /* Theme+VisitorCode.swift in Sources */,
C090476C2B7E24A8003C437C /* ChoiceCardOptionStateStyle.RemoteConfig.swift in Sources */,
C0857DED28D4831E008D171D /* Theme.Text.swift in Sources */,
2100B47C2CB66B6500AC7527 /* EntryWidget.Environment.swift in Sources */,
848B8ADC2C0759C500E990E6 /* Theme.CustomCardContainerStyle.swift in Sources */,
845876A22823FF34007AC3DF /* Survey.ViewController.Props.Mock.swift in Sources */,
C090471D2B7E1AFF003C437C /* GvaGalleryCardStyle.ButtonStyle.swift in Sources */,
Expand Down Expand Up @@ -5880,6 +5896,7 @@
7594098C298D38C2008B173A /* CallVisualizer.Environment.swift in Sources */,
1A1E309B25F8E1F700850E68 /* DataStorage.swift in Sources */,
AF6AB34D298A9F2500003645 /* SecureConversations.FileUploadListViewModel.swift in Sources */,
2100B4872CB91E7B00AC7527 /* CancelBag.swift in Sources */,
C0D6CA292C199A4700D4709B /* UnreadMessageIndicatorView.Environment.swift in Sources */,
C0D6CA1B2C185BDE00D4709B /* EngagementView.Environment.swift in Sources */,
C0D6CA1B2C185BDE00D4709B /* EngagementView.Environment.swift in Sources */,
Expand Down Expand Up @@ -6505,6 +6522,7 @@
847A7643285A1914004044D1 /* FileUploadListViewModelTests.swift in Sources */,
9A1992E727D66C7400161AAE /* UIKitBased.Failing.swift in Sources */,
31FF0DCB2B5907C600834AFB /* ChatCoordinatorTests.swift in Sources */,
2100B4842CB8143400AC7527 /* QueuesMonitor.Failing.swift in Sources */,
3146C9432AB1851C0047D8CC /* LocalizationTests.swift in Sources */,
8492F9172CAD2F2000242691 /* TranscriptModelTests+ResponseCard.swift in Sources */,
3115EFBA2BC960B500B24D5A /* (null) in Sources */,
Expand Down
11 changes: 8 additions & 3 deletions GliaWidgets/Public/Glia/Glia+EntryWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ extension Glia {
///
/// - Returns:
/// - `EntryWidget` instance.
public func getEntryWidget(queueIds: [String]) -> EntryWidget {
// The real implementation will be added once EngagementLauncher is added
.init(theme: theme)
public func getEntryWidget(queueIds: [String]) throws -> EntryWidget {
EntryWidget(
environment: .init(
queuesMonitor: environment.queuesMonitor,
engagementLauncher: try getEngagementLauncher(queueIds: queueIds),
theme: theme
)
)
}
}
21 changes: 21 additions & 0 deletions GliaWidgets/Sources/EntryWidget/EntryWidget.Channel.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import GliaCoreSDK
import SwiftUI

extension EntryWidget {
Expand Down Expand Up @@ -47,3 +48,23 @@ extension EntryWidget {
}
}
}

extension EntryWidget.Channel {
init?(mediaType: MediaType) {
switch mediaType {
case .audio:
self = .audio
case .video:
self = .video
case .text:
self = .chat
case .messaging:
self = .secureMessaging
case .phone, .unknown:
return nil
@unknown default:
debugPrint("💥 Unknown type MediaType received in: \(Self.self)")
return nil
}
}
}
9 changes: 9 additions & 0 deletions GliaWidgets/Sources/EntryWidget/EntryWidget.Environment.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation

extension EntryWidget {
struct Environment {
var queuesMonitor: QueuesMonitor
var engagementLauncher: EngagementLauncher
var theme: Theme
}
}
97 changes: 70 additions & 27 deletions GliaWidgets/Sources/EntryWidget/EntryWidget.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Combine
import Foundation
import GliaCoreSDK
import UIKit
import SwiftUI

Expand All @@ -7,9 +9,7 @@ public final class EntryWidget: NSObject {
private var embeddedView: UIView?
private var queueIds: [String] = []

// Channels will become dynamic in the subsequent PRs
private var channels: [Channel] = [.chat, .audio, .video, .secureMessaging]
private let theme: Theme
@Published private var channels: [Channel] = []
private let sizeConstraints: SizeConstraints = .init(
singleCellHeight: 72,
singleCellIconSize: 24,
Expand All @@ -20,11 +20,23 @@ public final class EntryWidget: NSObject {
dividerHeight: 1
)

init(theme: Theme) {
self.theme = theme
private let environment: Environment

private var cancellables = CancelBag()

init(environment: Environment) {
self.environment = environment
super.init()

environment.queuesMonitor.$state
.sink(receiveValue: handleQueuesMonitorUpdates(state:))
.store(in: &cancellables)
}

public func show(by presentation: EntryWidget.Presentation) {
EgorovEI marked this conversation as resolved.
Show resolved Hide resolved
defer {
environment.queuesMonitor.startMonitoring(queuesIds: queueIds)
}
switch presentation {
case let .sheet(parentViewController):
showSheet(in: parentViewController)
Expand All @@ -38,14 +50,8 @@ public final class EntryWidget: NSObject {
hostedViewController = nil
embeddedView?.removeFromSuperview()
embeddedView = nil
}

func createEntryWidgetView(
queueIds: [String],
channels: [Channel] = [.chat, .audio, .video]
) {
self.queueIds = queueIds
self.channels = channels
environment.queuesMonitor.stopMonitoring()
}
}

Expand All @@ -55,10 +61,7 @@ private extension EntryWidget {
let model = makeViewModel(
showHeader: false,
channels: channels,
selection: { channel in
// Logic for handling this callback will be handled later - MOB-3473
print(channel.headline)
}
selection: channelSelected(_:)
)
let view = makeView(model: model)
let hostingController = UIHostingController(rootView: view)
Expand All @@ -80,21 +83,18 @@ private extension EntryWidget {
let model = makeViewModel(
showHeader: true,
channels: channels,
selection: { channel in
// Logic for handling this callback will be handled later - MOB-3473
print(channel.headline)
}
selection: channelSelected(_:)
)
let view = makeView(model: model)
let hostingController = UIHostingController(rootView: view)

switch theme.entryWidget.backgroundColor {
switch environment.theme.entryWidget.backgroundColor {
case .fill(let color):
hostingController.view.backgroundColor = color
case .gradient(let colors):
hostingController.view.makeGradientBackground(
colors: colors,
cornerRadius: theme.entryWidget.cornerRadius
cornerRadius: environment.theme.entryWidget.cornerRadius
)
}

Expand All @@ -113,7 +113,7 @@ private extension EntryWidget {
}
sheet.detents = [smallDetent]
sheet.prefersScrollingExpandsWhenScrolledToEdge = true
sheet.preferredCornerRadius = theme.entryWidget.cornerRadius
sheet.preferredCornerRadius = environment.theme.entryWidget.cornerRadius
} else {
hostingController.modalPresentationStyle = .custom
hostingController.transitioningDelegate = self
Expand All @@ -130,13 +130,13 @@ private extension EntryWidget {
func makeViewModel(
showHeader: Bool,
channels: [Channel],
selection: @escaping (Channel) -> Void
selection: @escaping (Channel) throws -> Void
) -> EntryWidgetView.Model {
.init(
theme: theme,
theme: environment.theme,
showHeader: showHeader,
sizeConstrainsts: sizeConstraints,
channels: channels,
channels: $channels,
channelSelected: selection
)
}
Expand Down Expand Up @@ -172,7 +172,50 @@ extension EntryWidget: UIViewControllerTransitioningDelegate {
presentedViewController: presented,
presenting: presenting,
height: height,
cornerRadius: theme.entryWidget.cornerRadius
cornerRadius: environment.theme.entryWidget.cornerRadius
)
}
}

private extension EntryWidget {
func handleQueuesMonitorUpdates(state: QueuesMonitor.State) {
switch state {
case .idle:
break
case .updated(let queues):
let availableChannels = resolveAvailableChannels(from: queues)
self.channels = availableChannels
case .failed(let error):
// TODO: Handle error on EntryWidgetView
print(error)
}
}

func resolveAvailableChannels(from queues: [Queue]) -> [Channel] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand, usage of Set makes sure that channels are unique.
Is it necessary to return Array here, instead of a Set used inside? If conversation to Array needed for consumer of the channels, it probably makes sense to convert at consumer side, otherwise it is not clear why we convert it here to Array.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On UI it will be needed to sort the Channels' list, so that's why it's returning Array.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to return sorted array to preserve some kind of oder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know which order is required on the UI side, I will left a TODO comment so @rasmustautsglia will sort it in a way needed

var availableChannels: Set<Channel> = []

queues.forEach { queue in
queue.state.media.forEach { mediaType in
guard let channel = Channel(mediaType: mediaType) else {
return
}
availableChannels.insert(channel)
}
}
// TODO: Add sorting for representing on UI
return Array(availableChannels)
}

func channelSelected(_ channel: Channel) throws {
switch channel {
case .chat:
try environment.engagementLauncher.startChat()
case .audio:
try environment.engagementLauncher.startAudioCall()
case .video:
try environment.engagementLauncher.startVideoCall()
case .secureMessaging:
try environment.engagementLauncher.startSecureMessaging()
}
}
}
20 changes: 13 additions & 7 deletions GliaWidgets/Sources/EntryWidget/EntryWidgetViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import SwiftUI
extension EntryWidgetView {
class Model: ObservableObject {
@Published var viewState: ViewState
@Published var channels: [EntryWidget.Channel] = []
let theme: Theme
let channelSelected: (EntryWidget.Channel) -> Void
let channels: [EntryWidget.Channel]
let channelSelected: (EntryWidget.Channel) throws -> Void
let sizeConstraints: EntryWidget.SizeConstraints
let showHeader: Bool

Expand All @@ -25,22 +25,28 @@ extension EntryWidgetView {
theme: Theme,
showHeader: Bool,
sizeConstrainsts: EntryWidget.SizeConstraints,
channels: [EntryWidget.Channel],
channelSelected: @escaping (EntryWidget.Channel) -> Void
channels: Published<[EntryWidget.Channel]>.Publisher,
channelSelected: @escaping (EntryWidget.Channel) throws -> Void
) {
self.theme = theme
self.sizeConstraints = sizeConstrainsts
self.showHeader = showHeader
self.channels = channels
self.channelSelected = channelSelected
self.viewState = .offline
self.viewState = .mediaTypes

channels.assign(to: &self.$channels)
}
}
}

extension EntryWidgetView.Model {
func selectChannel(_ channel: EntryWidget.Channel) {
channelSelected(channel)
do {
try channelSelected(channel)
} catch {
// TODO: Distinguish errors on View if needed
viewState = .error
}
}

func onTryAgainTapped() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ extension Glia {
var snackBar: SnackBar
var processInfo: ProcessInfoHandling
var cameraDeviceManager: CoreSdkClient.GetCameraDeviceManageable
var queuesMonitor: QueuesMonitor
}
}

Expand Down
Loading
Loading