mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-29 07:42:40 +00:00
Cleaned up various things, mostly removed unnecessary self
s, comments and bad formatting
This commit is contained in:
parent
492b7410b9
commit
148bc1624a
39 changed files with 141 additions and 350 deletions
|
@ -17,7 +17,6 @@
|
|||
import SwiftUI
|
||||
|
||||
struct SeparatorLine: View {
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
var body: some View {
|
||||
|
|
|
@ -19,7 +19,6 @@ import DesignKit
|
|||
|
||||
/// Avatar view for device
|
||||
struct DeviceAvatarView: View {
|
||||
|
||||
@Environment(\.theme) var theme: ThemeSwiftUI
|
||||
|
||||
var viewData: DeviceAvatarViewData
|
||||
|
@ -55,7 +54,6 @@ struct DeviceAvatarView: View {
|
|||
}
|
||||
|
||||
struct DeviceAvatarViewListPreview: View {
|
||||
|
||||
var viewDataList: [DeviceAvatarViewData] {
|
||||
return [
|
||||
DeviceAvatarViewData(deviceType: .desktop, isVerified: true),
|
||||
|
@ -78,7 +76,6 @@ struct DeviceAvatarViewListPreview: View {
|
|||
}
|
||||
|
||||
struct DeviceAvatarView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
DeviceAvatarViewListPreview().theme(.light).preferredColorScheme(.light)
|
||||
|
|
|
@ -19,8 +19,6 @@ import SwiftUI
|
|||
|
||||
/// View data for DeviceAvatarView
|
||||
struct DeviceAvatarViewData {
|
||||
|
||||
let deviceType: DeviceType
|
||||
|
||||
let isVerified: Bool?
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
extension DeviceType {
|
||||
|
||||
var image: Image {
|
||||
|
||||
let image: Image
|
||||
|
||||
switch self {
|
||||
case .desktop:
|
||||
image = Image(Asset.Images.deviceTypeDesktop.name)
|
||||
case .web:
|
||||
image = Image(Asset.Images.deviceTypeWeb.name)
|
||||
case .mobile:
|
||||
image = Image(Asset.Images.deviceTypeMobile.name)
|
||||
case .unknown:
|
||||
image = Image(Asset.Images.deviceTypeUnknown.name)
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
var name: String {
|
||||
let name: String
|
||||
|
||||
let appName = AppInfo.current.displayName
|
||||
|
||||
switch self {
|
||||
case .desktop:
|
||||
name = VectorL10n.deviceNameDesktop(appName)
|
||||
case .web:
|
||||
name = VectorL10n.deviceNameWeb(appName)
|
||||
case .mobile:
|
||||
name = VectorL10n.deviceNameMobile(appName)
|
||||
case .unknown:
|
||||
name = VectorL10n.deviceNameUnknown
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
}
|
|
@ -23,4 +23,32 @@ enum DeviceType {
|
|||
case web
|
||||
case mobile
|
||||
case unknown
|
||||
|
||||
var image: Image {
|
||||
switch self {
|
||||
case .desktop:
|
||||
return Image(Asset.Images.deviceTypeDesktop.name)
|
||||
case .web:
|
||||
return Image(Asset.Images.deviceTypeWeb.name)
|
||||
case .mobile:
|
||||
return Image(Asset.Images.deviceTypeMobile.name)
|
||||
case .unknown:
|
||||
return Image(Asset.Images.deviceTypeUnknown.name)
|
||||
}
|
||||
}
|
||||
|
||||
var name: String {
|
||||
let appName = AppInfo.current.displayName
|
||||
|
||||
switch self {
|
||||
case .desktop:
|
||||
return VectorL10n.deviceNameDesktop(appName)
|
||||
case .web:
|
||||
return VectorL10n.deviceNameWeb(appName)
|
||||
case .mobile:
|
||||
return VectorL10n.deviceNameMobile(appName)
|
||||
case .unknown:
|
||||
return VectorL10n.deviceNameUnknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import SwiftUI
|
|||
import DesignKit
|
||||
|
||||
struct UserSessionCardView: View {
|
||||
|
||||
@Environment(\.theme) var theme: ThemeSwiftUI
|
||||
|
||||
var viewData: UserSessionCardViewData
|
||||
|
@ -82,7 +81,7 @@ struct UserSessionCardView: View {
|
|||
.multilineTextAlignment(.center)
|
||||
}
|
||||
|
||||
if self.showExtraInformations {
|
||||
if showExtraInformations {
|
||||
VStack(spacing: 2) {
|
||||
if let lastActivityDateString = viewData.lastActivityDateString, lastActivityDateString.isEmpty == false {
|
||||
Text(lastActivityDateString)
|
||||
|
@ -126,13 +125,12 @@ struct UserSessionCardView: View {
|
|||
.padding(24)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(theme.colors.background)
|
||||
.clipShape(self.backgroundShape)
|
||||
.shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1.0, shape: self.backgroundShape)
|
||||
.clipShape(backgroundShape)
|
||||
.shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1.0, shape: backgroundShape)
|
||||
}
|
||||
}
|
||||
|
||||
struct UserSessionCardViewPreview: View {
|
||||
|
||||
@Environment(\.theme) var theme: ThemeSwiftUI
|
||||
|
||||
let viewData: UserSessionCardViewData
|
||||
|
@ -145,7 +143,7 @@ struct UserSessionCardViewPreview: View {
|
|||
|
||||
var body: some View {
|
||||
VStack {
|
||||
UserSessionCardView(viewData: self.viewData)
|
||||
UserSessionCardView(viewData: viewData)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(theme.colors.system)
|
||||
|
@ -154,7 +152,6 @@ struct UserSessionCardViewPreview: View {
|
|||
}
|
||||
|
||||
struct UserSessionCardView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
UserSessionCardViewPreview(isCurrentSessionInfo: true).theme(.light).preferredColorScheme(.light)
|
||||
|
|
|
@ -18,13 +18,8 @@ import Foundation
|
|||
|
||||
/// View data for UserSessionCardView
|
||||
struct UserSessionCardViewData {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private static let userSessionNameFormatter = UserSessionNameFormatter()
|
||||
private static let lastActivityDateFormatter = UserSessionLastActivityFormatter()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var id: String {
|
||||
return sessionId
|
||||
|
@ -45,8 +40,6 @@ struct UserSessionCardViewData {
|
|||
/// Indicate if the current user session is shown and to adpat the layout
|
||||
let isCurrentSessionDisplayMode: Bool
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(sessionId: String,
|
||||
sessionDisplayName: String?,
|
||||
deviceType: DeviceType,
|
||||
|
@ -55,7 +48,7 @@ struct UserSessionCardViewData {
|
|||
lastSeenIP: String?,
|
||||
isCurrentSessionDisplayMode: Bool = false) {
|
||||
self.sessionId = sessionId
|
||||
self.sessionName = Self.userSessionNameFormatter.sessionName(deviceType: deviceType, sessionDisplayName: sessionDisplayName)
|
||||
sessionName = Self.userSessionNameFormatter.sessionName(deviceType: deviceType, sessionDisplayName: sessionDisplayName)
|
||||
self.isVerified = isVerified
|
||||
|
||||
var lastActivityDateString: String?
|
||||
|
@ -65,16 +58,21 @@ struct UserSessionCardViewData {
|
|||
}
|
||||
|
||||
self.lastActivityDateString = lastActivityDateString
|
||||
self.lastSeenIPInfo = lastSeenIP
|
||||
self.deviceAvatarViewData = DeviceAvatarViewData(deviceType: deviceType, isVerified: nil)
|
||||
lastSeenIPInfo = lastSeenIP
|
||||
deviceAvatarViewData = DeviceAvatarViewData(deviceType: deviceType, isVerified: nil)
|
||||
|
||||
self.isCurrentSessionDisplayMode = isCurrentSessionDisplayMode
|
||||
}
|
||||
}
|
||||
|
||||
extension UserSessionCardViewData {
|
||||
|
||||
init(userSessionInfo: UserSessionInfo, isCurrentSessionDisplayMode: Bool = false) {
|
||||
self.init(sessionId: userSessionInfo.sessionId, sessionDisplayName: userSessionInfo.sessionName, deviceType: userSessionInfo.deviceType, isVerified: userSessionInfo.isVerified, lastActivityTimestamp: userSessionInfo.lastSeenTimestamp, lastSeenIP: userSessionInfo.lastSeenIP, isCurrentSessionDisplayMode: isCurrentSessionDisplayMode)
|
||||
self.init(sessionId: userSessionInfo.sessionId,
|
||||
sessionDisplayName: userSessionInfo.sessionName,
|
||||
deviceType: userSessionInfo.deviceType,
|
||||
isVerified: userSessionInfo.isVerified,
|
||||
lastActivityTimestamp: userSessionInfo.lastSeenTimestamp,
|
||||
lastSeenIP: userSessionInfo.lastSeenIP,
|
||||
isCurrentSessionDisplayMode: isCurrentSessionDisplayMode)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,6 @@ struct UserSessionDetailsCoordinatorParameters {
|
|||
}
|
||||
|
||||
final class UserSessionDetailsCoordinator: Coordinator, Presentable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: UserSessionDetailsCoordinatorParameters
|
||||
private let userSessionDetailsHostingController: UIViewController
|
||||
private var userSessionDetailsViewModel: UserSessionDetailsViewModelProtocol
|
||||
|
@ -58,13 +53,16 @@ final class UserSessionDetailsCoordinator: Coordinator, Presentable {
|
|||
func start() {
|
||||
MXLog.debug("[UserSessionDetailsCoordinator] did start.")
|
||||
userSessionDetailsViewModel.completion = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
MXLog.debug("[UserSessionDetailsCoordinator] UserSessionDetailsViewModel did complete with result: \(result).")
|
||||
self.completion?(result)
|
||||
}
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userSessionDetailsHostingController
|
||||
return userSessionDetailsHostingController
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import XCTest
|
|||
import RiotSwiftUI
|
||||
|
||||
class UserSessionDetailsUITests: MockScreenTestCase {
|
||||
|
||||
func test_longPressDetailsCell_CopiesValueToClipboard() throws {
|
||||
app.goToScreenWithIdentifier(MockUserSessionDetailsScreenState.allSections.title)
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import XCTest
|
|||
@testable import RiotSwiftUI
|
||||
|
||||
class UserSessionDetailsViewModelTests: XCTestCase {
|
||||
|
||||
func test_whenSessionNameAndLastSeenIPNil_viewStateCorrect() {
|
||||
let userSessionInfo = createUserSessionInfo(sessionId: "session",
|
||||
sessionName: nil,
|
||||
|
|
|
@ -29,7 +29,6 @@ enum UserSessionDetailsViewAction {
|
|||
}
|
||||
|
||||
struct UserSessionDetailsViewState: BindableState, Equatable {
|
||||
|
||||
let sections: [UserSessionDetailsSectionViewData]
|
||||
}
|
||||
|
||||
|
@ -47,7 +46,6 @@ struct UserSessionDetailsSectionItemViewData: Identifiable {
|
|||
}
|
||||
|
||||
extension UserSessionDetailsSectionViewData: Equatable {
|
||||
|
||||
static func == (lhs: UserSessionDetailsSectionViewData, rhs: UserSessionDetailsSectionViewData) -> Bool {
|
||||
lhs.header == rhs.header &&
|
||||
lhs.footer == rhs.footer &&
|
||||
|
@ -56,7 +54,6 @@ extension UserSessionDetailsSectionViewData: Equatable {
|
|||
}
|
||||
|
||||
extension UserSessionDetailsSectionItemViewData: Equatable {
|
||||
|
||||
static func == (lhs: UserSessionDetailsSectionItemViewData, rhs: UserSessionDetailsSectionItemViewData) -> Bool {
|
||||
lhs.title == rhs.title &&
|
||||
lhs.value == rhs.value
|
||||
|
|
|
@ -21,17 +21,8 @@ typealias UserSessionDetailsViewModelType = StateStoreViewModel<UserSessionDetai
|
|||
UserSessionDetailsViewAction>
|
||||
|
||||
class UserSessionDetailsViewModel: UserSessionDetailsViewModelType, UserSessionDetailsViewModelProtocol {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var completion: ((UserSessionDetailsViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(userSessionInfo: UserSessionInfo) {
|
||||
super.init(initialViewState: UserSessionDetailsViewState(sections: []))
|
||||
updateViewState(userSessionInfo: userSessionInfo)
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
import Foundation
|
||||
|
||||
protocol UserSessionDetailsViewModelProtocol {
|
||||
|
||||
var completion: ((UserSessionDetailsViewModelResult) -> Void)? { get set }
|
||||
var context: UserSessionDetailsViewModelType.Context { get }
|
||||
}
|
||||
|
|
|
@ -17,11 +17,6 @@
|
|||
import SwiftUI
|
||||
|
||||
struct UserSessionDetails: View {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private enum LayoutConstants {
|
||||
static let listItemHorizontalPadding: CGFloat = 20
|
||||
static let sectionVerticalPadding: CGFloat = 8
|
||||
|
@ -29,8 +24,6 @@ struct UserSessionDetails: View {
|
|||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@ObservedObject var viewModel: UserSessionDetailsViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
|
|
|
@ -17,15 +17,8 @@
|
|||
import SwiftUI
|
||||
|
||||
struct UserSessionDetailsItem: View {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
// MARK: Public
|
||||
|
||||
let viewData: UserSessionDetailsSectionItemViewData
|
||||
let horizontalPadding: CGFloat
|
||||
|
||||
|
|
|
@ -18,9 +18,6 @@ import Foundation
|
|||
|
||||
/// Enables to build last activity date string
|
||||
class UserSessionLastActivityFormatter {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private static var lastActivityDateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.locale = Locale.current
|
||||
|
@ -30,8 +27,6 @@ class UserSessionLastActivityFormatter {
|
|||
return dateFormatter
|
||||
}()
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Session last activity string
|
||||
func lastActivityDateString(from lastActivityTimestamp: TimeInterval) -> String {
|
||||
|
||||
|
|
|
@ -17,11 +17,9 @@
|
|||
import Foundation
|
||||
|
||||
/// Enables to build user session name
|
||||
class UserSessionNameFormatter {
|
||||
|
||||
class UserSessionNameFormatter {
|
||||
/// Session name with client name and session display name
|
||||
func sessionName(deviceType: DeviceType, sessionDisplayName: String?) -> String {
|
||||
|
||||
let sessionName: String
|
||||
|
||||
let clientName = deviceType.name
|
||||
|
|
|
@ -18,7 +18,6 @@ import Foundation
|
|||
|
||||
/// Represents a user session information
|
||||
struct UserSessionInfo: Identifiable {
|
||||
|
||||
/// Delay after which session is considered inactive, 90 days
|
||||
static let inactiveSessionDurationTreshold: TimeInterval = 90 * 86400
|
||||
|
||||
|
@ -67,12 +66,9 @@ struct UserSessionInfo: Identifiable {
|
|||
|
||||
if let lastSeenTimestamp = lastSeenTimestamp {
|
||||
let elapsedTime = Date().timeIntervalSince1970 - lastSeenTimestamp
|
||||
|
||||
let isSessionInactive = elapsedTime >= Self.inactiveSessionDurationTreshold
|
||||
|
||||
self.isSessionActive = !isSessionInactive
|
||||
isSessionActive = elapsedTime < Self.inactiveSessionDurationTreshold
|
||||
} else {
|
||||
self.isSessionActive = true
|
||||
isSessionActive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,14 +23,9 @@ struct UserSessionOverviewCoordinatorParameters {
|
|||
}
|
||||
|
||||
final class UserSessionOverviewCoordinator: Coordinator, Presentable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: UserSessionOverviewCoordinatorParameters
|
||||
private let userSessionOverviewHostingController: UIViewController
|
||||
private var userSessionOverviewViewModel: UserSessionOverviewViewModelProtocol
|
||||
private let hostingController: UIViewController
|
||||
private var viewModel: UserSessionOverviewViewModelProtocol
|
||||
|
||||
private var indicatorPresenter: UserIndicatorTypePresenterProtocol
|
||||
private var loadingIndicator: UserIndicator?
|
||||
|
@ -45,20 +40,20 @@ final class UserSessionOverviewCoordinator: Coordinator, Presentable {
|
|||
|
||||
init(parameters: UserSessionOverviewCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
let viewModel = UserSessionOverviewViewModel(userSessionInfo: parameters.userSessionInfo,
|
||||
isCurrentSession: parameters.isCurrentSession)
|
||||
let view = UserSessionOverview(viewModel: viewModel.context)
|
||||
userSessionOverviewViewModel = viewModel
|
||||
userSessionOverviewHostingController = VectorHostingController(rootView: view)
|
||||
|
||||
viewModel = UserSessionOverviewViewModel(userSessionInfo: parameters.userSessionInfo,
|
||||
isCurrentSession: parameters.isCurrentSession)
|
||||
|
||||
indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: userSessionOverviewHostingController)
|
||||
hostingController = VectorHostingController(rootView: UserSessionOverview(viewModel: viewModel.context))
|
||||
|
||||
indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: hostingController)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
MXLog.debug("[UserSessionOverviewCoordinator] did start.")
|
||||
userSessionOverviewViewModel.completion = { [weak self] result in
|
||||
viewModel.completion = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
MXLog.debug("[UserSessionOverviewCoordinator] UserSessionOverviewViewModel did complete with result: \(result).")
|
||||
switch result {
|
||||
|
@ -71,7 +66,7 @@ final class UserSessionOverviewCoordinator: Coordinator, Presentable {
|
|||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userSessionOverviewHostingController
|
||||
return hostingController
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
|
|
@ -60,10 +60,6 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
|
|||
}
|
||||
|
||||
// can simulate service and viewModel actions here if needs be.
|
||||
|
||||
return (
|
||||
[viewModel],
|
||||
AnyView(UserSessionOverview(viewModel: viewModel.context))
|
||||
)
|
||||
return ([viewModel], AnyView(UserSessionOverview(viewModel: viewModel.context)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import XCTest
|
|||
import RiotSwiftUI
|
||||
|
||||
class UserSessionOverviewUITests: MockScreenTestCase {
|
||||
|
||||
func test_whenCurrentSessionSelected_correctNavTittleDisplayed() {
|
||||
app.goToScreenWithIdentifier(MockUserSessionOverviewScreenState.currentSession.title)
|
||||
let navTitle = VectorL10n.userSessionOverviewCurrentSessionTitle
|
||||
|
|
|
@ -20,7 +20,6 @@ import Combine
|
|||
@testable import RiotSwiftUI
|
||||
|
||||
class UserSessionOverviewViewModelTests: XCTestCase {
|
||||
|
||||
var sut: UserSessionOverviewViewModel!
|
||||
|
||||
func test_whenVerifyCurrentSessionProcessed_completionWithVerifyCurrentSessionCalled() {
|
||||
|
|
|
@ -21,17 +21,10 @@ typealias UserSessionOverviewViewModelType = StateStoreViewModel<UserSessionOver
|
|||
UserSessionOverviewViewAction>
|
||||
|
||||
class UserSessionOverviewViewModel: UserSessionOverviewViewModelType, UserSessionOverviewViewModelProtocol {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
private let userSessionInfo: UserSessionInfo
|
||||
// MARK: Public
|
||||
|
||||
var completion: ((UserSessionOverviewViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(userSessionInfo: UserSessionInfo, isCurrentSession: Bool) {
|
||||
self.userSessionInfo = userSessionInfo
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
import Foundation
|
||||
|
||||
protocol UserSessionOverviewViewModelProtocol {
|
||||
|
||||
var completion: ((UserSessionOverviewViewModelResult) -> Void)? { get set }
|
||||
var context: UserSessionOverviewViewModelType.Context { get }
|
||||
}
|
||||
|
|
|
@ -17,15 +17,8 @@
|
|||
import SwiftUI
|
||||
|
||||
struct UserSessionOverview: View {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@ObservedObject var viewModel: UserSessionOverviewViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
import SwiftUI
|
||||
|
||||
struct UserSessionOverviewDisclosureCell: View {
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
let title: String
|
||||
|
|
|
@ -22,33 +22,24 @@ struct UserSessionsFlowCoordinatorParameters {
|
|||
}
|
||||
|
||||
final class UserSessionsFlowCoordinator: Coordinator, Presentable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: UserSessionsFlowCoordinatorParameters
|
||||
private let navigationRouter: NavigationRouterType
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
var completion: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: UserSessionsFlowCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
self.navigationRouter = parameters.router ?? NavigationRouter(navigationController: RiotNavigationController())
|
||||
navigationRouter = parameters.router ?? NavigationRouter(navigationController: RiotNavigationController())
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func pushScreen(with coordinator: Coordinator & Presentable) {
|
||||
add(childCoordinator: coordinator)
|
||||
self.navigationRouter.push(coordinator, animated: true, popCompletion: { [weak self] in
|
||||
|
||||
navigationRouter.push(coordinator, animated: true, popCompletion: { [weak self] in
|
||||
self?.remove(childCoordinator: coordinator)
|
||||
})
|
||||
|
||||
|
@ -56,7 +47,7 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
|
|||
}
|
||||
|
||||
private func createUserSessionsOverviewCoordinator() -> UserSessionsOverviewCoordinator {
|
||||
let parameters = UserSessionsOverviewCoordinatorParameters(session: self.parameters.session)
|
||||
let parameters = UserSessionsOverviewCoordinatorParameters(session: parameters.session)
|
||||
|
||||
let coordinator = UserSessionsOverviewCoordinator(parameters: parameters)
|
||||
coordinator.completion = { [weak self] result in
|
||||
|
@ -104,15 +95,15 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
|
|||
let rootCoordinator = createUserSessionsOverviewCoordinator()
|
||||
rootCoordinator.start()
|
||||
|
||||
self.add(childCoordinator: rootCoordinator)
|
||||
add(childCoordinator: rootCoordinator)
|
||||
|
||||
if self.navigationRouter.modules.isEmpty == false {
|
||||
self.navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in
|
||||
if navigationRouter.modules.isEmpty == false {
|
||||
navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in
|
||||
self?.remove(childCoordinator: rootCoordinator)
|
||||
self?.completion?()
|
||||
})
|
||||
} else {
|
||||
self.navigationRouter.setRootModule(rootCoordinator) { [weak self] in
|
||||
navigationRouter.setRootModule(rootCoordinator) { [weak self] in
|
||||
self?.remove(childCoordinator: rootCoordinator)
|
||||
self?.completion?()
|
||||
}
|
||||
|
@ -120,6 +111,6 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
|
|||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.navigationRouter.toPresentable()
|
||||
return navigationRouter.toPresentable()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,22 +24,11 @@ import UIKit
|
|||
/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
|
||||
@objcMembers
|
||||
final class UserSessionsFlowCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let mxSession: MXSession
|
||||
private var coordinator: UserSessionsFlowCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
|
||||
var completion: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(mxSession: MXSession) {
|
||||
self.mxSession = mxSession
|
||||
super.init()
|
||||
|
@ -48,26 +37,22 @@ final class UserSessionsFlowCoordinatorBridgePresenter: NSObject {
|
|||
// MARK: - Public
|
||||
|
||||
func push(from navigationController: UINavigationController, animated: Bool) {
|
||||
|
||||
self.startUserSessionsFlow(mxSession: self.mxSession, navigationController: navigationController)
|
||||
startUserSessionsFlow(mxSession: mxSession, navigationController: navigationController)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func startUserSessionsFlow(mxSession: MXSession, navigationController: UINavigationController?) {
|
||||
|
||||
var navigationRouter: NavigationRouterType?
|
||||
|
||||
if let navigationController = navigationController {
|
||||
navigationRouter = NavigationRouterStore.shared.navigationRouter(for: navigationController)
|
||||
}
|
||||
|
||||
let coordinatorParameters = UserSessionsFlowCoordinatorParameters(session: mxSession, router: navigationRouter)
|
||||
let parameters = UserSessionsFlowCoordinatorParameters(session: mxSession, router: navigationRouter)
|
||||
let coordinator = UserSessionsFlowCoordinator(parameters: parameters)
|
||||
|
||||
let userSessionsFlowCoordinator = UserSessionsFlowCoordinator(parameters: coordinatorParameters)
|
||||
|
||||
userSessionsFlowCoordinator.completion = { [weak self] in
|
||||
|
||||
coordinator.completion = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
@ -76,8 +61,8 @@ final class UserSessionsFlowCoordinatorBridgePresenter: NSObject {
|
|||
self.coordinator = nil
|
||||
}
|
||||
|
||||
userSessionsFlowCoordinator.start()
|
||||
coordinator.start()
|
||||
|
||||
self.coordinator = userSessionsFlowCoordinator
|
||||
self.coordinator = coordinator
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,39 +22,24 @@ struct UserSessionsOverviewCoordinatorParameters {
|
|||
}
|
||||
|
||||
final class UserSessionsOverviewCoordinator: Coordinator, Presentable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: UserSessionsOverviewCoordinatorParameters
|
||||
private let userSessionsOverviewHostingController: UIViewController
|
||||
private var userSessionsOverviewViewModel: UserSessionsOverviewViewModelProtocol
|
||||
private let hostingViewController: UIViewController
|
||||
private var viewModel: UserSessionsOverviewViewModelProtocol
|
||||
private let service: UserSessionsOverviewService
|
||||
|
||||
private var indicatorPresenter: UserIndicatorTypePresenterProtocol
|
||||
private var loadingIndicator: UserIndicator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
var completion: ((UserSessionsOverviewCoordinatorResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: UserSessionsOverviewCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
let service = UserSessionsOverviewService(mxSession: parameters.session)
|
||||
self.service = service
|
||||
let viewModel = UserSessionsOverviewViewModel(userSessionsOverviewService: service)
|
||||
let view = UserSessionsOverview(viewModel: viewModel.context)
|
||||
userSessionsOverviewViewModel = viewModel
|
||||
|
||||
let hostingViewController = VectorHostingController(rootView: view)
|
||||
|
||||
userSessionsOverviewHostingController = hostingViewController
|
||||
|
||||
service = UserSessionsOverviewService(mxSession: parameters.session)
|
||||
viewModel = UserSessionsOverviewViewModel(userSessionsOverviewService: service)
|
||||
hostingViewController = VectorHostingController(rootView: UserSessionsOverview(viewModel: viewModel.context))
|
||||
indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: hostingViewController)
|
||||
}
|
||||
|
||||
|
@ -62,9 +47,10 @@ final class UserSessionsOverviewCoordinator: Coordinator, Presentable {
|
|||
|
||||
func start() {
|
||||
MXLog.debug("[UserSessionsOverviewCoordinator] did start.")
|
||||
userSessionsOverviewViewModel.completion = { [weak self] result in
|
||||
viewModel.completion = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
MXLog.debug("[UserSessionsOverviewCoordinator] UserSessionsOverviewViewModel did complete with result: \(result).")
|
||||
|
||||
switch result {
|
||||
case .showAllUnverifiedSessions:
|
||||
self.showAllUnverifiedSessions()
|
||||
|
@ -83,7 +69,7 @@ final class UserSessionsOverviewCoordinator: Coordinator, Presentable {
|
|||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.userSessionsOverviewHostingController
|
||||
return hostingViewController
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
|
|
@ -18,33 +18,25 @@ import Foundation
|
|||
import MatrixSDK
|
||||
|
||||
class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let mxSession: MXSession
|
||||
|
||||
// MARK: Public
|
||||
|
||||
private(set) var lastOverviewData: UserSessionsOverviewData
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(mxSession: MXSession) {
|
||||
self.mxSession = mxSession
|
||||
|
||||
self.lastOverviewData = UserSessionsOverviewData(currentSessionInfo: nil, unverifiedSessionsInfo: [], inactiveSessionsInfo: [], otherSessionsInfo: [])
|
||||
lastOverviewData = UserSessionsOverviewData(currentSessionInfo: nil,
|
||||
unverifiedSessionsInfo: [],
|
||||
inactiveSessionsInfo: [],
|
||||
otherSessionsInfo: [])
|
||||
|
||||
self.setupInitialOverviewData()
|
||||
setupInitialOverviewData()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fetchUserSessionsOverviewData(completion: @escaping (Result<UserSessionsOverviewData, Error>) -> Void) {
|
||||
self.mxSession.matrixRestClient.devices { response in
|
||||
mxSession.matrixRestClient.devices { response in
|
||||
switch response {
|
||||
case .success(let devices):
|
||||
self.lastOverviewData = self.userSessionsOverviewData(from: devices)
|
||||
|
@ -62,21 +54,20 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
|
|||
// MARK: - Private
|
||||
|
||||
private func setupInitialOverviewData() {
|
||||
let currentSessionInfo = self.getCurrentUserSessionInfoFromCache()
|
||||
let currentSessionInfo = getCurrentUserSessionInfoFromCache()
|
||||
|
||||
self.lastOverviewData = UserSessionsOverviewData(currentSessionInfo: currentSessionInfo, unverifiedSessionsInfo: [], inactiveSessionsInfo: [], otherSessionsInfo: [])
|
||||
lastOverviewData = UserSessionsOverviewData(currentSessionInfo: currentSessionInfo, unverifiedSessionsInfo: [], inactiveSessionsInfo: [], otherSessionsInfo: [])
|
||||
}
|
||||
|
||||
private func getCurrentUserSessionInfoFromCache() -> UserSessionInfo? {
|
||||
guard let mainAccount = MXKAccountManager.shared().activeAccounts.first, let device = mainAccount.device else {
|
||||
return nil
|
||||
}
|
||||
return self.userSessionInfo(from: device)
|
||||
return userSessionInfo(from: device)
|
||||
}
|
||||
|
||||
private func userSessionInfo(from device: MXDevice) -> UserSessionInfo {
|
||||
|
||||
let deviceInfo = self.getDeviceInfo(for: device.deviceId)
|
||||
let deviceInfo = getDeviceInfo(for: device.deviceId)
|
||||
|
||||
let isSessionVerified = deviceInfo?.trustLevel.isVerified ?? false
|
||||
|
||||
|
@ -95,20 +86,20 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
|
|||
}
|
||||
|
||||
private func getDeviceInfo(for deviceId: String) -> MXDeviceInfo? {
|
||||
guard let userId = self.mxSession.myUserId else {
|
||||
guard let userId = mxSession.myUserId else {
|
||||
return nil
|
||||
}
|
||||
return self.mxSession.crypto.device(withDeviceId: deviceId, ofUser: userId)
|
||||
|
||||
return mxSession.crypto.device(withDeviceId: deviceId, ofUser: userId)
|
||||
}
|
||||
|
||||
private func userSessionsOverviewData(from devices: [MXDevice]) -> UserSessionsOverviewData {
|
||||
|
||||
let sortedDevices = devices.sorted { device1, device2 in
|
||||
device1.lastSeenTs > device2.lastSeenTs
|
||||
}
|
||||
|
||||
let allUserSessionInfo = sortedDevices.map { device in
|
||||
return self.userSessionInfo(from: device)
|
||||
return userSessionInfo(from: device)
|
||||
}
|
||||
|
||||
var currentSessionInfo: UserSessionInfo?
|
||||
|
@ -118,7 +109,7 @@ class UserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
|
|||
var otherSessionsInfo: [UserSessionInfo] = []
|
||||
|
||||
for userSessionInfo in allUserSessionInfo {
|
||||
if userSessionInfo.sessionId == self.mxSession.myDeviceId {
|
||||
if userSessionInfo.sessionId == mxSession.myDeviceId {
|
||||
currentSessionInfo = userSessionInfo
|
||||
} else {
|
||||
otherSessionsInfo.append(userSessionInfo)
|
||||
|
|
|
@ -17,11 +17,10 @@
|
|||
import Foundation
|
||||
|
||||
class MockUserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
|
||||
|
||||
var lastOverviewData: UserSessionsOverviewData
|
||||
|
||||
func fetchUserSessionsOverviewData(completion: @escaping (Result<UserSessionsOverviewData, Error>) -> Void) {
|
||||
completion(.success(self.lastOverviewData))
|
||||
completion(.success(lastOverviewData))
|
||||
}
|
||||
|
||||
func getOtherSession(sessionId: String) -> UserSessionInfo? {
|
||||
|
@ -41,6 +40,9 @@ class MockUserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
|
|||
UserSessionInfo(sessionId: "3", sessionName: "Android", deviceType: .mobile, isVerified: false, lastSeenIP: "3.0.0.3", lastSeenTimestamp: (Date().timeIntervalSince1970 - 10))
|
||||
]
|
||||
|
||||
self.lastOverviewData = UserSessionsOverviewData(currentSessionInfo: currentSessionInfo, unverifiedSessionsInfo: unverifiedSessionsInfo, inactiveSessionsInfo: inactiveSessionsInfo, otherSessionsInfo: otherSessionsInfo)
|
||||
lastOverviewData = UserSessionsOverviewData(currentSessionInfo: currentSessionInfo,
|
||||
unverifiedSessionsInfo: unverifiedSessionsInfo,
|
||||
inactiveSessionsInfo: inactiveSessionsInfo,
|
||||
otherSessionsInfo: otherSessionsInfo)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
import Foundation
|
||||
|
||||
struct UserSessionsOverviewData {
|
||||
|
||||
let currentSessionInfo: UserSessionInfo?
|
||||
let unverifiedSessionsInfo: [UserSessionInfo]
|
||||
let inactiveSessionsInfo: [UserSessionInfo]
|
||||
|
@ -25,7 +24,6 @@ struct UserSessionsOverviewData {
|
|||
}
|
||||
|
||||
protocol UserSessionsOverviewServiceProtocol {
|
||||
|
||||
var lastOverviewData: UserSessionsOverviewData { get }
|
||||
|
||||
func fetchUserSessionsOverviewData(completion: @escaping (Result<UserSessionsOverviewData, Error>) -> Void) -> Void
|
||||
|
|
|
@ -20,7 +20,6 @@ import Combine
|
|||
@testable import RiotSwiftUI
|
||||
|
||||
class UserSessionsOverviewViewModelTests: XCTestCase {
|
||||
|
||||
var service: MockUserSessionsOverviewService!
|
||||
var viewModel: UserSessionsOverviewViewModelProtocol!
|
||||
var context: UserSessionsOverviewViewModelType.Context!
|
||||
|
|
|
@ -36,14 +36,13 @@ enum UserSessionsOverviewViewModelResult {
|
|||
// MARK: View
|
||||
|
||||
struct UserSessionsOverviewViewState: BindableState {
|
||||
|
||||
var unverifiedSessionsViewData: [UserSessionListItemViewData]
|
||||
|
||||
var inactiveSessionsViewData: [UserSessionListItemViewData]
|
||||
|
||||
var currentSessionViewData: UserSessionCardViewData?
|
||||
|
||||
var otherSessionsViewData: [UserSessionListItemViewData]
|
||||
var unverifiedSessionsViewData = [UserSessionListItemViewData]()
|
||||
|
||||
var inactiveSessionsViewData = [UserSessionListItemViewData]()
|
||||
|
||||
var otherSessionsViewData = [UserSessionListItemViewData]()
|
||||
|
||||
var showLoadingIndicator: Bool = false
|
||||
}
|
||||
|
|
|
@ -21,91 +21,63 @@ typealias UserSessionsOverviewViewModelType = StateStoreViewModel<UserSessionsOv
|
|||
UserSessionsOverviewViewAction>
|
||||
|
||||
class UserSessionsOverviewViewModel: UserSessionsOverviewViewModelType, UserSessionsOverviewViewModelProtocol {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let userSessionsOverviewService: UserSessionsOverviewServiceProtocol
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var completion: ((UserSessionsOverviewViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(userSessionsOverviewService: UserSessionsOverviewServiceProtocol) {
|
||||
self.userSessionsOverviewService = userSessionsOverviewService
|
||||
|
||||
let initialViewState = UserSessionsOverviewViewState(unverifiedSessionsViewData: [], inactiveSessionsViewData: [], currentSessionViewData: nil, otherSessionsViewData: [])
|
||||
super.init(initialViewState: .init())
|
||||
|
||||
super.init(initialViewState: initialViewState)
|
||||
|
||||
self.updateViewState(with: userSessionsOverviewService.lastOverviewData)
|
||||
updateViewState(with: userSessionsOverviewService.lastOverviewData)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
|
||||
override func process(viewAction: UserSessionsOverviewViewAction) {
|
||||
switch viewAction {
|
||||
case .viewAppeared:
|
||||
self.loadData()
|
||||
loadData()
|
||||
case .verifyCurrentSession:
|
||||
self.completion?(.verifyCurrentSession)
|
||||
completion?(.verifyCurrentSession)
|
||||
case .viewCurrentSessionDetails:
|
||||
guard let currentSessionInfo = userSessionsOverviewService.lastOverviewData.currentSessionInfo else {
|
||||
assertionFailure("currentSessionInfo should be present")
|
||||
return
|
||||
return
|
||||
}
|
||||
self.completion?(.showCurrentSessionOverview(sessionInfo: currentSessionInfo))
|
||||
completion?(.showCurrentSessionOverview(sessionInfo: currentSessionInfo))
|
||||
case .viewAllUnverifiedSessions:
|
||||
self.completion?(.showAllUnverifiedSessions)
|
||||
completion?(.showAllUnverifiedSessions)
|
||||
case .viewAllInactiveSessions:
|
||||
self.completion?(.showAllInactiveSessions)
|
||||
completion?(.showAllInactiveSessions)
|
||||
case .viewAllOtherSessions:
|
||||
self.completion?(.showAllOtherSessions)
|
||||
completion?(.showAllOtherSessions)
|
||||
case .tapUserSession(let sessionId):
|
||||
guard let sessionInfo = userSessionsOverviewService.getOtherSession(sessionId: sessionId) else {
|
||||
assertionFailure("missing session info")
|
||||
return
|
||||
}
|
||||
self.completion?(.showUserSessionOverview(sessionInfo: sessionInfo))
|
||||
completion?(.showUserSessionOverview(sessionInfo: sessionInfo))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func updateViewState(with userSessionsViewData: UserSessionsOverviewData) {
|
||||
|
||||
let unverifiedSessionsViewData = self.userSessionListItemViewDataList(from: userSessionsViewData.unverifiedSessionsInfo)
|
||||
let inactiveSessionsViewData = self.userSessionListItemViewDataList(from: userSessionsViewData.inactiveSessionsInfo)
|
||||
|
||||
var currentSessionViewData: UserSessionCardViewData?
|
||||
|
||||
let otherSessionsViewData = self.userSessionListItemViewDataList(from: userSessionsViewData.otherSessionsInfo)
|
||||
|
||||
state.unverifiedSessionsViewData = userSessionsViewData.unverifiedSessionsInfo.asViewData()
|
||||
state.inactiveSessionsViewData = userSessionsViewData.inactiveSessionsInfo.asViewData()
|
||||
state.otherSessionsViewData = userSessionsViewData.otherSessionsInfo.asViewData()
|
||||
|
||||
if let currentSessionInfo = userSessionsViewData.currentSessionInfo {
|
||||
currentSessionViewData = UserSessionCardViewData(userSessionInfo: currentSessionInfo, isCurrentSessionDisplayMode: true)
|
||||
}
|
||||
|
||||
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)
|
||||
state.currentSessionViewData = UserSessionCardViewData(userSessionInfo: currentSessionInfo, isCurrentSessionDisplayMode: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func loadData() {
|
||||
state.showLoadingIndicator = true
|
||||
|
||||
self.state.showLoadingIndicator = true
|
||||
|
||||
self.userSessionsOverviewService.fetchUserSessionsOverviewData { [weak self] result in
|
||||
userSessionsOverviewService.fetchUserSessionsOverviewData { [weak self] result in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
@ -122,3 +94,9 @@ class UserSessionsOverviewViewModel: UserSessionsOverviewViewModelType, UserSess
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Collection where Element == UserSessionInfo {
|
||||
func asViewData() -> [UserSessionListItemViewData] {
|
||||
map { UserSessionListItemViewData(userSessionInfo: $0) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
import Foundation
|
||||
|
||||
protocol UserSessionsOverviewViewModelProtocol {
|
||||
|
||||
var completion: ((UserSessionsOverviewViewModelResult) -> Void)? { get set }
|
||||
|
||||
var context: UserSessionsOverviewViewModelType.Context { get }
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
import SwiftUI
|
||||
|
||||
struct UserSessionListItem: View {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum LayoutConstants {
|
||||
static let horizontalPadding: CGFloat = 15
|
||||
static let verticalPadding: CGFloat = 16
|
||||
|
@ -27,23 +24,16 @@ struct UserSessionListItem: View {
|
|||
static let avatarRightMargin: CGFloat = 18
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
// MARK: Public
|
||||
|
||||
let viewData: UserSessionListItemViewData
|
||||
|
||||
var onBackgroundTap: ((String) -> (Void))? = nil
|
||||
|
||||
// MARK: - Body
|
||||
|
||||
var body: some View {
|
||||
Button(action: { onBackgroundTap?(self.viewData.sessionId)
|
||||
}) {
|
||||
Button {
|
||||
onBackgroundTap?(viewData.sessionId)
|
||||
} label: {
|
||||
VStack(alignment: .leading, spacing: LayoutConstants.verticalPadding) {
|
||||
HStack(spacing: LayoutConstants.avatarRightMargin) {
|
||||
DeviceAvatarView(viewData: viewData.deviceAvatarViewData)
|
||||
|
@ -74,7 +64,6 @@ struct UserSessionListItem: View {
|
|||
}
|
||||
|
||||
struct UserSessionListPreview: View {
|
||||
|
||||
let userSessionsOverviewService: UserSessionsOverviewServiceProtocol = MockUserSessionsOverviewService()
|
||||
|
||||
var body: some View {
|
||||
|
|
|
@ -18,14 +18,9 @@ import Foundation
|
|||
|
||||
/// View data for UserSessionListItem
|
||||
struct UserSessionListItemViewData: Identifiable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private static let userSessionNameFormatter = UserSessionNameFormatter()
|
||||
private static let lastActivityDateFormatter = UserSessionLastActivityFormatter()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
|
||||
var id: String {
|
||||
return sessionId
|
||||
}
|
||||
|
@ -38,8 +33,6 @@ struct UserSessionListItemViewData: Identifiable {
|
|||
|
||||
let deviceAvatarViewData: DeviceAvatarViewData
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(sessionId: String,
|
||||
sessionDisplayName: String?,
|
||||
deviceType: DeviceType,
|
||||
|
@ -47,15 +40,14 @@ struct UserSessionListItemViewData: Identifiable {
|
|||
lastActivityDate: TimeInterval?) {
|
||||
|
||||
self.sessionId = sessionId
|
||||
self.sessionName = Self.userSessionNameFormatter.sessionName(deviceType: deviceType, sessionDisplayName: sessionDisplayName)
|
||||
self.sessionDetails = Self.buildSessionDetails(isVerified: isVerified, lastActivityDate: lastActivityDate)
|
||||
self.deviceAvatarViewData = DeviceAvatarViewData(deviceType: deviceType, isVerified: isVerified)
|
||||
sessionName = Self.userSessionNameFormatter.sessionName(deviceType: deviceType, sessionDisplayName: sessionDisplayName)
|
||||
sessionDetails = Self.buildSessionDetails(isVerified: isVerified, lastActivityDate: lastActivityDate)
|
||||
deviceAvatarViewData = DeviceAvatarViewData(deviceType: deviceType, isVerified: isVerified)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static func buildSessionDetails(isVerified: Bool, lastActivityDate: TimeInterval?) -> String {
|
||||
|
||||
let sessionDetailsString: String
|
||||
|
||||
let sessionStatusText = isVerified ? VectorL10n.userSessionVerifiedShort : VectorL10n.userSessionUnverifiedShort
|
||||
|
@ -77,7 +69,6 @@ struct UserSessionListItemViewData: Identifiable {
|
|||
}
|
||||
|
||||
extension UserSessionListItemViewData {
|
||||
|
||||
init(userSessionInfo: UserSessionInfo) {
|
||||
self.init(sessionId: userSessionInfo.sessionId, sessionDisplayName: userSessionInfo.sessionName, deviceType: userSessionInfo.deviceType, isVerified: userSessionInfo.isVerified, lastActivityDate: userSessionInfo.lastSeenTimestamp)
|
||||
}
|
||||
|
|
|
@ -17,11 +17,6 @@
|
|||
import SwiftUI
|
||||
|
||||
struct UserSessionsOverview: View {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -52,7 +47,6 @@ struct UserSessionsOverview: View {
|
|||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
|
||||
// Security recommendations section
|
||||
if viewModel.viewState.unverifiedSessionsViewData.isEmpty == false || viewModel.viewState.inactiveSessionsViewData.isEmpty == false {
|
||||
|
||||
|
@ -64,7 +58,7 @@ struct UserSessionsOverview: View {
|
|||
|
||||
// Other sessions section
|
||||
if viewModel.viewState.otherSessionsViewData.isEmpty == false {
|
||||
self.otherSessionsSection
|
||||
otherSessionsSection
|
||||
}
|
||||
}
|
||||
.background(theme.colors.system.ignoresSafeArea())
|
||||
|
@ -77,7 +71,6 @@ struct UserSessionsOverview: View {
|
|||
}
|
||||
|
||||
private var otherSessionsSection: some View {
|
||||
|
||||
SwiftUI.Section {
|
||||
// Device list
|
||||
LazyVStack(spacing: 0) {
|
||||
|
|
Loading…
Reference in a new issue