Merge pull request #6860 from vector-im/aleksandrs/6838_filter_sessions

Device Manager: Filter sessions
This commit is contained in:
Aleksandrs Proskurins
2022-10-12 15:27:42 +03:00
committed by GitHub
22 changed files with 412 additions and 57 deletions
@@ -141,7 +141,7 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
return UserSessionOverviewCoordinator(parameters: parameters)
}
private func openOtherSessions(sessionInfos: [UserSessionInfo], filterBy filter: OtherUserSessionsFilter) {
private func openOtherSessions(sessionInfos: [UserSessionInfo], filterBy filter: UserOtherSessionsFilter) {
let title = filter == .all ? VectorL10n.userSessionsOverviewOtherSessionsSectionTitle : VectorL10n.userOtherSessionSecurityRecommendationTitle
let coordinator = createOtherSessionsCoordinator(sessionInfos: sessionInfos,
filterBy: filter,
@@ -157,7 +157,7 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
}
private func createOtherSessionsCoordinator(sessionInfos: [UserSessionInfo],
filterBy filter: OtherUserSessionsFilter,
filterBy filter: UserOtherSessionsFilter,
title: String) -> UserOtherSessionsCoordinator {
let parameters = UserOtherSessionsCoordinatorParameters(sessionInfos: sessionInfos,
filter: filter,
@@ -19,7 +19,7 @@ import SwiftUI
struct UserOtherSessionsCoordinatorParameters {
let sessionInfos: [UserSessionInfo]
let filter: OtherUserSessionsFilter
let filter: UserOtherSessionsFilter
let title: String
}
@@ -27,6 +27,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
case all
case inactiveSessions
case unverifiedSessions
case verifiedSessions
/// The associated screen
var screenType: Any.Type {
@@ -36,7 +37,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
/// A list of screen state definitions
static var allCases: [MockUserOtherSessionsScreenState] {
// Each of the presence statuses
[.all, .inactiveSessions, .unverifiedSessions]
[.all, .inactiveSessions, .unverifiedSessions, .verifiedSessions]
}
/// Generate the view struct for the screen state.
@@ -55,6 +56,10 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
viewModel = UserOtherSessionsViewModel(sessionInfos: unverifiedSessions(),
filter: .unverified,
title: VectorL10n.userOtherSessionSecurityRecommendationTitle)
case .verifiedSessions:
viewModel = UserOtherSessionsViewModel(sessionInfos: verifiedSessions(),
filter: .verified,
title: VectorL10n.userOtherSessionSecurityRecommendationTitle)
}
// can simulate service and viewModel actions here if needs be.
@@ -167,6 +172,41 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
isCurrent: false)]
}
private func verifiedSessions() -> [UserSessionInfo] {
[UserSessionInfo(id: "0",
name: "iOS",
deviceType: .mobile,
verificationState: .verified,
lastSeenIP: "10.0.0.10",
lastSeenTimestamp: nil,
applicationName: nil,
applicationVersion: nil,
applicationURL: nil,
deviceModel: nil,
deviceOS: nil,
lastSeenIPLocation: nil,
clientName: nil,
clientVersion: nil,
isActive: true,
isCurrent: true),
UserSessionInfo(id: "1",
name: "macOS",
deviceType: .desktop,
verificationState: .verified,
lastSeenIP: "1.0.0.1",
lastSeenTimestamp: Date().timeIntervalSince1970 - 8_000_000,
applicationName: nil,
applicationVersion: nil,
applicationURL: nil,
deviceModel: nil,
deviceOS: nil,
lastSeenIPLocation: nil,
clientName: nil,
clientVersion: nil,
isActive: true,
isCurrent: false)]
}
private func allSessions() -> [UserSessionInfo] {
[UserSessionInfo(id: "0",
name: "iOS",
@@ -21,7 +21,7 @@ class UserOtherSessionsUITests: MockScreenTestCase {
func test_whenOtherSessionsWithInactiveSessionFilterPresented_correctHeaderDisplayed() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.inactiveSessions.title)
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveTitle].exists)
XCTAssertTrue(app.staticTexts[VectorL10n.userOtherSessionFilterMenuInactive].exists)
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveInfo].exists)
}
@@ -34,7 +34,7 @@ class UserOtherSessionsUITests: MockScreenTestCase {
func test_whenOtherSessionsWithUnverifiedSessionFilterPresented_correctHeaderDisplayed() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.unverifiedSessions.title)
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionsOverviewSecurityRecommendationsUnverifiedTitle].exists)
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionUnverifiedShort].exists)
XCTAssertTrue(app.staticTexts[VectorL10n.userOtherSessionUnverifiedSessionsHeaderSubtitle].exists)
}
@@ -49,4 +49,11 @@ class UserOtherSessionsUITests: MockScreenTestCase {
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionsOverviewOtherSessionsSectionInfo].exists)
}
func test_whenOtherSessionsWithVerifiedSessionFilterPresented_correctHeaderDisplayed() {
app.goToScreenWithIdentifier(MockUserOtherSessionsScreenState.verifiedSessions.title)
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionVerifiedShort].exists)
XCTAssertTrue(app.staticTexts[VectorL10n.userOtherSessionVerifiedSessionsHeaderSubtitle].exists)
}
}
@@ -19,12 +19,27 @@ import XCTest
@testable import RiotSwiftUI
class UserOtherSessionsViewModelTests: XCTestCase {
private let unverifiedSectionHeader = UserOtherSessionsHeaderViewData(title: VectorL10n.userSessionUnverifiedShort,
subtitle: VectorL10n.userOtherSessionUnverifiedSessionsHeaderSubtitle,
iconName: Asset.Images.userOtherSessionsUnverified.name)
private let inactiveSectionHeader = UserOtherSessionsHeaderViewData(title: VectorL10n.userOtherSessionFilterMenuInactive,
subtitle: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveInfo,
iconName: Asset.Images.userOtherSessionsInactive.name)
private let allSectionHeader = UserOtherSessionsHeaderViewData(title: nil,
subtitle: VectorL10n.userSessionsOverviewOtherSessionsSectionInfo,
iconName: nil)
private let verifiedSectionHeader = UserOtherSessionsHeaderViewData(title: VectorL10n.userOtherSessionFilterMenuVerified,
subtitle: VectorL10n.userOtherSessionVerifiedSessionsHeaderSubtitle,
iconName: Asset.Images.userOtherSessionsVerified.name)
func test_whenUserOtherSessionSelectedProcessed_completionWithShowUserSessionOverviewCalled() {
let expectedUserSessionInfo = createUserSessionInfo(sessionId: "session 2")
let sut = UserOtherSessionsViewModel(sessionInfos: [createUserSessionInfo(sessionId: "session 1"),
expectedUserSessionInfo],
filter: .inactive,
title: "Title")
let sessionInfos = [createUserSessionInfo(sessionId: "session 1"),
expectedUserSessionInfo]
let sut = createSUT(sessionInfos: sessionInfos, filter: .inactive)
var modelResult: UserOtherSessionsViewModelResult?
sut.completion = { result in
@@ -35,40 +50,102 @@ class UserOtherSessionsViewModelTests: XCTestCase {
}
func test_whenModelCreated_withInactiveFilter_viewStateIsCorrect() {
let sessionInfos = [createUserSessionInfo(sessionId: "session 1"), createUserSessionInfo(sessionId: "session 2")]
let sut = UserOtherSessionsViewModel(sessionInfos: sessionInfos,
filter: .inactive,
title: "Title")
let sessionInfos = [createUserSessionInfo(sessionId: "session 1", isActive: false),
createUserSessionInfo(sessionId: "session 2", isActive: false)]
let sut = createSUT(sessionInfos: sessionInfos, filter: .inactive)
let expectedHeader = UserOtherSessionsHeaderViewData(title: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveTitle,
subtitle: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveInfo,
iconName: Asset.Images.userOtherSessionsInactive.name)
let expectedItems = sessionInfos.filter { !$0.isActive }.asViewData()
let expectedState = UserOtherSessionsViewState(title: "Title",
sections: [.sessionItems(header: expectedHeader, items: expectedItems)])
let expectedState = UserOtherSessionsViewState(bindings: UserOtherSessionsBindings(filter: .inactive),
title: "Title",
sections: [.sessionItems(header: inactiveSectionHeader, items: expectedItems)])
XCTAssertEqual(sut.state, expectedState)
}
func test_whenModelCreated_withAllFilter_viewStateIsCorrect() {
let sessionInfos = [createUserSessionInfo(sessionId: "session 1"), createUserSessionInfo(sessionId: "session 2")]
let sut = UserOtherSessionsViewModel(sessionInfos: sessionInfos,
filter: .all,
title: "Title")
let sessionInfos = [createUserSessionInfo(sessionId: "session 1"),
createUserSessionInfo(sessionId: "session 2")]
let sut = createSUT(sessionInfos: sessionInfos, filter: .all)
let expectedHeader = UserOtherSessionsHeaderViewData(title: nil,
subtitle: VectorL10n.userSessionsOverviewOtherSessionsSectionInfo,
iconName: nil)
let expectedItems = sessionInfos.filter { !$0.isCurrent }.asViewData()
let expectedState = UserOtherSessionsViewState(title: "Title",
sections: [.sessionItems(header: expectedHeader, items: expectedItems)])
let expectedState = UserOtherSessionsViewState(bindings: UserOtherSessionsBindings(filter: .all),
title: "Title",
sections: [.sessionItems(header: allSectionHeader, items: expectedItems)])
XCTAssertEqual(sut.state, expectedState)
}
private func createUserSessionInfo(sessionId: String) -> UserSessionInfo {
func test_whenModelCreated_withUnverifiedFilter_viewStateIsCorrect() {
let sessionInfos = [createUserSessionInfo(sessionId: "session 1"),
createUserSessionInfo(sessionId: "session 2")]
let sut = createSUT(sessionInfos: sessionInfos, filter: .unverified)
let expectedItems = sessionInfos.filter { !$0.isCurrent }.asViewData()
let expectedState = UserOtherSessionsViewState(bindings: UserOtherSessionsBindings(filter: .unverified),
title: "Title",
sections: [.sessionItems(header: unverifiedSectionHeader, items: expectedItems)])
XCTAssertEqual(sut.state, expectedState)
}
func test_whenModelCreated_withVerifiedFilter_viewStateIsCorrect() {
let sessionInfos = [createUserSessionInfo(sessionId: "session 1", isVerified: true),
createUserSessionInfo(sessionId: "session 2", isVerified: true)]
let sut = createSUT(sessionInfos: sessionInfos, filter: .verified)
let expectedItems = sessionInfos.filter { !$0.isCurrent }.asViewData()
let expectedState = UserOtherSessionsViewState(bindings: UserOtherSessionsBindings(filter: .verified),
title: "Title",
sections: [.sessionItems(header: verifiedSectionHeader, items: expectedItems)])
XCTAssertEqual(sut.state, expectedState)
}
func test_whenModelCreated_withVerifiedFilterWithNoVerifiedSessions_viewStateIsCorrect() {
let sessionInfos = [createUserSessionInfo(sessionId: "session 1", isVerified: false),
createUserSessionInfo(sessionId: "session 2", isVerified: false)]
let sut = createSUT(sessionInfos: sessionInfos, filter: .verified)
let expectedState = UserOtherSessionsViewState(bindings: UserOtherSessionsBindings(filter: .verified),
title: "Title",
sections: [.emptySessionItems(header: verifiedSectionHeader, title: VectorL10n.userOtherSessionNoVerifiedSessions)])
XCTAssertEqual(sut.state, expectedState)
}
func test_whenModelCreated_withUnverifiedFilterWithNoUnverifiedSessions_viewStateIsCorrect() {
let sessionInfos = [createUserSessionInfo(sessionId: "session 1", isVerified: true),
createUserSessionInfo(sessionId: "session 2", isVerified: true)]
let sut = createSUT(sessionInfos: sessionInfos, filter: .unverified)
let expectedState = UserOtherSessionsViewState(bindings: UserOtherSessionsBindings(filter: .unverified),
title: "Title",
sections: [.emptySessionItems(header: unverifiedSectionHeader, title: VectorL10n.userOtherSessionNoUnverifiedSessions)])
XCTAssertEqual(sut.state, expectedState)
}
func test_whenModelCreated_withInactiveFilterWithNoInactiveSessions_viewStateIsCorrect() {
let sessionInfos = [createUserSessionInfo(sessionId: "session 1", isActive: true),
createUserSessionInfo(sessionId: "session 2", isActive: true)]
let sut = createSUT(sessionInfos: sessionInfos, filter: .inactive)
let expectedState = UserOtherSessionsViewState(bindings: UserOtherSessionsBindings(filter: .inactive),
title: "Title",
sections: [.emptySessionItems(header: inactiveSectionHeader, title: VectorL10n.userOtherSessionNoInactiveSessions)])
XCTAssertEqual(sut.state, expectedState)
}
private func createSUT(sessionInfos: [UserSessionInfo],
filter: UserOtherSessionsFilter,
title: String = "Title") -> UserOtherSessionsViewModel {
UserOtherSessionsViewModel(sessionInfos: sessionInfos,
filter: filter,
title: title)
}
private func createUserSessionInfo(sessionId: String,
isVerified: Bool = false,
isActive: Bool = true,
isCurrent: Bool = false) -> UserSessionInfo {
UserSessionInfo(id: sessionId,
name: "iOS",
deviceType: .mobile,
verificationState: .unverified,
verificationState: isVerified ? .verified : .unverified,
lastSeenIP: "10.0.0.10",
lastSeenTimestamp: Date().timeIntervalSince1970 - 100,
applicationName: nil,
@@ -79,7 +156,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
lastSeenIPLocation: nil,
clientName: nil,
clientVersion: nil,
isActive: true,
isCurrent: true)
isActive: isActive,
isCurrent: isCurrent)
}
}
@@ -0,0 +1,40 @@
//
// 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
enum UserOtherSessionsFilter: Identifiable, Equatable, CaseIterable {
var id: Self { self }
case all
case verified
case unverified
case inactive
}
extension UserOtherSessionsFilter {
var menuLocalizedName: String {
switch self {
case .all:
return VectorL10n.userOtherSessionFilterMenuAll
case .verified:
return VectorL10n.userOtherSessionFilterMenuVerified
case .unverified:
return VectorL10n.userOtherSessionFilterMenuUnverified
case .inactive:
return VectorL10n.userOtherSessionFilterMenuInactive
}
}
}
@@ -31,18 +31,26 @@ enum UserOtherSessionsViewModelResult: Equatable {
// MARK: View
struct UserOtherSessionsViewState: BindableState, Equatable {
var bindings: UserOtherSessionsBindings
let title: String
var sections: [UserOtherSessionsSection]
}
struct UserOtherSessionsBindings: Equatable {
var filter: UserOtherSessionsFilter
}
enum UserOtherSessionsSection: Hashable, Identifiable {
var id: Self {
self
}
case sessionItems(header: UserOtherSessionsHeaderViewData, items: [UserSessionListItemViewData])
case emptySessionItems(header: UserOtherSessionsHeaderViewData, title: String)
}
enum UserOtherSessionsViewAction {
case userOtherSessionSelected(sessionId: String)
case filterWasChanged
case clearFilter
}
@@ -18,22 +18,18 @@ import SwiftUI
typealias UserOtherSessionsViewModelType = StateStoreViewModel<UserOtherSessionsViewState, UserOtherSessionsViewAction>
enum OtherUserSessionsFilter {
case all
case inactive
case unverified
}
class UserOtherSessionsViewModel: UserOtherSessionsViewModelType, UserOtherSessionsViewModelProtocol {
var completion: ((UserOtherSessionsViewModelResult) -> Void)?
private let sessionInfos: [UserSessionInfo]
init(sessionInfos: [UserSessionInfo],
filter: OtherUserSessionsFilter,
filter: UserOtherSessionsFilter,
title: String) {
self.sessionInfos = sessionInfos
super.init(initialViewState: UserOtherSessionsViewState(title: title, sections: []))
updateViewState(sessionInfos: sessionInfos, filter: filter)
super.init(initialViewState: UserOtherSessionsViewState(bindings: UserOtherSessionsBindings(filter: filter),
title: title,
sections: []))
updateViewState()
}
// MARK: - Public
@@ -46,18 +42,29 @@ class UserOtherSessionsViewModel: UserOtherSessionsViewModelType, UserOtherSessi
return
}
completion?(.showUserSessionOverview(sessionInfo: session))
case .filterWasChanged:
updateViewState()
case .clearFilter:
state.bindings.filter = .all
updateViewState()
}
}
// MARK: - Private
private func updateViewState(sessionInfos: [UserSessionInfo], filter: OtherUserSessionsFilter) {
let sectionItems = createSectionItems(sessionInfos: sessionInfos, filter: filter)
let sectionHeader = createHeaderData(filter: filter)
state.sections = [.sessionItems(header: sectionHeader, items: sectionItems)]
private func updateViewState() {
let sectionItems = createSectionItems(sessionInfos: sessionInfos, filter: state.bindings.filter)
let sectionHeader = createHeaderData(filter: state.bindings.filter)
if sectionItems.isEmpty {
state.sections = [.emptySessionItems(header: sectionHeader,
title: noSessionsTitle(filter: state.bindings.filter))]
} else {
state.sections = [.sessionItems(header: sectionHeader,
items: sectionItems)]
}
}
private func createSectionItems(sessionInfos: [UserSessionInfo], filter: OtherUserSessionsFilter) -> [UserSessionListItemViewData] {
private func createSectionItems(sessionInfos: [UserSessionInfo], filter: UserOtherSessionsFilter) -> [UserSessionListItemViewData] {
filterSessions(sessionInfos: sessionInfos, by: filter)
.map {
UserSessionListItemViewDataFactory().create(from: $0,
@@ -65,7 +72,7 @@ class UserOtherSessionsViewModel: UserOtherSessionsViewModelType, UserOtherSessi
}
}
private func filterSessions(sessionInfos: [UserSessionInfo], by filter: OtherUserSessionsFilter) -> [UserSessionInfo] {
private func filterSessions(sessionInfos: [UserSessionInfo], by filter: UserOtherSessionsFilter) -> [UserSessionInfo] {
switch filter {
case .all:
return sessionInfos.filter { !$0.isCurrent }
@@ -73,23 +80,43 @@ class UserOtherSessionsViewModel: UserOtherSessionsViewModelType, UserOtherSessi
return sessionInfos.filter { !$0.isActive }
case .unverified:
return sessionInfos.filter { $0.verificationState != .verified }
case .verified:
return sessionInfos.filter { $0.verificationState == .verified }
}
}
private func createHeaderData(filter: OtherUserSessionsFilter) -> UserOtherSessionsHeaderViewData {
private func createHeaderData(filter: UserOtherSessionsFilter) -> UserOtherSessionsHeaderViewData {
switch filter {
case .all:
return UserOtherSessionsHeaderViewData(title: nil,
subtitle: VectorL10n.userSessionsOverviewOtherSessionsSectionInfo,
iconName: nil)
case .inactive:
return UserOtherSessionsHeaderViewData(title: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveTitle,
return UserOtherSessionsHeaderViewData(title: VectorL10n.userOtherSessionFilterMenuInactive,
subtitle: VectorL10n.userSessionsOverviewSecurityRecommendationsInactiveInfo,
iconName: Asset.Images.userOtherSessionsInactive.name)
case .unverified:
return UserOtherSessionsHeaderViewData(title: VectorL10n.userSessionsOverviewSecurityRecommendationsUnverifiedTitle,
return UserOtherSessionsHeaderViewData(title: VectorL10n.userSessionUnverifiedShort,
subtitle: VectorL10n.userOtherSessionUnverifiedSessionsHeaderSubtitle,
iconName: Asset.Images.userOtherSessionsUnverified.name)
case .verified:
return UserOtherSessionsHeaderViewData(title: VectorL10n.userOtherSessionFilterMenuVerified,
subtitle: VectorL10n.userOtherSessionVerifiedSessionsHeaderSubtitle,
iconName: Asset.Images.userOtherSessionsVerified.name)
}
}
private func noSessionsTitle(filter: UserOtherSessionsFilter) -> String {
switch filter {
case .all:
assertionFailure("The view is not intended to be displayed without any session")
return ""
case .verified:
return VectorL10n.userOtherSessionNoVerifiedSessions
case .unverified:
return VectorL10n.userOtherSessionNoUnverifiedSessions
case .inactive:
return VectorL10n.userOtherSessionNoInactiveSessions
}
}
}
@@ -27,12 +27,32 @@ struct UserOtherSessions: View {
switch section {
case let .sessionItems(header: header, items: items):
createSessionItemsSection(header: header, items: items)
case let .emptySessionItems(header: header, title: title):
createEmptySessionsItemsSection(header: header, title: title)
}
}
}
.background(theme.colors.system.ignoresSafeArea())
.frame(maxHeight: .infinity)
.navigationTitle(viewModel.viewState.title)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Picker("", selection: $viewModel.filter) {
ForEach(UserOtherSessionsFilter.allCases) { filter in
Text(filter.menuLocalizedName).tag(filter)
}
}
.labelsHidden()
.onChange(of: viewModel.filter) { _ in
viewModel.send(viewAction: .filterWasChanged)
}
} label: {
Image(viewModel.filter == .all ? Asset.Images.userOtherSessionsFilter.name : Asset.Images.userOtherSessionsFilterSelected.name)
}
.accessibilityLabel(VectorL10n.userOtherSessionFilter)
}
}
}
private func createSessionItemsSection(header: UserOtherSessionsHeaderViewData, items: [UserSessionListItemViewData]) -> some View {
@@ -46,11 +66,43 @@ struct UserOtherSessions: View {
}
.background(theme.colors.background)
} header: {
UserOtherSessionsHeaderView(viewData: header)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.top, 24.0)
headerView(header: header)
}
}
private func createEmptySessionsItemsSection(header: UserOtherSessionsHeaderViewData, title: String) -> some View {
SwiftUI.Section {
VStack {
Text(title)
.font(theme.fonts.footnote)
.foregroundColor(theme.colors.primaryContent)
.padding(.bottom, 20)
Button {
viewModel.send(viewAction: .clearFilter)
} label: {
VStack(spacing: 0) {
SeparatorLine()
Text(VectorL10n.userOtherSessionClearFilter)
.font(theme.fonts.body)
.foregroundColor(theme.colors.accent)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.vertical, 11)
SeparatorLine()
}
.background(theme.colors.background)
}
}
} header: {
headerView(header: header)
}
}
private func headerView(header: UserOtherSessionsHeaderViewData) -> some View {
UserOtherSessionsHeaderView(viewData: header)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.top, 24.0)
}
}
// MARK: - Previews
@@ -94,7 +94,7 @@ final class UserSessionsOverviewCoordinator: Coordinator, Presentable {
loadingIndicator = nil
}
private func showOtherSessions(sessionInfos: [UserSessionInfo], filterBy filter: OtherUserSessionsFilter) {
private func showOtherSessions(sessionInfos: [UserSessionInfo], filterBy filter: UserOtherSessionsFilter) {
completion?(.openOtherSessions(sessionInfos: sessionInfos, filter: filter))
}
@@ -23,14 +23,14 @@ enum UserSessionsOverviewCoordinatorResult {
case renameSession(UserSessionInfo)
case logoutOfSession(UserSessionInfo)
case openSessionOverview(sessionInfo: UserSessionInfo)
case openOtherSessions(sessionInfos: [UserSessionInfo], filter: OtherUserSessionsFilter)
case openOtherSessions(sessionInfos: [UserSessionInfo], filter: UserOtherSessionsFilter)
case linkDevice
}
// MARK: View model
enum UserSessionsOverviewViewModelResult: Equatable {
case showOtherSessions(sessionInfos: [UserSessionInfo], filter: OtherUserSessionsFilter)
case showOtherSessions(sessionInfos: [UserSessionInfo], filter: UserOtherSessionsFilter)
case verifyCurrentSession
case renameSession(UserSessionInfo)
case logoutOfSession(UserSessionInfo)
@@ -108,7 +108,7 @@ class UserSessionsOverviewViewModel: UserSessionsOverviewViewModelType, UserSess
}
}
private func showSessions(filteredBy filter: OtherUserSessionsFilter) {
private func showSessions(filteredBy filter: UserOtherSessionsFilter) {
completion?(.showOtherSessions(sessionInfos: userSessionsOverviewService.sessionInfos,
filter: filter))
}