Create UserSessionsOverview screen.

This commit is contained in:
SBiOSoftWhare 2022-09-05 11:15:37 +02:00
parent 2f2bc831e0
commit 3e30a19dcd
5 changed files with 178 additions and 12 deletions

View file

@ -31,6 +31,9 @@ final class UserSessionsOverviewCoordinator: Coordinator, Presentable {
private let userSessionsOverviewHostingController: UIViewController private let userSessionsOverviewHostingController: UIViewController
private var userSessionsOverviewViewModel: UserSessionsOverviewViewModelProtocol private var userSessionsOverviewViewModel: UserSessionsOverviewViewModelProtocol
private var indicatorPresenter: UserIndicatorTypePresenterProtocol
private var loadingIndicator: UserIndicator?
// MARK: Public // MARK: Public
// Must be used only internally // Must be used only internally
@ -41,10 +44,15 @@ final class UserSessionsOverviewCoordinator: Coordinator, Presentable {
init(parameters: UserSessionsOverviewCoordinatorParameters) { init(parameters: UserSessionsOverviewCoordinatorParameters) {
self.parameters = parameters self.parameters = parameters
let viewModel = UserSessionsOverviewViewModel(userSessionsOverviewService: UserSessionsOverviewService()) let viewModel = UserSessionsOverviewViewModel(userSessionsOverviewService: UserSessionsOverviewService(mxSession: parameters.session))
let view = UserSessionsOverview(viewModel: viewModel.context) let view = UserSessionsOverview(viewModel: viewModel.context)
userSessionsOverviewViewModel = viewModel userSessionsOverviewViewModel = viewModel
userSessionsOverviewHostingController = VectorHostingController(rootView: view)
let hostingViewController = VectorHostingController(rootView: view)
userSessionsOverviewHostingController = hostingViewController
indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: hostingViewController)
} }
// MARK: - Public // MARK: - Public
@ -55,8 +63,22 @@ final class UserSessionsOverviewCoordinator: Coordinator, Presentable {
guard let self = self else { return } guard let self = self else { return }
MXLog.debug("[UserSessionsOverviewCoordinator] UserSessionsOverviewViewModel did complete with result: \(result).") MXLog.debug("[UserSessionsOverviewCoordinator] UserSessionsOverviewViewModel did complete with result: \(result).")
switch result { switch result {
case .done: case .cancel:
self.completion?() self.completion?()
case .loadData:
self.loadData()
case .showAllUnverifiedSessions:
self.showAllUnverifiedSessions()
case .showAllInactiveSessions:
self.showAllInactiveSessions()
case .verifyCurrentSession:
self.startVerifyCurrentSession()
case .showCurrentSessionDetails:
self.showCurrentSessionDetails()
case .showAllOtherSessions:
self.showAllOtherSessions()
case .showUserSessionDetails(let sessionId):
self.showUserSessionDetails(sessionId: sessionId)
} }
} }
} }
@ -64,4 +86,47 @@ final class UserSessionsOverviewCoordinator: Coordinator, Presentable {
func toPresentable() -> UIViewController { func toPresentable() -> UIViewController {
return self.userSessionsOverviewHostingController return self.userSessionsOverviewHostingController
} }
// MARK: - Private
/// Show an activity indicator whilst loading.
/// - Parameters:
/// - label: The label to show on the indicator.
/// - isInteractionBlocking: Whether the indicator should block any user interaction.
private func startLoading(label: String = VectorL10n.loading, isInteractionBlocking: Bool = true) {
loadingIndicator = indicatorPresenter.present(.loading(label: label, isInteractionBlocking: isInteractionBlocking))
}
/// Hide the currently displayed activity indicator.
private func stopLoading() {
loadingIndicator = nil
}
private func loadData() {
// TODO
}
private func showAllUnverifiedSessions() {
// TODO
}
private func showAllInactiveSessions() {
// TODO
}
private func startVerifyCurrentSession() {
// TODO
}
private func showCurrentSessionDetails() {
// TODO
}
private func showUserSessionDetails(sessionId: String) {
// TODO
}
private func showAllOtherSessions() {
// TODO
}
} }

View file

@ -21,18 +21,37 @@ import Foundation
// MARK: View model // MARK: View model
enum UserSessionsOverviewViewModelResult { enum UserSessionsOverviewViewModelResult {
case done case cancel
case loadData
case showAllUnverifiedSessions
case showAllInactiveSessions
case verifyCurrentSession
case showCurrentSessionDetails
case showAllOtherSessions
case showUserSessionDetails(_ sessionId: String)
} }
// MARK: View // MARK: View
struct UserSessionsOverviewViewState: BindableState { struct UserSessionsOverviewViewState: BindableState {
var unverifiedSessionsViewData: [UserSessionListItemViewData]
var inactiveSessionsViewData: [UserSessionListItemViewData]
var currentSessionViewData: UserSessionListItemViewData?
var otherSessionsViewData: [UserSessionListItemViewData]
var showLoadingIndicator: Bool = false
} }
enum UserSessionsOverviewViewAction { enum UserSessionsOverviewViewAction {
case viewAppeared
case verifyCurrentSession case verifyCurrentSession
case viewCurrentSessionDetails case viewCurrentSessionDetails
case viewAllUnverifiedSessions case viewAllUnverifiedSessions
case viewAllInactiveSessions case viewAllInactiveSessions
case viewAllOtherSessions case viewAllOtherSessions
case tapUserSession(_ sessionId: String)
} }

View file

@ -37,25 +37,80 @@ class UserSessionsOverviewViewModel: UserSessionsOverviewViewModelType, UserSess
init(userSessionsOverviewService: UserSessionsOverviewServiceProtocol) { init(userSessionsOverviewService: UserSessionsOverviewServiceProtocol) {
self.userSessionsOverviewService = userSessionsOverviewService self.userSessionsOverviewService = userSessionsOverviewService
let viewState = UserSessionsOverviewViewState() let viewState = UserSessionsOverviewViewState(unverifiedSessionsViewData: [], inactiveSessionsViewData: [], currentSessionViewData: nil, otherSessionsViewData: [])
super.init(initialViewState: viewState) super.init(initialViewState: viewState)
self.updateViewState(with: userSessionsOverviewService.lastOverviewData)
} }
// MARK: - Public // MARK: - Public
override func process(viewAction: UserSessionsOverviewViewAction) { override func process(viewAction: UserSessionsOverviewViewAction) {
switch viewAction { switch viewAction {
case .viewAppeared:
self.loadData()
case .verifyCurrentSession: case .verifyCurrentSession:
break self.completion?(.verifyCurrentSession)
case .viewCurrentSessionDetails: case .viewCurrentSessionDetails:
break self.completion?(.showCurrentSessionDetails)
case .viewAllUnverifiedSessions: case .viewAllUnverifiedSessions:
break self.completion?(.showAllUnverifiedSessions)
case .viewAllInactiveSessions: case .viewAllInactiveSessions:
break self.completion?(.showAllInactiveSessions)
case .viewAllOtherSessions: case .viewAllOtherSessions:
break self.completion?(.showAllOtherSessions)
case .tapUserSession(let sessionId):
self.completion?(.showUserSessionDetails(sessionId))
}
}
// MARK: - Private
private func updateViewState(with userSessionsViewData: UserSessionsOverviewData) {
let unverifiedSessionsViewData = self.userSessionListItemViewDataList(from: userSessionsViewData.unverifiedSessionsInfo)
let inactiveSessionsViewData = self.userSessionListItemViewDataList(from: userSessionsViewData.inactiveSessionsInfo)
var currentSessionViewData: UserSessionListItemViewData?
let otherSessionsViewData = self.userSessionListItemViewDataList(from: userSessionsViewData.otherSessionsInfo)
if let currentSessionInfo = userSessionsViewData.currentSessionInfo {
currentSessionViewData = UserSessionListItemViewData(userSessionInfo: currentSessionInfo)
}
self.state.unverifiedSessionsViewData = unverifiedSessionsViewData
self.state.inactiveSessionsViewData = inactiveSessionsViewData
self.state.currentSessionViewData = currentSessionViewData
self.state.otherSessionsViewData = otherSessionsViewData
}
private func userSessionListItemViewDataList(from userSessionInfoList: [UserSessionInfo]) -> [UserSessionListItemViewData] {
return userSessionInfoList.map {
return UserSessionListItemViewData(userSessionInfo: $0)
}
}
private func loadData() {
self.state.showLoadingIndicator = true
self.userSessionsOverviewService.fetchUserSessionsOverviewData { [weak self] result in
guard let self = self else {
return
}
self.state.showLoadingIndicator = false
switch result {
case .success(let overViewData):
self.updateViewState(with: overViewData)
case .failure(let error):
// TODO
break
}
} }
} }
} }

View file

@ -51,7 +51,7 @@ struct UserSessionListItem: View {
lastActivityDateString = self.lastActivityDateString(from: lastActivityDate) lastActivityDateString = self.lastActivityDateString(from: lastActivityDate)
} }
if let lastActivityDateString = lastActivityDateString { if let lastActivityDateString = lastActivityDateString, lastActivityDateString.isEmpty == false {
sessionDetailsString = VectorL10n.userSessionItemDetails(sessionStatusText, lastActivityDateString) sessionDetailsString = VectorL10n.userSessionItemDetails(sessionStatusText, lastActivityDateString)
} else { } else {
sessionDetailsString = sessionStatusText sessionDetailsString = sessionStatusText

View file

@ -29,11 +29,38 @@ struct UserSessionsOverview: View {
@ObservedObject var viewModel: UserSessionsOverviewViewModel.Context @ObservedObject var viewModel: UserSessionsOverviewViewModel.Context
var body: some View { var body: some View {
VStack { ScrollView {
// Security recommendations section
if viewModel.viewState.unverifiedSessionsViewData.isEmpty == false || viewModel.viewState.inactiveSessionsViewData.isEmpty == false {
// TODO:
}
// Current session section
if let currentSessionViewData = viewModel.viewState.currentSessionViewData {
// TODO:
}
// Other sessions section
if viewModel.viewState.otherSessionsViewData.isEmpty == false {
VStack(spacing: 15) {
ForEach(viewModel.viewState.otherSessionsViewData) { viewData in
UserSessionListItem(viewData: viewData, onBackgroundTap: { sessionId in
viewModel.send(viewAction: .tapUserSession(sessionId))
})
}
}
}
} }
.background(theme.colors.background) .background(theme.colors.background)
.frame(maxHeight: .infinity) .frame(maxHeight: .infinity)
.navigationTitle(VectorL10n.userSessionsOverviewTitle) .navigationTitle(VectorL10n.userSessionsOverviewTitle)
.activityIndicator(show: viewModel.viewState.showLoadingIndicator)
.onAppear() {
viewModel.send(viewAction: .viewAppeared)
}
} }
} }