mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-23 10:02:46 +02:00
Merge branch 'develop' into aleksandrs/6786_inactive_sessions_screen
# Conflicts: # Riot/Assets/en.lproj/Vector.strings # Riot/Generated/Strings.swift # RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift # RiotSwiftUI/Modules/UserSessions/UserSessionOverview/Test/Unit/UserSessionOverviewViewModelTests.swift # RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Coordinator/UserSessionsOverviewCoordinator.swift # RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/Mock/MockUserSessionsOverviewService.swift # RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/UserSessionsOverviewModels.swift # RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewData.swift
This commit is contained in:
+6
-4
@@ -18,7 +18,8 @@ import CommonKit
|
||||
import SwiftUI
|
||||
|
||||
struct UserSessionOverviewCoordinatorParameters {
|
||||
let session: UserSessionInfo
|
||||
let session: MXSession
|
||||
let sessionInfo: UserSessionInfo
|
||||
}
|
||||
|
||||
final class UserSessionOverviewCoordinator: Coordinator, Presentable {
|
||||
@@ -40,7 +41,8 @@ final class UserSessionOverviewCoordinator: Coordinator, Presentable {
|
||||
init(parameters: UserSessionOverviewCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = UserSessionOverviewViewModel(session: parameters.session)
|
||||
let service = UserSessionOverviewService(session: parameters.session, sessionInfo: parameters.sessionInfo)
|
||||
viewModel = UserSessionOverviewViewModel(sessionInfo: parameters.sessionInfo, service: service)
|
||||
|
||||
hostingController = VectorHostingController(rootView: UserSessionOverview(viewModel: viewModel.context))
|
||||
|
||||
@@ -57,8 +59,8 @@ final class UserSessionOverviewCoordinator: Coordinator, Presentable {
|
||||
switch result {
|
||||
case .verifyCurrentSession:
|
||||
break // TODO:
|
||||
case let .showSessionDetails(session: session):
|
||||
self.completion?(.openSessionDetails(session: session))
|
||||
case let .showSessionDetails(sessionInfo: sessionInfo):
|
||||
self.completion?(.openSessionDetails(sessionInfo: sessionInfo))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+78
-22
@@ -25,7 +25,9 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
|
||||
// mock that screen.
|
||||
case currentSession
|
||||
case otherSession
|
||||
|
||||
case sessionWithPushNotifications(enabled: Bool)
|
||||
case remotelyTogglingPushersNotAvailable
|
||||
|
||||
/// The associated screen
|
||||
var screenType: Any.Type {
|
||||
UserSessionOverview.self
|
||||
@@ -33,35 +35,89 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
|
||||
|
||||
/// A list of screen state definitions
|
||||
static var allCases: [MockUserSessionOverviewScreenState] {
|
||||
[.currentSession, .otherSession]
|
||||
[.currentSession,
|
||||
.otherSession,
|
||||
.sessionWithPushNotifications(enabled: true),
|
||||
.sessionWithPushNotifications(enabled: false),
|
||||
.remotelyTogglingPushersNotAvailable]
|
||||
}
|
||||
|
||||
/// Generate the view struct for the screen state.
|
||||
var screenView: ([Any], AnyView) {
|
||||
let viewModel: UserSessionOverviewViewModel
|
||||
let session: UserSessionInfo
|
||||
let service: UserSessionOverviewServiceProtocol
|
||||
switch self {
|
||||
case .currentSession:
|
||||
let session = UserSessionInfo(id: "session",
|
||||
name: "iOS",
|
||||
deviceType: .mobile,
|
||||
isVerified: false,
|
||||
lastSeenIP: "10.0.0.10",
|
||||
lastSeenTimestamp: Date().timeIntervalSince1970 - 100,
|
||||
isActive: true,
|
||||
isCurrent: true)
|
||||
viewModel = UserSessionOverviewViewModel(session: session)
|
||||
session = UserSessionInfo(id: "alice",
|
||||
name: "iOS",
|
||||
deviceType: .mobile,
|
||||
isVerified: false,
|
||||
lastSeenIP: "10.0.0.10",
|
||||
lastSeenTimestamp: nil,
|
||||
applicationName: "Element iOS",
|
||||
applicationVersion: "1.0.0",
|
||||
applicationURL: nil,
|
||||
deviceModel: nil,
|
||||
deviceOS: "iOS 15.5",
|
||||
lastSeenIPLocation: nil,
|
||||
deviceName: "My iPhone",
|
||||
isActive: true,
|
||||
isCurrent: true)
|
||||
service = MockUserSessionOverviewService()
|
||||
case .otherSession:
|
||||
let session = UserSessionInfo(id: "session",
|
||||
name: "Mac",
|
||||
deviceType: .desktop,
|
||||
isVerified: true,
|
||||
lastSeenIP: "10.0.0.10",
|
||||
lastSeenTimestamp: Date().timeIntervalSince1970 - 100,
|
||||
isActive: true,
|
||||
isCurrent: false)
|
||||
viewModel = UserSessionOverviewViewModel(session: session)
|
||||
session = UserSessionInfo(id: "1",
|
||||
name: "macOS",
|
||||
deviceType: .desktop,
|
||||
isVerified: true,
|
||||
lastSeenIP: "1.0.0.1",
|
||||
lastSeenTimestamp: Date().timeIntervalSince1970 - 130_000,
|
||||
applicationName: "Element MacOS",
|
||||
applicationVersion: "1.0.0",
|
||||
applicationURL: nil,
|
||||
deviceModel: nil,
|
||||
deviceOS: "macOS 12.5.1",
|
||||
lastSeenIPLocation: nil,
|
||||
deviceName: "My Mac",
|
||||
isActive: false,
|
||||
isCurrent: false)
|
||||
service = MockUserSessionOverviewService()
|
||||
case .sessionWithPushNotifications(let enabled):
|
||||
session = UserSessionInfo(id: "1",
|
||||
name: "macOS",
|
||||
deviceType: .desktop,
|
||||
isVerified: true,
|
||||
lastSeenIP: "1.0.0.1",
|
||||
lastSeenTimestamp: Date().timeIntervalSince1970 - 130_000,
|
||||
applicationName: "Element MacOS",
|
||||
applicationVersion: "1.0.0",
|
||||
applicationURL: nil,
|
||||
deviceModel: nil,
|
||||
deviceOS: "macOS 12.5.1",
|
||||
lastSeenIPLocation: nil,
|
||||
deviceName: "My Mac",
|
||||
isActive: false,
|
||||
isCurrent: false)
|
||||
service = MockUserSessionOverviewService(pusherEnabled: enabled)
|
||||
case .remotelyTogglingPushersNotAvailable:
|
||||
session = UserSessionInfo(id: "1",
|
||||
name: "macOS",
|
||||
deviceType: .desktop,
|
||||
isVerified: true,
|
||||
lastSeenIP: "1.0.0.1",
|
||||
lastSeenTimestamp: Date().timeIntervalSince1970 - 130_000,
|
||||
applicationName: "Element MacOS",
|
||||
applicationVersion: "1.0.0",
|
||||
applicationURL: nil,
|
||||
deviceModel: nil,
|
||||
deviceOS: "macOS 12.5.1",
|
||||
lastSeenIPLocation: nil,
|
||||
deviceName: "My Mac",
|
||||
isActive: false,
|
||||
isCurrent: false)
|
||||
service = MockUserSessionOverviewService(pusherEnabled: true, remotelyTogglingPushersAvailable: false)
|
||||
}
|
||||
|
||||
|
||||
let viewModel = UserSessionOverviewViewModel(sessionInfo: session, service: service)
|
||||
// can simulate service and viewModel actions here if needs be.
|
||||
return ([viewModel], AnyView(UserSessionOverview(viewModel: viewModel.context)))
|
||||
}
|
||||
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// 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 Combine
|
||||
import MatrixSDK
|
||||
|
||||
class UserSessionOverviewService: UserSessionOverviewServiceProtocol {
|
||||
|
||||
// MARK: - Members
|
||||
|
||||
private(set) var pusherEnabledSubject: CurrentValueSubject<Bool?, Never>
|
||||
private(set) var remotelyTogglingPushersAvailableSubject: CurrentValueSubject<Bool, Never>
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private let session: MXSession
|
||||
private let sessionInfo: UserSessionInfo
|
||||
private var pusher: MXPusher?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, sessionInfo: UserSessionInfo) {
|
||||
self.session = session
|
||||
self.sessionInfo = sessionInfo
|
||||
self.pusherEnabledSubject = CurrentValueSubject(nil)
|
||||
self.remotelyTogglingPushersAvailableSubject = CurrentValueSubject(false)
|
||||
|
||||
checkServerVersions { [weak self] in
|
||||
self?.checkPusher()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserSessionOverviewServiceProtocol
|
||||
|
||||
func togglePushNotifications() {
|
||||
guard let pusher = pusher, let enabled = pusher.enabled?.boolValue, self.remotelyTogglingPushersAvailableSubject.value else {
|
||||
return
|
||||
}
|
||||
|
||||
let data = pusher.data.jsonDictionary() as? [String: Any] ?? [:]
|
||||
|
||||
self.session.matrixRestClient.setPusher(pushKey: pusher.pushkey,
|
||||
kind: MXPusherKind(value: pusher.kind),
|
||||
appId: pusher.appId,
|
||||
appDisplayName:pusher.appDisplayName,
|
||||
deviceDisplayName: pusher.deviceDisplayName,
|
||||
profileTag: pusher.profileTag ?? "",
|
||||
lang: pusher.lang,
|
||||
data: data,
|
||||
append: false,
|
||||
enabled: !enabled) { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
|
||||
switch response {
|
||||
case .success:
|
||||
self.checkPusher()
|
||||
case .failure(let error):
|
||||
MXLog.warning("[UserSessionOverviewService] togglePushNotifications failed due to error: \(error)")
|
||||
self.pusherEnabledSubject.send(enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func checkServerVersions(_ completion: @escaping () -> Void) {
|
||||
session.supportedMatrixVersions { [weak self] response in
|
||||
switch response {
|
||||
case .success(let versions):
|
||||
self?.remotelyTogglingPushersAvailableSubject.send(versions.supportsRemotelyTogglingPushNotifications)
|
||||
case .failure(let error):
|
||||
MXLog.warning("[UserSessionOverviewService] checkServerVersions failed due to error: \(error)")
|
||||
}
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
private func checkPusher() {
|
||||
session.matrixRestClient.pushers { [weak self] response in
|
||||
switch response {
|
||||
case .success(let pushers):
|
||||
self?.check(pushers: pushers)
|
||||
case .failure(let error):
|
||||
MXLog.warning("[UserSessionOverviewService] checkPusher failed due to error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func check(pushers: [MXPusher]) {
|
||||
for pusher in pushers where pusher.deviceId == sessionInfo.id {
|
||||
self.pusher = pusher
|
||||
|
||||
guard let enabled = pusher.enabled else {
|
||||
// For backwards compatibility, any pusher without an enabled field should be treated as if enabled is false
|
||||
pusherEnabledSubject.send(false)
|
||||
return
|
||||
}
|
||||
|
||||
pusherEnabledSubject.send(enabled.boolValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// 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 Combine
|
||||
import Foundation
|
||||
|
||||
class MockUserSessionOverviewService: UserSessionOverviewServiceProtocol {
|
||||
|
||||
|
||||
var pusherEnabledSubject: CurrentValueSubject<Bool?, Never>
|
||||
var remotelyTogglingPushersAvailableSubject: CurrentValueSubject<Bool, Never>
|
||||
|
||||
init(pusherEnabled: Bool? = nil, remotelyTogglingPushersAvailable: Bool = true) {
|
||||
self.pusherEnabledSubject = CurrentValueSubject(pusherEnabled)
|
||||
self.remotelyTogglingPushersAvailableSubject = CurrentValueSubject(remotelyTogglingPushersAvailable)
|
||||
}
|
||||
|
||||
func togglePushNotifications() {
|
||||
guard let enabled = pusherEnabledSubject.value, self.remotelyTogglingPushersAvailableSubject.value else {
|
||||
return
|
||||
}
|
||||
|
||||
pusherEnabledSubject.send(!enabled)
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// 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 Combine
|
||||
import Foundation
|
||||
|
||||
protocol UserSessionOverviewServiceProtocol {
|
||||
var pusherEnabledSubject: CurrentValueSubject<Bool?, Never> { get }
|
||||
var remotelyTogglingPushersAvailableSubject: CurrentValueSubject<Bool, Never> { get }
|
||||
|
||||
func togglePushNotifications()
|
||||
}
|
||||
+27
@@ -34,4 +34,31 @@ class UserSessionOverviewUITests: MockScreenTestCase {
|
||||
app.goToScreenWithIdentifier(MockUserSessionOverviewScreenState.currentSession.title)
|
||||
XCTAssertTrue(app.buttons[VectorL10n.userSessionOverviewSessionDetailsButtonTitle].exists)
|
||||
}
|
||||
|
||||
func test_whenSessionOverviewPresented_pusherEnabledToggleExists() {
|
||||
app.goToScreenWithIdentifier(MockUserSessionOverviewScreenState.sessionWithPushNotifications(enabled: true).title)
|
||||
XCTAssertTrue(app.switches["UserSessionOverviewToggleCell"].exists)
|
||||
XCTAssertTrue(app.switches["UserSessionOverviewToggleCell"].isOn)
|
||||
XCTAssertTrue(app.switches["UserSessionOverviewToggleCell"].isEnabled)
|
||||
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionPushNotifications].exists)
|
||||
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionPushNotificationsMessage].exists)
|
||||
}
|
||||
|
||||
func test_whenSessionOverviewPresented_pusherDisabledToggleExists() {
|
||||
app.goToScreenWithIdentifier(MockUserSessionOverviewScreenState.sessionWithPushNotifications(enabled: false).title)
|
||||
XCTAssertTrue(app.switches["UserSessionOverviewToggleCell"].exists)
|
||||
XCTAssertFalse(app.switches["UserSessionOverviewToggleCell"].isOn)
|
||||
XCTAssertTrue(app.switches["UserSessionOverviewToggleCell"].isEnabled)
|
||||
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionPushNotifications].exists)
|
||||
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionPushNotificationsMessage].exists)
|
||||
}
|
||||
|
||||
func test_whenSessionOverviewPresented_pusherEnabledToggleExists_remotelyTogglingPushersAvailable() {
|
||||
app.goToScreenWithIdentifier(MockUserSessionOverviewScreenState.remotelyTogglingPushersNotAvailable.title)
|
||||
XCTAssertTrue(app.switches["UserSessionOverviewToggleCell"].exists)
|
||||
XCTAssertTrue(app.switches["UserSessionOverviewToggleCell"].isOn)
|
||||
XCTAssertFalse(app.switches["UserSessionOverviewToggleCell"].isEnabled)
|
||||
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionPushNotifications].exists)
|
||||
XCTAssertTrue(app.staticTexts[VectorL10n.userSessionPushNotificationsMessage].exists)
|
||||
}
|
||||
}
|
||||
|
||||
+52
-4
@@ -23,8 +23,9 @@ class UserSessionOverviewViewModelTests: XCTestCase {
|
||||
var sut: UserSessionOverviewViewModel!
|
||||
|
||||
func test_whenVerifyCurrentSessionProcessed_completionWithVerifyCurrentSessionCalled() {
|
||||
sut = UserSessionOverviewViewModel(session: createUserSessionInfo())
|
||||
sut = UserSessionOverviewViewModel(sessionInfo: createUserSessionInfo(), service: MockUserSessionOverviewService())
|
||||
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, nil)
|
||||
var modelResult: UserSessionOverviewViewModelResult?
|
||||
sut.completion = { result in
|
||||
modelResult = result
|
||||
@@ -34,17 +35,57 @@ class UserSessionOverviewViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_whenViewSessionDetailsProcessed_completionWithShowSessionDetailsCalled() {
|
||||
let session = createUserSessionInfo()
|
||||
sut = UserSessionOverviewViewModel(session: session)
|
||||
let sessionInfo = createUserSessionInfo()
|
||||
sut = UserSessionOverviewViewModel(sessionInfo: sessionInfo, service: MockUserSessionOverviewService())
|
||||
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, nil)
|
||||
var modelResult: UserSessionOverviewViewModelResult?
|
||||
sut.completion = { result in
|
||||
modelResult = result
|
||||
}
|
||||
sut.process(viewAction: .viewSessionDetails)
|
||||
XCTAssertEqual(modelResult, .showSessionDetails(session: session))
|
||||
XCTAssertEqual(modelResult, .showSessionDetails(sessionInfo: sessionInfo))
|
||||
}
|
||||
|
||||
func test_whenViewSessionDetailsProcessed_toggleAvailablePusher() {
|
||||
let sessionInfo = createUserSessionInfo()
|
||||
let service = MockUserSessionOverviewService(pusherEnabled: true)
|
||||
sut = UserSessionOverviewViewModel(sessionInfo: sessionInfo, service: service)
|
||||
|
||||
XCTAssertTrue(sut.state.remotelyTogglingPushersAvailable)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, true)
|
||||
sut.process(viewAction: .togglePushNotifications)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, false)
|
||||
sut.process(viewAction: .togglePushNotifications)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, true)
|
||||
}
|
||||
|
||||
func test_whenViewSessionDetailsProcessed_toggleNoPusher() {
|
||||
let sessionInfo = createUserSessionInfo()
|
||||
let service = MockUserSessionOverviewService(pusherEnabled: nil)
|
||||
sut = UserSessionOverviewViewModel(sessionInfo: sessionInfo, service: service)
|
||||
|
||||
XCTAssertTrue(sut.state.remotelyTogglingPushersAvailable)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, nil)
|
||||
sut.process(viewAction: .togglePushNotifications)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, nil)
|
||||
sut.process(viewAction: .togglePushNotifications)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, nil)
|
||||
}
|
||||
|
||||
func test_whenViewSessionDetailsProcessed_remotelyTogglingPushersNotAvailable() {
|
||||
let sessionInfo = createUserSessionInfo()
|
||||
let service = MockUserSessionOverviewService(pusherEnabled: true, remotelyTogglingPushersAvailable: false)
|
||||
sut = UserSessionOverviewViewModel(sessionInfo: sessionInfo, service: service)
|
||||
|
||||
XCTAssertFalse(sut.state.remotelyTogglingPushersAvailable)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, true)
|
||||
sut.process(viewAction: .togglePushNotifications)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, true)
|
||||
sut.process(viewAction: .togglePushNotifications)
|
||||
XCTAssertEqual(sut.state.isPusherEnabled, true)
|
||||
}
|
||||
|
||||
private func createUserSessionInfo() -> UserSessionInfo {
|
||||
UserSessionInfo(id: "session",
|
||||
name: "iOS",
|
||||
@@ -52,6 +93,13 @@ class UserSessionOverviewViewModelTests: XCTestCase {
|
||||
isVerified: false,
|
||||
lastSeenIP: "10.0.0.10",
|
||||
lastSeenTimestamp: Date().timeIntervalSince1970 - 100,
|
||||
applicationName: "Element",
|
||||
applicationVersion: "1.9.7",
|
||||
applicationURL: nil,
|
||||
deviceModel: "iPhone XS",
|
||||
deviceOS: "iOS 15.5",
|
||||
lastSeenIPLocation: nil,
|
||||
deviceName: "Mobile",
|
||||
isActive: true,
|
||||
isCurrent: true)
|
||||
}
|
||||
|
||||
+6
-2
@@ -19,13 +19,13 @@ import Foundation
|
||||
// MARK: - Coordinator
|
||||
|
||||
enum UserSessionOverviewCoordinatorResult {
|
||||
case openSessionDetails(session: UserSessionInfo)
|
||||
case openSessionDetails(sessionInfo: UserSessionInfo)
|
||||
}
|
||||
|
||||
// MARK: View model
|
||||
|
||||
enum UserSessionOverviewViewModelResult: Equatable {
|
||||
case showSessionDetails(session: UserSessionInfo)
|
||||
case showSessionDetails(sessionInfo: UserSessionInfo)
|
||||
case verifyCurrentSession
|
||||
}
|
||||
|
||||
@@ -34,9 +34,13 @@ enum UserSessionOverviewViewModelResult: Equatable {
|
||||
struct UserSessionOverviewViewState: BindableState {
|
||||
let cardViewData: UserSessionCardViewData
|
||||
let isCurrentSession: Bool
|
||||
var isPusherEnabled: Bool?
|
||||
var remotelyTogglingPushersAvailable: Bool
|
||||
var showLoadingIndicator: Bool
|
||||
}
|
||||
|
||||
enum UserSessionOverviewViewAction {
|
||||
case verifyCurrentSession
|
||||
case viewSessionDetails
|
||||
case togglePushNotifications
|
||||
}
|
||||
|
||||
+36
-6
@@ -19,18 +19,45 @@ import SwiftUI
|
||||
typealias UserSessionOverviewViewModelType = StateStoreViewModel<UserSessionOverviewViewState, UserSessionOverviewViewAction>
|
||||
|
||||
class UserSessionOverviewViewModel: UserSessionOverviewViewModelType, UserSessionOverviewViewModelProtocol {
|
||||
private let session: UserSessionInfo
|
||||
private let sessionInfo: UserSessionInfo
|
||||
private let service: UserSessionOverviewServiceProtocol
|
||||
|
||||
var completion: ((UserSessionOverviewViewModelResult) -> Void)?
|
||||
|
||||
init(session: UserSessionInfo) {
|
||||
self.session = session
|
||||
// MARK: - Setup
|
||||
|
||||
init(sessionInfo: UserSessionInfo, service: UserSessionOverviewServiceProtocol) {
|
||||
self.sessionInfo = sessionInfo
|
||||
self.service = service
|
||||
|
||||
let cardViewData = UserSessionCardViewData(session: session)
|
||||
let state = UserSessionOverviewViewState(cardViewData: cardViewData, isCurrentSession: session.isCurrent)
|
||||
let cardViewData = UserSessionCardViewData(sessionInfo: sessionInfo)
|
||||
let state = UserSessionOverviewViewState(cardViewData: cardViewData,
|
||||
isCurrentSession: sessionInfo.isCurrent,
|
||||
isPusherEnabled: service.pusherEnabledSubject.value,
|
||||
remotelyTogglingPushersAvailable: service.remotelyTogglingPushersAvailableSubject.value,
|
||||
showLoadingIndicator: false)
|
||||
super.init(initialViewState: state)
|
||||
|
||||
startObservingService()
|
||||
}
|
||||
|
||||
private func startObservingService() {
|
||||
service
|
||||
.pusherEnabledSubject
|
||||
.sink(receiveValue: { [weak self] pushEnabled in
|
||||
self?.state.showLoadingIndicator = false
|
||||
self?.state.isPusherEnabled = pushEnabled
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
|
||||
service
|
||||
.remotelyTogglingPushersAvailableSubject
|
||||
.sink(receiveValue: { [weak self] remotelyTogglingPushersAvailable in
|
||||
self?.state.remotelyTogglingPushersAvailable = remotelyTogglingPushersAvailable
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: UserSessionOverviewViewAction) {
|
||||
@@ -38,7 +65,10 @@ class UserSessionOverviewViewModel: UserSessionOverviewViewModelType, UserSessio
|
||||
case .verifyCurrentSession:
|
||||
completion?(.verifyCurrentSession)
|
||||
case .viewSessionDetails:
|
||||
completion?(.showSessionDetails(session: session))
|
||||
completion?(.showSessionDetails(sessionInfo: sessionInfo))
|
||||
case .togglePushNotifications:
|
||||
self.state.showLoadingIndicator = true
|
||||
service.togglePushNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-3
@@ -26,7 +26,7 @@ struct UserSessionOverview: View {
|
||||
UserSessionCardView(viewData: viewModel.viewState.cardViewData, onVerifyAction: { _ in
|
||||
viewModel.send(viewAction: .verifyCurrentSession)
|
||||
},
|
||||
onViewDetailsAction: { _ in
|
||||
onViewDetailsAction: { _ in
|
||||
viewModel.send(viewAction: .viewSessionDetails)
|
||||
})
|
||||
.padding(16)
|
||||
@@ -34,13 +34,21 @@ struct UserSessionOverview: View {
|
||||
UserSessionOverviewDisclosureCell(title: VectorL10n.userSessionOverviewSessionDetailsButtonTitle, onBackgroundTap: {
|
||||
viewModel.send(viewAction: .viewSessionDetails)
|
||||
})
|
||||
if let enabled = viewModel.viewState.isPusherEnabled {
|
||||
UserSessionOverviewToggleCell(title: VectorL10n.userSessionPushNotifications,
|
||||
message: VectorL10n.userSessionPushNotificationsMessage,
|
||||
isOn: enabled, isEnabled: viewModel.viewState.remotelyTogglingPushersAvailable) {
|
||||
viewModel.send(viewAction: .togglePushNotifications)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.background(theme.colors.system.ignoresSafeArea())
|
||||
.frame(maxHeight: .infinity)
|
||||
.waitOverlay(show: viewModel.viewState.showLoadingIndicator, allowUserInteraction: false)
|
||||
.navigationTitle(viewModel.viewState.isCurrentSession ?
|
||||
VectorL10n.userSessionOverviewCurrentSessionTitle :
|
||||
VectorL10n.userSessionOverviewSessionTitle)
|
||||
VectorL10n.userSessionOverviewCurrentSessionTitle :
|
||||
VectorL10n.userSessionOverviewSessionTitle)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ struct UserSessionOverviewDisclosureCell: View {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Image(Asset.Images.chevron.name)
|
||||
}
|
||||
.padding(.vertical, 12)
|
||||
.padding(.vertical, 15)
|
||||
.padding(.horizontal, 16)
|
||||
SeparatorLine()
|
||||
}
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// 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 SwiftUI
|
||||
|
||||
struct UserSessionOverviewToggleCell: View {
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
let title: String
|
||||
let message: String?
|
||||
let isOn: Bool
|
||||
let isEnabled: Bool
|
||||
var onBackgroundTap: (() -> Void)?
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
Button(action: {
|
||||
guard isEnabled else { return }
|
||||
onBackgroundTap?()
|
||||
}) {
|
||||
VStack(spacing: 0) {
|
||||
SeparatorLine()
|
||||
Toggle(isOn: .constant(isOn)) {
|
||||
Text(title)
|
||||
.font(theme.fonts.body)
|
||||
.foregroundColor(theme.colors.primaryContent)
|
||||
.opacity(isEnabled ? 1 : 0.3)
|
||||
}
|
||||
.disabled(!isEnabled)
|
||||
.allowsHitTesting(false)
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 16)
|
||||
.accessibilityIdentifier("UserSessionOverviewToggleCell")
|
||||
SeparatorLine()
|
||||
}
|
||||
.background(theme.colors.background)
|
||||
}
|
||||
.disabled(!isEnabled)
|
||||
if let message = message {
|
||||
Text(message)
|
||||
.multilineTextAlignment(.leading)
|
||||
.font(theme.fonts.footnote)
|
||||
.foregroundColor(theme.colors.secondaryContent)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UserSessionOverviewToggleCell_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
preview
|
||||
.theme(.light)
|
||||
.preferredColorScheme(.light)
|
||||
preview
|
||||
.theme(.dark)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
|
||||
static var preview: some View {
|
||||
VStack {
|
||||
UserSessionOverviewToggleCell(title: "Title", message: nil, isOn: true, isEnabled: true)
|
||||
UserSessionOverviewToggleCell(title: "Title", message: nil, isOn: false, isEnabled: true)
|
||||
UserSessionOverviewToggleCell(title: "Title", message: "some very long message text in order to test the multine alignment", isOn: true, isEnabled: true)
|
||||
UserSessionOverviewToggleCell(title: "Title", message: nil, isOn: true, isEnabled: false)
|
||||
UserSessionOverviewToggleCell(title: "Title", message: nil, isOn: false, isEnabled: false)
|
||||
UserSessionOverviewToggleCell(title: "Title", message: "some very long message text in order to test the multine alignment", isOn: true, isEnabled: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user