Skip to content

Commit

Permalink
Merge pull request #6789 from vector-im/gil/6787-Add_support_for_MSC3881
Browse files Browse the repository at this point in the history
Added support for MSC3881
  • Loading branch information
gileluard authored Oct 4, 2022
2 parents b3e9b83 + 0ecbf82 commit c8b1657
Show file tree
Hide file tree
Showing 25 changed files with 489 additions and 59 deletions.
2 changes: 2 additions & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2388,6 +2388,8 @@ To enable access, tap Settings> Location and select Always";
"user_session_verified_additional_info" = "Your current session is ready for secure messaging.";
"user_session_unverified_additional_info" = "Verify your current session for enhanced secure messaging.";

"user_session_push_notifications" = "Push notifications";
"user_session_push_notifications_message" = "When turned on, this session will receive push notifications.";

// First item is client name and second item is session display name
"user_session_name" = "%@: %@";
Expand Down
8 changes: 8 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8563,6 +8563,14 @@ public class VectorL10n: NSObject {
public static var userSessionOverviewSessionTitle: String {
return VectorL10n.tr("Vector", "user_session_overview_session_title")
}
/// Push notifications
public static var userSessionPushNotifications: String {
return VectorL10n.tr("Vector", "user_session_push_notifications")
}
/// When turned on, this session will receive push notifications.
public static var userSessionPushNotificationsMessage: String {
return VectorL10n.tr("Vector", "user_session_push_notifications_message")
}
/// Unverified session
public static var userSessionUnverified: String {
return VectorL10n.tr("Vector", "user_session_unverified")
Expand Down
8 changes: 4 additions & 4 deletions Riot/Managers/Call/CallPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -496,12 +496,12 @@ class CallPresenter: NSObject {
#if canImport(JitsiMeetSDK)
JMCallKitProxy.removeListener(self)

guard let session = sessions.first else {
guard let sessionInfo = sessions.first else {
return
}

if let widgetEventsListener = widgetEventsListener {
session.removeListener(widgetEventsListener)
sessionInfo.removeListener(widgetEventsListener)
}
widgetEventsListener = nil
#endif
Expand Down Expand Up @@ -872,11 +872,11 @@ extension CallPresenter: JMCallKitListener {

}

func providerDidActivateAudioSession(session: AVAudioSession) {
func providerDidActivateAudioSession(sessionInfo: AVAudioSession) {

}

func providerDidDeactivateAudioSession(session: AVAudioSession) {
func providerDidDeactivateAudioSession(sessionInfo: AVAudioSession) {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ struct UserSessionCardViewPreview: View {
let viewData: UserSessionCardViewData

init(isCurrent: Bool = false) {
let session = UserSessionInfo(id: "alice",
let sessionInfo = UserSessionInfo(id: "alice",
name: "iOS",
deviceType: .mobile,
isVerified: false,
Expand All @@ -153,7 +153,7 @@ struct UserSessionCardViewPreview: View {
deviceName: "My iPhone",
isActive: true,
isCurrent: isCurrent)
viewData = UserSessionCardViewData(session: session)
viewData = UserSessionCardViewData(sessionInfo: sessionInfo)
}

var body: some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ struct UserSessionCardViewData {
}

extension UserSessionCardViewData {
init(session: UserSessionInfo) {
self.init(sessionId: session.id,
sessionDisplayName: session.name,
deviceType: session.deviceType,
isVerified: session.isVerified,
lastActivityTimestamp: session.lastSeenTimestamp,
lastSeenIP: session.lastSeenIP,
isCurrentSessionDisplayMode: session.isCurrent)
init(sessionInfo: UserSessionInfo) {
self.init(sessionId: sessionInfo.id,
sessionDisplayName: sessionInfo.name,
deviceType: sessionInfo.deviceType,
isVerified: sessionInfo.isVerified,
lastActivityTimestamp: sessionInfo.lastSeenTimestamp,
lastSeenIP: sessionInfo.lastSeenIP,
isCurrentSessionDisplayMode: sessionInfo.isCurrent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,37 +53,37 @@ final class UserSessionsFlowCoordinator: Coordinator, Presentable {
coordinator.completion = { [weak self] result in
guard let self = self else { return }
switch result {
case let .openSessionOverview(session: session):
self.openSessionOverview(session: session)
case let .openSessionOverview(sessionInfo: sessionInfo):
self.openSessionOverview(sessionInfo: sessionInfo)
}
}
return coordinator
}

private func openSessionDetails(session: UserSessionInfo) {
let coordinator = createUserSessionDetailsCoordinator(session: session)
private func openSessionDetails(sessionInfo: UserSessionInfo) {
let coordinator = createUserSessionDetailsCoordinator(sessionInfo: sessionInfo)
pushScreen(with: coordinator)
}

private func createUserSessionDetailsCoordinator(session: UserSessionInfo) -> UserSessionDetailsCoordinator {
let parameters = UserSessionDetailsCoordinatorParameters(session: session)
private func createUserSessionDetailsCoordinator(sessionInfo: UserSessionInfo) -> UserSessionDetailsCoordinator {
let parameters = UserSessionDetailsCoordinatorParameters(session: sessionInfo)
return UserSessionDetailsCoordinator(parameters: parameters)
}

private func openSessionOverview(session: UserSessionInfo) {
let coordinator = createUserSessionOverviewCoordinator(session: session)
private func openSessionOverview(sessionInfo: UserSessionInfo) {
let coordinator = createUserSessionOverviewCoordinator(sessionInfo: sessionInfo)
coordinator.completion = { [weak self] result in
guard let self = self else { return }
switch result {
case let .openSessionDetails(session: session):
self.openSessionDetails(session: session)
case let .openSessionDetails(sessionInfo: sessionInfo):
self.openSessionDetails(sessionInfo: sessionInfo)
}
}
pushScreen(with: coordinator)
}

private func createUserSessionOverviewCoordinator(session: UserSessionInfo) -> UserSessionOverviewCoordinator {
let parameters = UserSessionOverviewCoordinatorParameters(session: session)
private func createUserSessionOverviewCoordinator(sessionInfo: UserSessionInfo) -> UserSessionOverviewCoordinator {
let parameters = UserSessionOverviewCoordinatorParameters(session: self.parameters.session, sessionInfo: sessionInfo)
return UserSessionOverviewCoordinator(parameters: parameters)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import CommonKit
import SwiftUI

struct UserSessionOverviewCoordinatorParameters {
let session: UserSessionInfo
let session: MXSession
let sessionInfo: UserSessionInfo
}

final class UserSessionOverviewCoordinator: Coordinator, Presentable {
Expand All @@ -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))

Expand All @@ -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))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,27 @@ 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
}

/// 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 session: UserSessionInfo
let service: UserSessionOverviewServiceProtocol
switch self {
case .currentSession:
session = UserSessionInfo(id: "alice",
Expand All @@ -56,6 +63,7 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
deviceName: "My iPhone",
isActive: true,
isCurrent: true)
service = MockUserSessionOverviewService()
case .otherSession:
session = UserSessionInfo(id: "1",
name: "macOS",
Expand All @@ -72,9 +80,44 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
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(session: session)
let viewModel = UserSessionOverviewViewModel(sessionInfo: session, service: service)
// can simulate service and viewModel actions here if needs be.
return ([viewModel], AnyView(UserSessionOverview(viewModel: viewModel.context)))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Loading

0 comments on commit c8b1657

Please sign in to comment.