diff --git a/GliaWidgets.xcodeproj/project.pbxproj b/GliaWidgets.xcodeproj/project.pbxproj index 0c622bca5..2c39a0b4a 100644 --- a/GliaWidgets.xcodeproj/project.pbxproj +++ b/GliaWidgets.xcodeproj/project.pbxproj @@ -598,7 +598,6 @@ C06A7584296EC9DC006B69A2 /* NumberSlotStyle.Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06A7583296EC9DC006B69A2 /* NumberSlotStyle.Accessibility.swift */; }; C06A7586296ECC57006B69A2 /* VisitorCodeStyle.Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06A7585296ECC57006B69A2 /* VisitorCodeStyle.Accessibility.swift */; }; C06A7588296ECD75006B69A2 /* Theme+VisitorCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06A7587296ECD75006B69A2 /* Theme+VisitorCode.swift */; }; - C07666752ACAD76C00BB8375 /* LiveObservationConfirmationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07666742ACAD76C00BB8375 /* LiveObservationConfirmationPresenter.swift */; }; C07F62462ABC322B003EFC97 /* OrientationManager.Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07F62452ABC322B003EFC97 /* OrientationManager.Mock.swift */; }; C07F62772AC1BA2B003EFC97 /* UIViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07F62762AC1BA2B003EFC97 /* UIViewController+Extensions.swift */; }; C07F62832AC33BB9003EFC97 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07F62822AC33BB9003EFC97 /* UIView+Extensions.swift */; }; @@ -1341,7 +1340,6 @@ C06A7583296EC9DC006B69A2 /* NumberSlotStyle.Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberSlotStyle.Accessibility.swift; sourceTree = ""; }; C06A7585296ECC57006B69A2 /* VisitorCodeStyle.Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisitorCodeStyle.Accessibility.swift; sourceTree = ""; }; C06A7587296ECD75006B69A2 /* Theme+VisitorCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+VisitorCode.swift"; sourceTree = ""; }; - C07666742ACAD76C00BB8375 /* LiveObservationConfirmationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveObservationConfirmationPresenter.swift; sourceTree = ""; }; C07F62452ABC322B003EFC97 /* OrientationManager.Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrientationManager.Mock.swift; sourceTree = ""; }; C07F62762AC1BA2B003EFC97 /* UIViewController+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extensions.swift"; sourceTree = ""; }; C07F62822AC33BB9003EFC97 /* UIView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = ""; }; @@ -2337,7 +2335,6 @@ 1A0EDF8925E8E20A0076D1AD /* Popover */, 1A6EBB2325ADE35500EE325D /* MediaUpgrade */, 1A63B2F1257A3F2D00508478 /* Alert */, - C07666742ACAD76C00BB8375 /* LiveObservationConfirmationPresenter.swift */, ); path = Common; sourceTree = ""; @@ -4347,7 +4344,6 @@ 75CF8D6329B7DD8300CB1524 /* SecureConversations+ConfirmationStyle+RemoteConfig.swift in Sources */, 1A475BB325DE831F00296D55 /* BadgeView.swift in Sources */, 1A38A8A8258B652B0089DE7B /* OperatorChatMessageStyle.swift in Sources */, - C07666752ACAD76C00BB8375 /* LiveObservationConfirmationPresenter.swift in Sources */, 1A7CA8212574D6760047CBBE /* ConnectOperatorView.swift in Sources */, 755D187329A6A6860009F5E8 /* WelcomeStyle+MessageTextViewActiveStyle.swift in Sources */, 9AB196D427C3D7FD00FD60AB /* Interactor.Environment.Mock.swift in Sources */, diff --git a/GliaWidgets/Sources/ViewController/Call/CallViewController.swift b/GliaWidgets/Sources/ViewController/Call/CallViewController.swift index ca17b1d58..18d990ee2 100644 --- a/GliaWidgets/Sources/ViewController/Call/CallViewController.swift +++ b/GliaWidgets/Sources/ViewController/Call/CallViewController.swift @@ -4,6 +4,9 @@ final class CallViewController: EngagementViewController { private let viewModel: CallViewModel private let environment: Environment private var proximityManager: ProximityManager? + private var hasViewAppeared = false + + private var pendingLiveObservationConfirmation: LiveObservationConfirmation? init(viewModel: CallViewModel, viewFactory: ViewFactory, environment: Environment) { self.environment = environment @@ -63,6 +66,15 @@ final class CallViewController: EngagementViewController { view.checkBarsOrientation() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + hasViewAppeared = true + if let alertConfig = pendingLiveObservationConfirmation { + showLiveObservationConfirmationAlert(with: alertConfig) + pendingLiveObservationConfirmation = nil + } + } + @objc private func deviceDidRotate() { guard let view = view as? CallView else { return } @@ -125,6 +137,8 @@ final class CallViewController: EngagementViewController { view.isVisitrOnHold = isOnHold case .transferring: view.setConnectState(.transferring, animated: true) + case let .showLiveObservationConfirmation(configuration): + self?.showLiveObservationConfirmation(with: configuration) } } } @@ -140,6 +154,26 @@ final class CallViewController: EngagementViewController { style: props.style ) } + + private func showLiveObservationConfirmation(with config: LiveObservationConfirmation) { + switch hasViewAppeared { + case true: showLiveObservationConfirmationAlert(with: config) + case false: pendingLiveObservationConfirmation = config + } + } + + private func showLiveObservationConfirmationAlert(with config: LiveObservationConfirmation) { + let alert = AlertViewController( + kind: .liveObservationConfirmation( + config.conf, + accepted: config.accepted, + declined: config.declined + ), + viewFactory: self.viewFactory + ) + + replacePresentedOfferIfPossible(with: alert) + } } private extension CallViewModel.CallButton { @@ -194,4 +228,10 @@ extension CallViewController { var uiDevice: UIKitBased.UIDevice var notificationCenter: FoundationBased.NotificationCenter } + + struct LiveObservationConfirmation { + let conf: ConfirmationAlertConfiguration + let accepted: () -> Void + let declined: () -> Void + } } diff --git a/GliaWidgets/Sources/ViewController/Chat/ChatViewController.swift b/GliaWidgets/Sources/ViewController/Chat/ChatViewController.swift index b70a25503..17c07b134 100644 --- a/GliaWidgets/Sources/ViewController/Chat/ChatViewController.swift +++ b/GliaWidgets/Sources/ViewController/Chat/ChatViewController.swift @@ -8,6 +8,8 @@ final class ChatViewController: EngagementViewController, PopoverPresenter { } } private var lastVisibleRowIndexPath: IndexPath? + private var hasViewAppeared = false + private var pendingLiveObservationConfirmation: CallViewController.LiveObservationConfirmation? init( viewModel: SecureConversations.ChatWithTranscriptModel, @@ -36,6 +38,15 @@ final class ChatViewController: EngagementViewController, PopoverPresenter { viewModel.event(.viewDidLoad) } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + hasViewAppeared = true + if let alertConfig = pendingLiveObservationConfirmation { + showLiveObservationConfirmationAlert(with: alertConfig) + pendingLiveObservationConfirmation = nil + } + } + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() @@ -168,12 +179,35 @@ final class ChatViewController: EngagementViewController, PopoverPresenter { view?.messageEntryView.uploadListView.props = fileUploadListProps case let .quickReplyPropsUpdated(props): view?.renderQuickReply(props: props) + case let .showLiveObservationConfirmation(configuration): + self?.showLiveObservationConfirmation(with: configuration) } self?.renderProps() } } // swiftlint:enable function_body_length + private func showLiveObservationConfirmation( + with config: CallViewController.LiveObservationConfirmation) { + switch hasViewAppeared { + case true: showLiveObservationConfirmationAlert(with: config) + case false: pendingLiveObservationConfirmation = config + } + } + + private func showLiveObservationConfirmationAlert(with config: CallViewController.LiveObservationConfirmation) { + let alert = AlertViewController( + kind: .liveObservationConfirmation( + config.conf, + accepted: config.accepted, + declined: config.declined + ), + viewFactory: self.viewFactory + ) + + replacePresentedOfferIfPossible(with: alert) + } + private func presentMediaPicker( from sourceView: UIView, itemSelected: @escaping (AttachmentSourceItemKind) -> Void diff --git a/GliaWidgets/Sources/ViewController/Common/LiveObservationConfirmationPresenter.swift b/GliaWidgets/Sources/ViewController/Common/LiveObservationConfirmationPresenter.swift deleted file mode 100644 index 4463dea17..000000000 --- a/GliaWidgets/Sources/ViewController/Common/LiveObservationConfirmationPresenter.swift +++ /dev/null @@ -1,30 +0,0 @@ -import UIKit - -protocol LiveObservationConfirmationPresenter: DismissalAndPresentationController where Self: UIViewController { - var viewFactory: ViewFactory { get } - - func showLiveObservationConfirmationAlert( - with conf: ConfirmationAlertConfiguration, - accepted: @escaping () -> Void, - declined: @escaping () -> Void - ) -} - -extension LiveObservationConfirmationPresenter { - func showLiveObservationConfirmationAlert( - with conf: ConfirmationAlertConfiguration, - accepted: @escaping () -> Void, - declined: @escaping () -> Void - ) { - let alert = AlertViewController( - kind: .liveObservationConfirmation( - conf, - accepted: accepted, - declined: declined - ), - viewFactory: self.viewFactory - ) - - replacePresentedOfferIfPossible(with: alert) - } -} diff --git a/GliaWidgets/Sources/ViewController/EngagementViewController.swift b/GliaWidgets/Sources/ViewController/EngagementViewController.swift index df518b5b1..09db258dc 100644 --- a/GliaWidgets/Sources/ViewController/EngagementViewController.swift +++ b/GliaWidgets/Sources/ViewController/EngagementViewController.swift @@ -1,6 +1,6 @@ import UIKit -class EngagementViewController: UIViewController, AlertPresenter, MediaUpgradePresenter, ScreenShareOfferPresenter, LiveObservationConfirmationPresenter { +class EngagementViewController: UIViewController, AlertPresenter, MediaUpgradePresenter, ScreenShareOfferPresenter { let viewFactory: ViewFactory private var viewModel: CommonEngagementModel @@ -63,12 +63,6 @@ class EngagementViewController: UIViewController, AlertPresenter, MediaUpgradePr view?.header.showEndButton() case .showEndScreenShareButton: view?.header.showEndScreenSharingButton() - case .showLiveObservationConfirmation(let conf, let accepted, let declined): - self.showLiveObservationConfirmationAlert( - with: conf, - accepted: accepted, - declined: declined - ) } } } diff --git a/GliaWidgets/Sources/ViewModel/Call/CallViewModel.swift b/GliaWidgets/Sources/ViewModel/Call/CallViewModel.swift index e20c27693..de5ba2f18 100644 --- a/GliaWidgets/Sources/ViewModel/Call/CallViewModel.swift +++ b/GliaWidgets/Sources/ViewModel/Call/CallViewModel.swift @@ -78,8 +78,8 @@ class CallViewModel: EngagementViewModel, ViewModel { switch startWith { case .engagement(let mediaType): - enqueue(mediaType: mediaType) - + interactor.state = .enqueueing + showLiveObservationConfirmation(in: mediaType) case .call(offer: let offer, answer: let answer): call.upgrade(to: offer) showConnecting() @@ -315,6 +315,25 @@ extension CallViewModel { ) ) } + + private func showLiveObservationConfirmation(in mediaType: CoreSdkClient.MediaType) { + let liveObservationAlertConfig = createLiveObservationAlertConfig(with: mediaType) + action?(.showLiveObservationConfirmation(liveObservationAlertConfig)) + } + + private func createLiveObservationAlertConfig( + with mediaType: CoreSdkClient.MediaType + ) -> CallViewController.LiveObservationConfirmation { + .init( + conf: self.alertConfiguration.liveObservationConfirmation, + accepted: { [weak self] in + self?.enqueue(mediaType: mediaType) + }, + declined: { [weak self] in + self?.endSession() + } + ) + } } extension CallViewModel { @@ -537,6 +556,7 @@ extension CallViewModel { case setRemoteVideo(CoreSdkClient.StreamView?) case setLocalVideo(CoreSdkClient.StreamView?) case setVisitorOnHold(isOnHold: Bool) + case showLiveObservationConfirmation(CallViewController.LiveObservationConfirmation) } enum DelegateEvent { diff --git a/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+ViewModel.swift b/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+ViewModel.swift index 90e7493d8..4b6e65282 100644 --- a/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+ViewModel.swift +++ b/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel+ViewModel.swift @@ -55,6 +55,7 @@ extension ChatViewModel: ViewModel { case setAttachmentButtonVisibility(MediaPickerButtonVisibility) case fileUploadListPropsUpdated(SecureConversations.FileUploadListView.Props) case quickReplyPropsUpdated(QuickReplyView.Props) + case showLiveObservationConfirmation(CallViewController.LiveObservationConfirmation) } enum DelegateEvent { diff --git a/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel.swift b/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel.swift index f302c20ab..275ddfc08 100644 --- a/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel.swift +++ b/GliaWidgets/Sources/ViewModel/Chat/ChatViewModel.swift @@ -182,7 +182,6 @@ class ChatViewModel: EngagementViewModel { } } - // swiftlint:disable function_body_length override func update(for state: InteractorState) { super.update(for: state) @@ -199,15 +198,7 @@ class ChatViewModel: EngagementViewModel { action?(.queue) action?(.scrollToBottom(animated: true)) - engagementAction?(.showLiveObservationConfirmation( - self.alertConfiguration.liveObservationConfirmation, - accepted: { [weak self] in - self?.enqueue(mediaType: .text) - }, - declined: { [weak self] in - self?.endSession() - } - )) + showLiveObservationConfirmation(in: .text) case .engaged(let engagedOperator): let name = engagedOperator?.firstName let pictureUrl = engagedOperator?.picture?.url @@ -260,7 +251,6 @@ class ChatViewModel: EngagementViewModel { break } } - // swiftlint:enable function_body_length override func interactorEvent(_ event: InteractorEvent) { super.interactorEvent(event) @@ -941,6 +931,29 @@ extension ChatViewModel { } } +// MARK: Live Observation + +extension ChatViewModel { + private func showLiveObservationConfirmation(in mediaType: CoreSdkClient.MediaType) { + let liveObservationAlertConfig = createLiveObservationAlertConfig(with: mediaType) + action?(.showLiveObservationConfirmation(liveObservationAlertConfig)) + } + + private func createLiveObservationAlertConfig( + with mediaType: CoreSdkClient.MediaType + ) -> CallViewController.LiveObservationConfirmation { + .init( + conf: self.alertConfiguration.liveObservationConfirmation, + accepted: { [weak self] in + self?.enqueue(mediaType: mediaType) + }, + declined: { [weak self] in + self?.endSession() + } + ) + } +} + extension ChatViewModel { static func shouldSkipEnqueueingState(for chatType: ChatType) -> Bool { switch chatType { diff --git a/GliaWidgets/Sources/ViewModel/EngagementViewModel.swift b/GliaWidgets/Sources/ViewModel/EngagementViewModel.swift index d0b108d85..9cff5fd33 100644 --- a/GliaWidgets/Sources/ViewModel/EngagementViewModel.swift +++ b/GliaWidgets/Sources/ViewModel/EngagementViewModel.swift @@ -161,7 +161,6 @@ class EngagementViewModel: CommonEngagementModel { ) ) }) - default: break } @@ -191,19 +190,6 @@ class EngagementViewModel: CommonEngagementModel { ) } - func showAlert( - accepted: @escaping () -> Void, - declined: @escaping () -> Void - ) { - let conf = self.alertConfiguration.liveObservationConfirmation - engagementAction?( - .showLiveObservationConfirmation( - conf, - accepted: accepted, - declined: declined - )) - } - func showAlert(for error: Error) { showAlert(with: alertConfiguration.unexpectedError) } @@ -373,11 +359,6 @@ extension EngagementViewModel { accepted: () -> Void, declined: () -> Void ) - case showLiveObservationConfirmation( - ConfirmationAlertConfiguration, - accepted: () -> Void, - declined: () -> Void - ) case showEndButton case showEndScreenShareButton }