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

[Feature][Calling][CSS] Call history #610

Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6afebc3
Css call history
pavelprystinka Jan 24, 2023
3ee664c
rename callIds to callIdList
pavelprystinka Jan 24, 2023
ae46eb7
Store callid in sqlite
pavelprystinka Jan 25, 2023
6b16f2c
rename list to plurals
pavelprystinka Jan 26, 2023
f42881a
clear call history records
pavelprystinka Jan 26, 2023
d3dda8e
update data formatter use
pavelprystinka Jan 27, 2023
41d3a1e
Merge branch 'develop' into feature/css_call_history_v1
pavelprystinka Jan 27, 2023
70c93f2
Clean old call history records async
pavelprystinka Jan 28, 2023
6f84990
Show call history in demo app
pavelprystinka Jan 30, 2023
5f08fb6
Store call history in UserDefaults
pavelprystinka Feb 7, 2023
be35c85
remove unused import
pavelprystinka Feb 7, 2023
41c703a
Inject userDefaults to history repository
pavelprystinka Feb 8, 2023
3997d0f
Fix threshold condition
pavelprystinka Feb 8, 2023
6494cd0
CallHistoryRepository to inset async and serial
pavelprystinka Feb 19, 2023
0aa3973
Make CallHistoryRepository sync, but CallHistoryService to save async
pavelprystinka Feb 23, 2023
4b7c1f8
let view model instead of var
pavelprystinka Feb 23, 2023
2efd9e1
Move callHistoryDispatchQueue to repository
pavelprystinka Feb 28, 2023
812d79b
Code format
pavelprystinka Feb 28, 2023
4a3cbd5
Use XCTestExpectation in test for call history
pavelprystinka Mar 1, 2023
84b9590
Remove not needed thread sleep
pavelprystinka Mar 1, 2023
e682109
Filter out old call history records on getAll as well.
pavelprystinka Mar 1, 2023
92cacb0
remove CallHistoryRepositoryProtocol
pavelprystinka Mar 1, 2023
5502fdb
Update cocopods SDK version
pavelprystinka Mar 1, 2023
b7f5e8c
Update pool for nightly pipeline
pavelprystinka Mar 2, 2023
1201720
Revert SDK version update
pavelprystinka Mar 7, 2023
0389a4f
Address feedback
pavelprystinka Mar 7, 2023
89f36c3
Address feedback
pavelprystinka Mar 7, 2023
f6ca66c
Fix compile error
pavelprystinka Mar 7, 2023
257c9f1
Inject userDefault to CallHistoryRepositoryMocking
pavelprystinka Mar 7, 2023
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 @@ -15,6 +15,9 @@ struct CallingDemoView: View {
@State var isSettingsDisplayed: Bool = false
@State var isStartExperienceLoading: Bool = false
@State var errorMessage: String = ""
@State var isShowingCallHistory: Bool = false
@State var callHistoryTitle: String = ""
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
@State var callHistoryMessage: String = ""
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
@ObservedObject var envConfigSubject: EnvConfigSubject

let verticalPadding: CGFloat = 5
Expand All @@ -30,6 +33,7 @@ struct CallingDemoView: View {
displayNameTextField
meetingSelector
settingButton
showCallHistoryButton
startExperienceButton
Spacer()
}
Expand All @@ -43,6 +47,15 @@ struct CallingDemoView: View {
isErrorDisplayed = false
}))
}
.alert(isPresented: $isShowingCallHistory) {
Alert(
title: Text(callHistoryTitle),
message: Text(callHistoryMessage),
dismissButton:
.default(Text("Dismiss"), action: {
isShowingCallHistory = false
}))
}
.sheet(isPresented: $isSettingsDisplayed) {
SettingsView(envConfigSubject: envConfigSubject)
}
Expand Down Expand Up @@ -130,6 +143,26 @@ struct CallingDemoView: View {
.accessibility(identifier: AccessibilityId.startExperienceAccessibilityID.rawValue)
}

var showCallHistoryButton: some View {
Button("Show call history") {
let callComposite = CallComposite()
emlynmac marked this conversation as resolved.
Show resolved Hide resolved
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
let debugInfo = callComposite.debugInfo
let callHistory = debugInfo.callHistoryRecords
callHistoryTitle = "Total calls: \(callHistory.count)"
callHistoryMessage = "Last Call: none"
if let lastHistoryRecord = callHistory.last {
var formatter = DateFormatter()
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
callHistoryMessage = "Last Call: \(formatter.string(from: lastHistoryRecord.callStartedOn))"
lastHistoryRecord.callIds.forEach { callId in
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
callHistoryMessage += "\nCallId: \(callId)"
}
}
isShowingCallHistory = true
}
.buttonStyle(DemoButtonStyle())
}

var isStartExperienceDisabled: Bool {
let acsToken = envConfigSubject.useExpiredToken ? envConfigSubject.expiredAcsToken : envConfigSubject.acsToken
if (envConfigSubject.selectedAcsTokenType == .token && acsToken.isEmpty)
Expand Down Expand Up @@ -280,9 +313,10 @@ extension CallingDemoView {
}

private func onError(_ error: CallCompositeError, callComposite: CallComposite) {
let callId = callComposite.debugInfo.callHistoryRecords.last?.callIds.last ?? "Unknown"
print("::::CallingDemoView::getEventsHandler::onError \(error)")
print("::::CallingDemoView error.code \(error.code)")
print("::::CallingDemoView debug info \(callComposite.debugInfo.currentOrLastCallId ?? "Unknown")")
print("::::CallingDemoView debug info \(callId)")
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
showError(for: error.code)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class CallingDemoViewController: UIViewController {
private var groupCallTextField: UITextField!
private var teamsMeetingTextField: UITextField!
private var settingsButton: UIButton!
private var showCallHistoryButton: UIButton!
private var startExperienceButton: UIButton!
private var acsTokenTypeSegmentedControl: UISegmentedControl!
private var meetingTypeSegmentedControl: UISegmentedControl!
Expand Down Expand Up @@ -158,7 +159,8 @@ class CallingDemoViewController: UIViewController {
private func onError(_ error: CallCompositeError, callComposite: CallComposite) {
print("::::UIKitDemoView::getEventsHandler::onError \(error)")
print("::::UIKitDemoView error.code \(error.code)")
print("::::SwiftUIDemoView debug info \(callComposite.debugInfo.currentOrLastCallId ?? "Unknown")")
let callId = callComposite.debugInfo.callHistoryRecords.last?.callIds.last ?? "Unknown"
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
print("::::SwiftUIDemoView debug info \(callId)")
}

private func onRemoteParticipantJoined(to callComposite: CallComposite, identifiers: [CommunicationIdentifier]) {
Expand Down Expand Up @@ -354,6 +356,28 @@ class CallingDemoViewController: UIViewController {
present(settingsViewHostingController, animated: true, completion: nil)
}

@objc func onShowHistoryBtnPressed() {
let callComposite = CallComposite()
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
let debugInfo = callComposite.debugInfo
let callHistory = debugInfo.callHistoryRecords
var callHistoryTitle = "Total calls: \(callHistory.count)"
var callHistoryMessage = "Last Call: none"
if let lastHistoryRecord = callHistory.last {
var formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
callHistoryMessage = "Last Call: \(formatter.string(from: lastHistoryRecord.callStartedOn))"
lastHistoryRecord.callIds.forEach { callId in
callHistoryMessage += "\nCallId: \(callId)"
}
}

let errorAlert = UIAlertController(title: callHistoryTitle, message: callHistoryMessage, preferredStyle: .alert)
errorAlert.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler: nil))
present(errorAlert,
animated: true,
completion: nil)
}

@objc func onStartExperienceBtnPressed() {
startExperienceButton.isEnabled = false
startExperienceButton.backgroundColor = .systemGray3
Expand Down Expand Up @@ -494,6 +518,18 @@ class CallingDemoViewController: UIViewController {
right: LayoutConstants.buttonHorizontalInset)
settingsButton.accessibilityIdentifier = AccessibilityId.settingsButtonAccessibilityID.rawValue

showCallHistoryButton = UIButton()
showCallHistoryButton.setTitle("Show call history", for: .normal)
showCallHistoryButton.backgroundColor = .systemBlue
// showCallHistoryButton.setTitleColor(UIColor.white, for: .normal)
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
// showCallHistoryButton.setTitleColor(UIColor.systemGray6, for: .disabled)
showCallHistoryButton.contentEdgeInsets = UIEdgeInsets.init(top: LayoutConstants.buttonVerticalInset,
left: LayoutConstants.buttonHorizontalInset,
bottom: LayoutConstants.buttonVerticalInset,
right: LayoutConstants.buttonHorizontalInset)
showCallHistoryButton.layer.cornerRadius = 8
pavelprystinka marked this conversation as resolved.
Show resolved Hide resolved
showCallHistoryButton.addTarget(self, action: #selector(onShowHistoryBtnPressed), for: .touchUpInside)

startExperienceButton = UIButton()
startExperienceButton.backgroundColor = .systemBlue
startExperienceButton.setTitleColor(UIColor.white, for: .normal)
Expand Down Expand Up @@ -527,6 +563,22 @@ class CallingDemoViewController: UIViewController {
settingsButtonHStack.distribution = .fill
settingsButtonHStack.translatesAutoresizingMaskIntoConstraints = false

let showHistoryButtonHSpacer1 = UIView()
showHistoryButtonHSpacer1.translatesAutoresizingMaskIntoConstraints = false
showHistoryButtonHSpacer1.setContentHuggingPriority(.defaultLow, for: .horizontal)

let showHistoryButtonHSpacer2 = UIView()
showHistoryButtonHSpacer2.translatesAutoresizingMaskIntoConstraints = false
showHistoryButtonHSpacer2.setContentHuggingPriority(.defaultLow, for: .horizontal)

let showHistoryButtonHStack = UIStackView(arrangedSubviews: [showHistoryButtonHSpacer1,
showCallHistoryButton,
showHistoryButtonHSpacer2])
showHistoryButtonHStack.axis = .horizontal
showHistoryButtonHStack.alignment = .fill
showHistoryButtonHStack.distribution = .fill
showHistoryButtonHStack.translatesAutoresizingMaskIntoConstraints = false

let startButtonHSpacer1 = UIView()
emlynmac marked this conversation as resolved.
Show resolved Hide resolved
startButtonHSpacer1.translatesAutoresizingMaskIntoConstraints = false
startButtonHSpacer1.setContentHuggingPriority(.defaultLow, for: .horizontal)
Expand Down Expand Up @@ -555,6 +607,7 @@ class CallingDemoViewController: UIViewController {
groupCallTextField,
teamsMeetingTextField,
settingsButtonHStack,
showHistoryButtonHStack,
startButtonHStack])
stackView.spacing = LayoutConstants.stackViewSpacingPortrait
stackView.axis = .vertical
Expand Down Expand Up @@ -586,6 +639,7 @@ class CallingDemoViewController: UIViewController {
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

settingButtonHSpacer2.widthAnchor.constraint(equalTo: settingButtonHSpacer1.widthAnchor).isActive = true
showHistoryButtonHSpacer2.widthAnchor.constraint(equalTo: showHistoryButtonHSpacer1.widthAnchor).isActive = true
startButtonHSpacer2.widthAnchor.constraint(equalTo: startButtonHSpacer1.widthAnchor).isActive = true

updateAcsTokenTypeFields()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
05C1C828297F4830006EDD0D /* CallHistoryRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05C1C827297F4830006EDD0D /* CallHistoryRecord.swift */; };
05C1C82A297F516D006EDD0D /* CallHistoryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05C1C829297F516D006EDD0D /* CallHistoryService.swift */; };
05C1C831297F5680006EDD0D /* CallHistoryRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05C1C830297F5680006EDD0D /* CallHistoryRepository.swift */; };
1B315123284EA8630043231B /* OnHoldOverlayViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B315122284EA8620043231B /* OnHoldOverlayViewModelTests.swift */; };
1B3A8EED285129CC002ED55A /* AudioSessionReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B3A8EEC285129CC002ED55A /* AudioSessionReducerTests.swift */; };
1B5F169D27FBB56100C35126 /* CompositeLeaveCallConfirmationList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B5F169C27FBB56100C35126 /* CompositeLeaveCallConfirmationList.swift */; };
Expand Down Expand Up @@ -268,6 +271,9 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
05C1C827297F4830006EDD0D /* CallHistoryRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallHistoryRecord.swift; sourceTree = "<group>"; };
05C1C829297F516D006EDD0D /* CallHistoryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallHistoryService.swift; sourceTree = "<group>"; };
05C1C830297F5680006EDD0D /* CallHistoryRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallHistoryRepository.swift; sourceTree = "<group>"; };
1B315122284EA8620043231B /* OnHoldOverlayViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnHoldOverlayViewModelTests.swift; sourceTree = "<group>"; };
1B3A8EEC285129CC002ED55A /* AudioSessionReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSessionReducerTests.swift; sourceTree = "<group>"; };
1B5F169C27FBB56100C35126 /* CompositeLeaveCallConfirmationList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositeLeaveCallConfirmationList.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -586,6 +592,14 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
05C1C82F297F5680006EDD0D /* Data */ = {
isa = PBXGroup;
children = (
05C1C830297F5680006EDD0D /* CallHistoryRepository.swift */,
);
path = Data;
sourceTree = "<group>";
};
1F03D3CC2638A6860055C456 /* Calling */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -867,6 +881,7 @@
75C1EE0D28E4BEF600FF78B1 /* AzureCommunicationCalling */,
1F6470A82639DCFA0008B9E9 /* CallingService.swift */,
1F48CF1E283447F400A44D58 /* CallingSDKWrapperProtocol.swift */,
05C1C829297F516D006EDD0D /* CallHistoryService.swift */,
);
path = Calling;
sourceTree = "<group>";
Expand Down Expand Up @@ -1156,6 +1171,7 @@
FA378671284152CA005B6288 /* SetParticipantViewDataError.swift */,
759BDEFC28FF0A81002B6853 /* SupportedLocale.swift */,
1F94DADB2673E87700691D1E /* ThemeOptions.swift */,
05C1C827297F4830006EDD0D /* CallHistoryRecord.swift */,
);
path = CallCompositeOptions;
sourceTree = "<group>";
Expand Down Expand Up @@ -1190,6 +1206,7 @@
1F2BED62262F859D00D98266 /* Redux */,
1FBCB6AD2649DA4100F57EEA /* Model */,
1F2BED862631EB5100D98266 /* Service */,
05C1C82F297F5680006EDD0D /* Data */,
1F6470AA2639FE560008B9E9 /* Utilities */,
505460EB27BAC6E900040467 /* Localization */,
A8659FC92602A22000C807DE /* Info.plist */,
Expand Down Expand Up @@ -1728,6 +1745,7 @@
985DF376269E07E700CFDA55 /* PrimaryButton.swift in Sources */,
1FBCB6AF2649DC7900F57EEA /* ParticipantInfoModel.swift in Sources */,
988799932746CDEC00AA3759 /* LockPhoneOrientation.swift in Sources */,
05C1C831297F5680006EDD0D /* CallHistoryRepository.swift in Sources */,
7529F5DE29661888002F7CE1 /* BaseLocalizationProvider.swift in Sources */,
1F47F78726E9E18E000AE4B7 /* DrawerContainerViewController.swift in Sources */,
1FBCB6AA2645AC2F00F57EEA /* VideoRenderView.swift in Sources */,
Expand Down Expand Up @@ -1761,6 +1779,7 @@
1F2BEDBB26328EBA00D98266 /* PermissionReducer.swift in Sources */,
1FBCB6B9264BA90000F57EEA /* ParticipantGridCellVideoView.swift in Sources */,
1F48B401274879F000B6E5F9 /* DiagnosticConfig.swift in Sources */,
05C1C828297F4830006EDD0D /* CallHistoryRecord.swift in Sources */,
9823AB4926A13F220006266D /* PreviewAreaView.swift in Sources */,
50FA46212698EA7A001844AC /* IconWithLabelButtonViewModel.swift in Sources */,
50E2D69926F4FA05006B0EF4 /* ParticipantsListCellViewModel.swift in Sources */,
Expand Down Expand Up @@ -1800,6 +1819,7 @@
1F13D36C26A8B83300E31666 /* RemoteParticipantsState.swift in Sources */,
1FFEA01426A68D0D00E90816 /* FontExtension.swift in Sources */,
1FBCB666264519DA00F57EEA /* VideoViewManager.swift in Sources */,
05C1C82A297F516D006EDD0D /* CallHistoryService.swift in Sources */,
50FA461026829B7A001844AC /* ControlBarViewModel.swift in Sources */,
1F2BED882631EB7900D98266 /* PermissionsManager.swift in Sources */,
1FA23F9E265DA21400B4A080 /* VideoStreamInfoModel.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ public class CallComposite {
private var avatarViewManager: AvatarViewManagerProtocol?
private var customCallingSdkWrapper: CallingSDKWrapperProtocol?
private var debugInfoManager: DebugInfoManagerProtocol?
private var callHistoryService: CallHistoryServiceProtocol?
private lazy var callHistoryRepository: CallHistoryRepositoryProtocol = CallHistoryRepository(
userDefaults: UserDefaults.standard)

/// Get debug information for the Call Composite.
public var debugInfo: DebugInfo {
guard let debugInfoManager = debugInfoManager else {
return DebugInfo()
}

return debugInfoManager.getDebugInfo()
let localDebugInfoManager = debugInfoManager ?? createDebugInfoManager()
return localDebugInfoManager.getDebugInfo()
}

/// Create an instance of CallComposite with options.
Expand Down Expand Up @@ -161,9 +161,9 @@ public class CallComposite {
callCompositeEventsHandler: callCompositeEventsHandler,
avatarViewManager: avatarViewManager
)

let debugInfoManager = DebugInfoManager(store: store)
let debugInfoManager = createDebugInfoManager()
self.debugInfoManager = debugInfoManager
self.callHistoryService = CallHistoryService(store: store, callHistoryRepository: self.callHistoryRepository)

return CompositeViewFactory(
logger: logger,
Expand All @@ -181,6 +181,10 @@ public class CallComposite {
)
}

private func createDebugInfoManager() -> DebugInfoManagerProtocol {
return DebugInfoManager(callHistoryRepository: self.callHistoryRepository)
}

private func cleanUpManagers() {
self.errorManager = nil
self.lifeCycleManager = nil
Expand All @@ -189,6 +193,7 @@ public class CallComposite {
self.avatarViewManager = nil
self.remoteParticipantsManager = nil
self.debugInfoManager = nil
self.callHistoryService = nil
}

private func makeToolkitHostingController(router: NavigationRouter,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//

import Foundation

/// Call history.
public struct CallHistoryRecord {

/// Local date call started on.
emlynmac marked this conversation as resolved.
Show resolved Hide resolved
public let callStartedOn: Date

/// Call Ids associated with particular call started on callStartedOn date.
public let callIds: [String]

init(callStartedOn: Date, callIds: [String]) {
self.callStartedOn = callStartedOn
self.callIds = callIds
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import Foundation

/// A Call Composite debug information.
public struct DebugInfo {
/// The current or last known call id for the current CallComposite object.
/// `Nil` is returned if a call hasn't started for CallComposite.
public let currentOrLastCallId: String?
/// The history of calls up to 30 days.
public let callHistoryRecords: [CallHistoryRecord]
emlynmac marked this conversation as resolved.
Show resolved Hide resolved

init(lastCallId: String? = nil) {
self.currentOrLastCallId = lastCallId
/// Call history.
init(callHistoryRecords: [CallHistoryRecord]) {
self.callHistoryRecords = callHistoryRecords
}
}
Loading