Separate Manage account from Manage devices. (#1698)

Update the SDK.
This commit is contained in:
Doug
2023-09-13 15:49:01 +01:00
committed by GitHub
parent 8268ecc036
commit da29c7efde
15 changed files with 138 additions and 90 deletions
+1 -1
View File
@@ -5613,7 +5613,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
requirement = {
kind = exactVersion;
version = 1.1.12;
version = 1.1.13;
};
};
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {
@@ -129,8 +129,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : {
"revision" : "8c2307180e468fc40ab72e6a5e8b31a4fbbea4d5",
"version" : "1.1.12"
"revision" : "482c8c04019e6e6ac9638ae792adae9d67b08114",
"version" : "1.1.13"
}
},
{
@@ -35,6 +35,8 @@
"action_learn_more" = "Learn more";
"action_leave" = "Leave";
"action_leave_room" = "Leave room";
"action_manage_account" = "Manage account";
"action_manage_devices" = "Manage devices";
"action_next" = "Next";
"action_no" = "No";
"action_not_now" = "Not now";
+4
View File
@@ -86,6 +86,10 @@ public enum L10n {
public static var actionLeave: String { return L10n.tr("Localizable", "action_leave") }
/// Leave room
public static var actionLeaveRoom: String { return L10n.tr("Localizable", "action_leave_room") }
/// Manage account
public static var actionManageAccount: String { return L10n.tr("Localizable", "action_manage_account") }
/// Manage devices
public static var actionManageDevices: String { return L10n.tr("Localizable", "action_manage_devices") }
/// Next
public static var actionNext: String { return L10n.tr("Localizable", "action_next") }
/// No
@@ -33,19 +33,27 @@ class SDKClientMock: SDKClientProtocol {
}
//MARK: - accountUrl
public var accountUrlCallsCount = 0
public var accountUrlCalled: Bool {
return accountUrlCallsCount > 0
public var accountUrlActionThrowableError: Error?
public var accountUrlActionCallsCount = 0
public var accountUrlActionCalled: Bool {
return accountUrlActionCallsCount > 0
}
public var accountUrlReturnValue: String?
public var accountUrlClosure: (() -> String?)?
public var accountUrlActionReceivedAction: AccountManagementAction?
public var accountUrlActionReceivedInvocations: [AccountManagementAction?] = []
public var accountUrlActionReturnValue: String?
public var accountUrlActionClosure: ((AccountManagementAction?) throws -> String?)?
public func accountUrl() -> String? {
accountUrlCallsCount += 1
if let accountUrlClosure = accountUrlClosure {
return accountUrlClosure()
public func accountUrl(action: AccountManagementAction?) throws -> String? {
if let error = accountUrlActionThrowableError {
throw error
}
accountUrlActionCallsCount += 1
accountUrlActionReceivedAction = action
accountUrlActionReceivedInvocations.append(action)
if let accountUrlActionClosure = accountUrlActionClosure {
return try accountUrlActionClosure(action)
} else {
return accountUrlReturnValue
return accountUrlActionReturnValue
}
}
//MARK: - avatarUrl
@@ -48,23 +48,25 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
switch action {
case .close:
self.callback?(.dismiss)
case .account:
self.presentAccountSettings()
callback?(.dismiss)
case .accountProfile:
presentAccountProfileURL()
case .analytics:
self.presentAnalyticsScreen()
presentAnalyticsScreen()
case .reportBug:
self.presentBugReportScreen()
presentBugReportScreen()
case .about:
self.presentLegalInformationScreen()
presentLegalInformationScreen()
case .sessionVerification:
self.verifySession()
verifySession()
case .accountSessionsList:
presentAccountSessionsListURL()
case .developerOptions:
self.presentDeveloperOptions()
presentDeveloperOptions()
case .logout:
self.callback?(.logout)
callback?(.logout)
case .notifications:
self.presentNotificationSettings()
presentNotificationSettings()
}
}
}
@@ -75,15 +77,26 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
AnyView(SettingsScreen(context: viewModel.context))
}
// MARK: - Private
// MARK: - OIDC Account Management
private var accountSettingsPresenter: OIDCAccountSettingsPresenter?
private func presentAccountSettings() {
guard let accountURL = viewModel.context.viewState.accountURL else {
private func presentAccountProfileURL() {
guard let url = viewModel.context.viewState.accountProfileURL else {
MXLog.error("Account URL is missing.")
return
}
presentAccountManagementURL(url)
}
private func presentAccountSessionsListURL() {
guard let url = viewModel.context.viewState.accountSessionsListURL else {
MXLog.error("Account URL is missing.")
return
}
presentAccountManagementURL(url)
}
private var accountSettingsPresenter: OIDCAccountSettingsPresenter?
private func presentAccountManagementURL(_ url: URL) {
guard let window = viewModel.context.viewState.window else {
MXLog.error("The window is missing.")
return
@@ -91,10 +104,12 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
// Note to anyone in the future if you come back here to make this open in Safari instead of a WAS.
// As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷‍♂️
accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: accountURL, presentationAnchor: window)
accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: url, presentationAnchor: window)
accountSettingsPresenter?.start()
}
// MARK: - Private
private func presentAnalyticsScreen() {
let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics))
@@ -19,11 +19,12 @@ import UIKit
enum SettingsScreenViewModelAction {
case close
case account
case accountProfile
case analytics
case reportBug
case about
case sessionVerification
case accountSessionsList
case developerOptions
case notifications
case logout
@@ -33,7 +34,8 @@ struct SettingsScreenViewState: BindableState {
var bindings: SettingsScreenViewStateBindings
var deviceID: String?
var userID: String
var accountURL: URL?
var accountProfileURL: URL?
var accountSessionsListURL: URL?
var userAvatarURL: URL?
var userDisplayName: String?
var showSessionVerificationSection: Bool
@@ -49,13 +51,14 @@ struct SettingsScreenViewStateBindings {
enum SettingsScreenViewAction {
case close
case account
case accountProfile
case analytics
case reportBug
case about
case sessionVerification
case logout
case changedTimelineStyle
case accountSessionsList
case developerOptions
case notifications
@@ -39,7 +39,8 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
super.init(initialViewState: .init(bindings: bindings,
deviceID: userSession.deviceID,
userID: userSession.userID,
accountURL: userSession.clientProxy.accountURL,
accountProfileURL: userSession.clientProxy.accountURL(action: .profile),
accountSessionsListURL: userSession.clientProxy.accountURL(action: .sessionsList),
showSessionVerificationSection: showSessionVerificationSection,
showDeveloperOptions: appSettings.canShowDeveloperOptions),
imageProvider: userSession.mediaProvider)
@@ -81,8 +82,8 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
switch viewAction {
case .close:
callback?(.close)
case .account:
callback?(.account)
case .accountProfile:
callback?(.accountProfile)
case .analytics:
callback?(.analytics)
case .reportBug:
@@ -97,6 +98,8 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
appSettings.timelineStyle = state.bindings.timelineStyle
case .notifications:
callback?(.notifications)
case .accountSessionsList:
callback?(.accountSessionsList)
case .developerOptions:
callback?(.developerOptions)
@@ -15,6 +15,7 @@
//
import Compound
import SFSafeSymbols
import SwiftUI
struct SettingsScreen: View {
@@ -30,7 +31,11 @@ struct SettingsScreen: View {
sessionVerificationSection
}
simplifiedSection
mainSection
if context.viewState.accountSessionsListURL != nil {
manageSessionsSection
}
if context.viewState.showDeveloperOptions {
developerOptionsSection
@@ -41,20 +46,12 @@ struct SettingsScreen: View {
.compoundList()
.navigationTitle(L10n.commonSettings)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
doneButton
}
}
.toolbar { toolbar }
.introspect(.window, on: .iOS(.v16, .v17)) { window in
context.send(viewAction: .updateWindow(window))
}
}
private var versionText: Text {
Text(L10n.settingsVersionNumber(InfoPlistReader.main.bundleShortVersionString, InfoPlistReader.main.bundleVersion))
}
private var userSection: some View {
Section {
ListRow(kind: .custom {
@@ -90,25 +87,13 @@ struct SettingsScreen: View {
}
}
private var developerOptionsSection: some View {
Section {
ListRow(label: .default(title: L10n.commonDeveloperOptions,
systemIcon: .hammerCircle),
kind: .navigationLink {
context.send(viewAction: .developerOptions)
})
.accessibilityIdentifier(A11yIdentifiers.settingsScreen.developerOptions)
}
}
private var simplifiedSection: some View {
private var mainSection: some View {
Section {
// Account
if context.viewState.accountURL != nil {
ListRow(label: .default(title: L10n.screenSettingsOidcAccount,
systemIcon: .person),
if context.viewState.accountProfileURL != nil {
ListRow(label: .default(title: L10n.actionManageAccount, systemIcon: .person),
kind: .button {
context.send(viewAction: .account)
context.send(viewAction: .accountProfile)
})
.accessibilityIdentifier(A11yIdentifiers.settingsScreen.account)
}
@@ -157,6 +142,36 @@ struct SettingsScreen: View {
}
}
private var manageSessionsSection: some View {
Section {
ListRow(label: .default(title: L10n.actionManageDevices, systemIcon: deviceIcon),
kind: .button {
context.send(viewAction: .accountSessionsList)
})
}
}
private var deviceIcon: SFSymbol {
if ProcessInfo.processInfo.isiOSAppOnMac {
return .macbookAndIphone
} else if UIDevice.current.userInterfaceIdiom == .pad {
return .ipad
} else {
return .iphone
}
}
private var developerOptionsSection: some View {
Section {
ListRow(label: .default(title: L10n.commonDeveloperOptions,
systemIcon: .hammerCircle),
kind: .navigationLink {
context.send(viewAction: .developerOptions)
})
.accessibilityIdentifier(A11yIdentifiers.settingsScreen.developerOptions)
}
}
private var signOutSection: some View {
Section {
ListRow(label: .default(title: L10n.screenSignoutPreferenceItem,
@@ -166,9 +181,9 @@ struct SettingsScreen: View {
})
.accessibilityIdentifier(A11yIdentifiers.settingsScreen.logout)
.alert(L10n.screenSignoutConfirmationDialogTitle, isPresented: $showingLogoutConfirmation) {
Button(L10n.screenSignoutConfirmationDialogSubmit,
role: .destructive,
action: logout)
Button(L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) {
context.send(viewAction: .logout)
}
} message: {
Text(L10n.screenSignoutConfirmationDialogContent)
}
@@ -186,18 +201,16 @@ struct SettingsScreen: View {
.padding(.top, 24)
}
}
private var doneButton: some View {
Button(L10n.actionDone, action: close)
.accessibilityIdentifier(A11yIdentifiers.settingsScreen.done)
private var versionText: Text {
Text(L10n.settingsVersionNumber(InfoPlistReader.main.bundleShortVersionString, InfoPlistReader.main.bundleVersion))
}
private func close() {
context.send(viewAction: .close)
}
private func logout() {
context.send(viewAction: .logout)
private var toolbar: some ToolbarContent {
ToolbarItem(placement: .confirmationAction) {
Button(L10n.actionDone) { context.send(viewAction: .close) }
.accessibilityIdentifier(A11yIdentifiers.settingsScreen.done)
}
}
}
@@ -220,8 +233,7 @@ struct SettingsScreen_Previews: PreviewProvider {
verificationController.isVerified = false
let userSession = MockUserSession(sessionVerificationController: verificationController,
clientProxy: MockClientProxy(userID: "@userid:example.com",
deviceID: "AAAAAAAAAAA",
accountURL: "https://matrix.org/account"),
deviceID: "AAAAAAAAAAA"),
mediaProvider: MockMediaProvider())
return SettingsScreenViewModel(userSession: userSession,
appSettings: ServiceLocator.shared.settings)
@@ -125,10 +125,6 @@ class ClientProxy: ClientProxyProtocol {
return nil
}
}
var accountURL: URL? {
client.accountUrl().flatMap(URL.init(string:))
}
func startSync() {
guard !hasEncounteredAuthError else {
@@ -155,6 +151,10 @@ class ClientProxy: ClientProxyProtocol {
}
}
func accountURL(action: AccountManagementAction) -> URL? {
try? client.accountUrl(action: action).flatMap(URL.init(string:))
}
func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError> {
await Task.dispatch(on: clientQueue) {
do {
@@ -83,8 +83,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
var restorationToken: RestorationToken? { get }
var accountURL: URL? { get }
var roomSummaryProvider: RoomSummaryProviderProtocol? { get }
var inviteSummaryProvider: RoomSummaryProviderProtocol? { get }
@@ -95,6 +93,8 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
func stopSync()
func accountURL(action: AccountManagementAction) -> URL?
func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError>
func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result<String, ClientProxyError>
@@ -27,7 +27,6 @@ class MockClientProxy: ClientProxyProtocol {
let deviceID: String?
let homeserver = ""
let restorationToken: RestorationToken? = nil
let accountURL: URL?
var roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()
@@ -37,21 +36,22 @@ class MockClientProxy: ClientProxyProtocol {
var notificationSettings: NotificationSettingsProxyProtocol = NotificationSettingsProxyMock(with: .init())
init(userID: String, deviceID: String? = nil, accountURL: URL? = nil, roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()) {
init(userID: String, deviceID: String? = nil, roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()) {
self.userID = userID
self.deviceID = deviceID
self.accountURL = accountURL
self.roomSummaryProvider = roomSummaryProvider
}
func loadUserAvatarURL() async { }
func startSync() { }
func stopSync(completionHandler: () -> Void) { }
func stopSync() { }
func accountURL(action: AccountManagementAction) -> URL? {
"https://matrix.org/account"
}
func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError> {
.failure(.failedRetrievingDirectRoom)
}
@@ -170,7 +170,7 @@ class MockScreen: Identifiable {
return navigationStackCoordinator
case .settings:
let navigationStackCoordinator = NavigationStackCoordinator()
let clientProxy = MockClientProxy(userID: "@mock:client.com", accountURL: "https://matrix.org/account")
let clientProxy = MockClientProxy(userID: "@mock:client.com")
let coordinator = SettingsScreenCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: nil,
userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()),
+1
View File
@@ -0,0 +1 @@
Separate Manage account from Manage devices
+1 -1
View File
@@ -46,7 +46,7 @@ packages:
# Element/Matrix dependencies
MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.1.12
exactVersion: 1.1.13
# path: ../matrix-rust-sdk
DesignKit:
path: DesignKit