Notifications (#275)

This commit is contained in:
ismailgulek
2022-11-21 19:37:13 +03:00
committed by GitHub
parent 9f3ed6ca7b
commit d389ce7ad7
74 changed files with 2459 additions and 324 deletions
@@ -1,24 +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
extension FileManager {
/// The URL of the primary app group container.
@objc var appGroupContainerURL: URL? {
containerURL(forSecurityApplicationGroupIdentifier: ElementInfoPlist.appGroupIdentifier)
}
}
@@ -1,85 +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 KeychainAccess
class KeychainController: KeychainControllerProtocol {
private let keychain: Keychain
init(identifier: String) {
keychain = Keychain(service: identifier)
}
func setRestorationToken(_ restorationToken: RestorationToken, forUsername username: String) {
do {
let tokenData = try JSONEncoder().encode(restorationToken)
try keychain.set(tokenData, key: username)
} catch {
MXLog.error("Failed storing user restore token with error: \(error)")
}
}
func restorationTokenForUsername(_ username: String) -> RestorationToken? {
do {
guard let tokenData = try keychain.getData(username) else {
return nil
}
// Handle the previous restoration token format as we don't want users to have to log in again
// It will automatically be updated to the new version after login
if let legacyRestorationToken = try? JSONDecoder().decode(LegacyRestorationToken.self, from: tokenData) {
return .init(session: .init(accessToken: legacyRestorationToken.session.accessToken,
refreshToken: nil,
userId: legacyRestorationToken.session.userId,
deviceId: legacyRestorationToken.session.deviceId,
homeserverUrl: legacyRestorationToken.homeURL,
isSoftLogout: legacyRestorationToken.isSoftLogout ?? false))
}
return try JSONDecoder().decode(RestorationToken.self, from: tokenData)
} catch {
MXLog.error("Failed retrieving user restore token")
return nil
}
}
func restorationTokens() -> [KeychainCredentials] {
keychain.allKeys().compactMap { username in
guard let restorationToken = restorationTokenForUsername(username) else {
return nil
}
return KeychainCredentials(userID: username, restorationToken: restorationToken)
}
}
func removeRestorationTokenForUsername(_ username: String) {
do {
try keychain.remove(username)
} catch {
MXLog.error("Failed removing restore token with error: \(error)")
}
}
func removeAllRestorationTokens() {
do {
try keychain.removeAll()
} catch {
MXLog.error("Failed removing all tokens")
}
}
}
@@ -1,30 +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
struct KeychainCredentials {
let userID: String
let restorationToken: RestorationToken
}
protocol KeychainControllerProtocol {
func setRestorationToken(_ restorationToken: RestorationToken, forUsername: String)
func restorationTokenForUsername(_ username: String) -> RestorationToken?
func restorationTokens() -> [KeychainCredentials]
func removeRestorationTokenForUsername(_ username: String)
func removeAllRestorationTokens()
}
@@ -42,7 +42,17 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
func start() {
stateMachine.processEvent(.start)
}
func stop() { }
func isDisplayingRoomScreen(withRoomId roomId: String) -> Bool {
stateMachine.isDisplayingRoomScreen(withRoomId: roomId)
}
func tryDisplayingRoomScreen(roomId: String) {
stateMachine.processEvent(.showRoomScreen(roomId: roomId))
}
// MARK: - Private
// swiftlint:disable:next cyclomatic_complexity
@@ -150,4 +150,14 @@ class UserSessionFlowCoordinatorStateMachine {
func addErrorHandler(_ handler: @escaping StateMachine<State, Event>.Handler) {
stateMachine.addErrorHandler(handler: handler)
}
/// Flag indicating the machine is displaying room screen with given room identifier
func isDisplayingRoomScreen(withRoomId roomId: String) -> Bool {
switch stateMachine.state {
case .roomScreen(let displayedRoomId):
return roomId == displayedRoomId
default:
return false
}
}
}
@@ -26,26 +26,14 @@ class UserSessionStore: UserSessionStoreProtocol {
var hasSessions: Bool { !keychainController.restorationTokens().isEmpty }
/// The base directory where all session data is stored.
private(set) lazy var baseDirectory: URL = {
guard let appGroupContainerURL = FileManager.default.appGroupContainerURL else {
fatalError("Should always be able to retrieve the container directory")
}
let url = appGroupContainerURL
.appendingPathComponent("Library", isDirectory: true)
.appendingPathComponent("Caches", isDirectory: true)
.appendingPathComponent("Sessions", isDirectory: true)
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: false, attributes: nil)
MXLog.debug("Setup base directory at: \(url)")
return url
}()
let baseDirectory: URL
init(bundleIdentifier: String, backgroundTaskService: BackgroundTaskServiceProtocol) {
keychainController = KeychainController(identifier: bundleIdentifier)
init(backgroundTaskService: BackgroundTaskServiceProtocol) {
keychainController = KeychainController(service: .sessions,
accessGroup: InfoPlistReader.target.appGroupIdentifier)
self.backgroundTaskService = backgroundTaskService
baseDirectory = .sessionsBaseDirectory
MXLog.debug("Setup base directory at: \(baseDirectory)")
}
func restoreUserSession() async -> Result<UserSession, UserSessionStoreError> {
@@ -58,7 +46,7 @@ class UserSessionStore: UserSessionStoreProtocol {
switch await restorePreviousLogin(credentials) {
case .success(let clientProxy):
return .success(UserSession(clientProxy: clientProxy,
mediaProvider: MediaProvider(clientProxy: clientProxy,
mediaProvider: MediaProvider(mediaProxy: clientProxy,
imageCache: .onlyInMemory,
fileCache: .default,
backgroundTaskService: backgroundTaskService)))
@@ -77,7 +65,7 @@ class UserSessionStore: UserSessionStoreProtocol {
switch await setupProxyForClient(client) {
case .success(let clientProxy):
return .success(UserSession(clientProxy: clientProxy,
mediaProvider: MediaProvider(clientProxy: clientProxy,
mediaProvider: MediaProvider(mediaProxy: clientProxy,
imageCache: .onlyInMemory,
fileCache: .default,
backgroundTaskService: backgroundTaskService)))