Update the SDK.

Handles changes that removed support for the sliding sync proxy.
This commit is contained in:
Doug 2025-02-18 10:34:32 +00:00 committed by Doug
parent f77faee981
commit 8c07ee35c4
33 changed files with 229 additions and 309 deletions

View File

@ -67,7 +67,6 @@
09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; };
09AAF04B27732046C755D914 /* SoftLogoutViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C5DAA1773F57653BF1C4F9 /* SoftLogoutViewModelTests.swift */; };
09D3D7D115318CAD131B4FE7 /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57084488B03BDB33C7B7CA0E /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift */; };
0A0625A271EE5B06D2AAA069 /* HomeScreenSlidingSyncMigrationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4691B8DE1D51DE152680098A /* HomeScreenSlidingSyncMigrationBanner.swift */; };
0A194F5E70B5A628C1BF4476 /* AdvancedSettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4999B5FD50AED7CB0F590FF8 /* AdvancedSettingsScreenModels.swift */; };
0ACAA31FD0399CEEBA3ECC21 /* UserDetailsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85149F56BA333619900E2410 /* UserDetailsEditScreenViewModelProtocol.swift */; };
0AD8EF040A60D62F488C18B5 /* KnockRequestProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F957320D0EB7D7B4E30C79D /* KnockRequestProxyMock.swift */; };
@ -1399,6 +1398,7 @@
0B0E0B55E2EE75AF67029924 /* SwipeToReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToReplyView.swift; sourceTree = "<group>"; };
0B32BBA8887BD7A5C4ECF16F /* RoomModerationRole.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomModerationRole.swift; sourceTree = "<group>"; };
0B987FC3FDBAA0E1C5AA235C /* PaginationIndicatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIndicatorRoomTimelineItem.swift; sourceTree = "<group>"; };
0BA7D6C94A50428463D09AF0 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/InfoPlist.strings; sourceTree = "<group>"; };
0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewerScreenViewModel.swift; sourceTree = "<group>"; };
0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenViewModel.swift; sourceTree = "<group>"; };
0BD116096CAA9139B95EEA9C /* UserProfileScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileScreenViewModel.swift; sourceTree = "<group>"; };
@ -1694,7 +1694,6 @@
4629710C0337ADD9C8909542 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/Localizable.strings; sourceTree = "<group>"; };
466C71A0FED9BFF287613C82 /* RoomDetailsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenModels.swift; sourceTree = "<group>"; };
467498BEA681758BE2F80826 /* TimelineMediaPreviewDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaPreviewDetailsView.swift; sourceTree = "<group>"; };
4691B8DE1D51DE152680098A /* HomeScreenSlidingSyncMigrationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenSlidingSyncMigrationBanner.swift; sourceTree = "<group>"; };
46A2AD86F7E618F468F6FAF5 /* VoiceMessageRecordingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecordingButton.swift; sourceTree = "<group>"; };
46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsAppCoordinator.swift; sourceTree = "<group>"; };
46D0BA44B1838E65B507B277 /* NotificationPermissionsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreen.swift; sourceTree = "<group>"; };
@ -1752,6 +1751,7 @@
52135BD9E0E7A091688F627A /* MessageForwardingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenModels.swift; sourceTree = "<group>"; };
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = "<group>"; };
5281C5CDC4A712265A0B5FBF /* PollRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollRoomTimelineItem.swift; sourceTree = "<group>"; };
529513218340CC8419273165 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
52BD6ED18E2EB61E28C340AD /* AttributedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedString.swift; sourceTree = "<group>"; };
52F5EE5DE3B55D59299DB5BC /* AppLockSetupBiometricsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenViewModelTests.swift; sourceTree = "<group>"; };
53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewAdapter.swift; sourceTree = "<group>"; };
@ -2315,6 +2315,7 @@
C9F893F4A111CB7BA5C96949 /* AppLockSetupBiometricsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenViewModel.swift; sourceTree = "<group>"; };
CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinator.swift; sourceTree = "<group>"; };
CA29952595B804DA221A0C1D /* ComposerToolbarViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerToolbarViewModelTests.swift; sourceTree = "<group>"; };
CA46BDF38F87118939BDF659 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
CA4F6D7000EDCD187E0989E7 /* PinnedEventsTimelineScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsTimelineScreen.swift; sourceTree = "<group>"; };
CA89A2DD51B6BBE1DA55E263 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
CA90BD288E5AE6BC643AFDDF /* TemplateScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenCoordinator.swift; sourceTree = "<group>"; };
@ -2409,6 +2410,7 @@
E0FCA0957FAA0E15A9F5579D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Untranslated.stringsdict; sourceTree = "<group>"; };
E0FF9CB3EFA753277291F609 /* EncryptionResetScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetScreenCoordinator.swift; sourceTree = "<group>"; };
E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileListRow.swift; sourceTree = "<group>"; };
E157152B11E347F735C3FD6E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenCoordinator.swift; sourceTree = "<group>"; };
E1A5FEF17ED7E6176D922D4F /* RoomDetailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreen.swift; sourceTree = "<group>"; };
E1E0B4A34E69BD2132BEC521 /* MessageText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageText.swift; sourceTree = "<group>"; };
@ -3764,7 +3766,6 @@
05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */,
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */,
C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */,
4691B8DE1D51DE152680098A /* HomeScreenSlidingSyncMigrationBanner.swift */,
84AF32E4136FD6F159D86C2C /* RoomDirectorySearchView.swift */,
037A5661B26EC6BE068188D7 /* Filters */,
);
@ -6296,6 +6297,7 @@
ru,
sk,
sv,
tr,
uk,
uz,
"zh-Hans",
@ -7077,7 +7079,6 @@
B04E9EB589CE99C3929E817A /* HomeScreenRecoveryKeyConfirmationBanner.swift in Sources */,
0AE0AB1952F186EB86719B4F /* HomeScreenRoomCell.swift in Sources */,
A10D6CCDE2010C09EEA1A593 /* HomeScreenRoomList.swift in Sources */,
0A0625A271EE5B06D2AAA069 /* HomeScreenSlidingSyncMigrationBanner.swift in Sources */,
DE4F8C4E0F1DB4832F09DE97 /* HomeScreenViewModel.swift in Sources */,
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */,
2BBE320EE426A347AAE5C7DA /* IdentityConfirmationScreen.swift in Sources */,
@ -7832,6 +7833,7 @@
E5F2B6443D1ED8602F328539 /* ru */,
667DD3A9D932D7D9EB380CAA /* sk */,
0EE9EAF0309A2A1D67D8FAF5 /* sv */,
E157152B11E347F735C3FD6E /* tr */,
5F12E996BFBEB43815189ABF /* uk */,
DFFB0E7C6D8E190AFA0176DC /* uz */,
AB26D5444A4A7E095222DE8B /* zh-Hans */,
@ -7868,6 +7870,7 @@
E8294DB9E95C0C0630418466 /* ru */,
AD378D580A41E42560C60E9C /* sk */,
ACA11F7F50A4A3887A18CA5A /* sv */,
529513218340CC8419273165 /* tr */,
ADCB8A232D3A8FB3E16A7303 /* uk */,
475EB595D7527E9A8A14043E /* uz */,
284FEEB0789B8894E52A7F34 /* zh-Hans */,
@ -7892,6 +7895,7 @@
1D652E78832289CD9EB64488 /* hu */,
7199693797B66245EF97BCF5 /* id */,
44C314C00533E2C297796B60 /* it */,
0BA7D6C94A50428463D09AF0 /* ka */,
E60757AFE04391B43EA568B8 /* nl */,
997BF045585AF6DB2EBC5755 /* pl */,
A8DF55467ED4CE76B7AE9A33 /* pt */,
@ -7900,6 +7904,7 @@
9B7D8D3638864B7482E148CC /* ru */,
7D39AF1F659923D77778511E /* sk */,
969694F67E844FCA51F7E051 /* sv */,
CA46BDF38F87118939BDF659 /* tr */,
D66B5D86A9AB95E0E01BED82 /* uk */,
FF720BA68256297680980481 /* zh-Hans */,
0545AC444BEEA89FF8C509FD /* zh-Hant-TW */,
@ -8521,7 +8526,7 @@
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
requirement = {
kind = exactVersion;
version = 25.02.11;
version = 25.02.17;
};
};
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {

View File

@ -149,8 +149,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
"state" : {
"revision" : "cc010fc6971370d1df2c0eb67cc5cfd577465b62",
"version" : "25.2.11"
"revision" : "422d7bef3ffd3edcb3e30afb410ee523f5659adc",
"version" : "25.2.17"
}
},
{

View File

@ -120,6 +120,7 @@
"action_yes" = "Yes";
"action_yes_try_again" = "Yes, try again";
"banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade";
"banner_migrate_to_native_sliding_sync_app_force_logout_title" = "Element X no longer supports the old protocol. Please log out and log back in to continue using the app.";
"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later.";
"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app.";
"banner_migrate_to_native_sliding_sync_title" = "Upgrade available";

View File

@ -120,6 +120,7 @@
"action_yes" = "Yes";
"action_yes_try_again" = "Yes, try again";
"banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade";
"banner_migrate_to_native_sliding_sync_app_force_logout_title" = "Element X no longer supports the old protocol. Please log out and log back in to continue using the app.";
"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later.";
"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app.";
"banner_migrate_to_native_sliding_sync_title" = "Upgrade available";

View File

@ -44,7 +44,6 @@ final class AppSettings {
case elementCallBaseURLOverride
// Feature flags
case slidingSyncDiscovery
case publicSearchEnabled
case fuzzyRoomListSearchEnabled
case enableOnlySignedDeviceIsolationMode
@ -283,10 +282,6 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.fuzzyRoomListSearchEnabled, defaultValue: false, storageType: .userDefaults(store))
var fuzzyRoomListSearchEnabled
enum SlidingSyncDiscovery: Codable { case proxy, native, forceNative }
@UserPreference(key: UserDefaultsKeys.slidingSyncDiscovery, defaultValue: .native, storageType: .userDefaults(store))
var slidingSyncDiscovery: SlidingSyncDiscovery
@UserPreference(key: UserDefaultsKeys.knockingEnabled, defaultValue: false, storageType: .userDefaults(store))
var knockingEnabled

View File

@ -230,7 +230,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
}
private func presentDeveloperOptions() {
let coordinator = DeveloperOptionsScreenCoordinator(isUsingNativeSlidingSync: parameters.userSession.clientProxy.slidingSyncVersion == .native)
let coordinator = DeveloperOptionsScreenCoordinator()
coordinator.actions
.sink { [weak self] action in

View File

@ -274,6 +274,8 @@ internal enum L10n {
internal static var actionYesTryAgain: String { return L10n.tr("Localizable", "action_yes_try_again") }
/// Log Out & Upgrade
internal static var bannerMigrateToNativeSlidingSyncAction: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_action") }
/// Element X no longer supports the old protocol. Please log out and log back in to continue using the app.
internal static var bannerMigrateToNativeSlidingSyncAppForceLogoutTitle: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_app_force_logout_title") }
/// Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later.
internal static var bannerMigrateToNativeSlidingSyncDescription: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_description") }
/// Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app.

View File

@ -69,10 +69,8 @@ extension ClientProxyMock {
ignoreUserReturnValue = .success(())
unignoreUserReturnValue = .success(())
needsSlidingSyncMigration = false
slidingSyncVersion = .native
availableSlidingSyncVersionsClosure = {
[]
}
trackRecentlyVisitedRoomReturnValue = .success(())
recentlyVisitedRoomsReturnValue = .success([])

View File

@ -2235,28 +2235,16 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
set(value) { underlyingHomeserver = value }
}
var underlyingHomeserver: String!
var needsSlidingSyncMigration: Bool {
get { return underlyingNeedsSlidingSyncMigration }
set(value) { underlyingNeedsSlidingSyncMigration = value }
}
var underlyingNeedsSlidingSyncMigration: Bool!
var slidingSyncVersion: SlidingSyncVersion {
get { return underlyingSlidingSyncVersion }
set(value) { underlyingSlidingSyncVersion = value }
}
var underlyingSlidingSyncVersion: SlidingSyncVersion!
var availableSlidingSyncVersionsCallsCount = 0
var availableSlidingSyncVersionsCalled: Bool {
return availableSlidingSyncVersionsCallsCount > 0
}
var availableSlidingSyncVersions: [SlidingSyncVersion] {
get async {
availableSlidingSyncVersionsCallsCount += 1
if let availableSlidingSyncVersionsClosure = availableSlidingSyncVersionsClosure {
return await availableSlidingSyncVersionsClosure()
} else {
return underlyingAvailableSlidingSyncVersions
}
}
}
var underlyingAvailableSlidingSyncVersions: [SlidingSyncVersion]!
var availableSlidingSyncVersionsClosure: (() async -> [SlidingSyncVersion])?
var canDeactivateAccount: Bool {
get { return underlyingCanDeactivateAccount }
set(value) { underlyingCanDeactivateAccount = value }

View File

@ -27,9 +27,7 @@ extension ClientBuilder {
builder = switch slidingSync {
case .restored: builder
case .discoverProxy: builder.slidingSyncVersionBuilder(versionBuilder: .discoverProxy)
case .discoverNative: builder.slidingSyncVersionBuilder(versionBuilder: .discoverNative)
case .forceNative: builder.slidingSyncVersionBuilder(versionBuilder: .native)
case .discover: builder.slidingSyncVersionBuilder(versionBuilder: .discoverNative)
}
if setupEncryption {
@ -58,12 +56,8 @@ extension ClientBuilder {
}
enum ClientBuilderSlidingSync {
/// The proxy will be supplied when restoring the Session.
/// Sliding sync will be configured when restoring the Session.
case restored
/// A proxy must be discovered whilst building the session.
case discoverProxy
/// Native sliding sync must be discovered whilst building the session.
case discoverNative
/// Forces native sliding sync without discovering it.
case forceNative
/// Sliding sync must be discovered whilst building the session.
case discover
}

View File

@ -36,8 +36,6 @@ enum HomeScreenViewAction {
case confirmRecoveryKey
case resetEncryption
case skipRecoveryKeyConfirmation
case confirmSlidingSyncUpgrade
case skipSlidingSyncUpgrade
case updateVisibleItemRange(Range<Int>)
case globalSearch
case markRoomAsUnread(roomIdentifier: String)
@ -86,17 +84,12 @@ enum HomeScreenSecurityBannerMode: Equatable {
}
}
enum HomeScreenMigrationBannerMode {
case none, show, dismissed
}
struct HomeScreenViewState: BindableState {
let userID: String
var userDisplayName: String?
var userAvatarURL: URL?
var securityBannerMode = HomeScreenSecurityBannerMode.none
var slidingSyncMigrationBannerMode = HomeScreenMigrationBannerMode.none
var requiresExtraAccountSetup = false

View File

@ -145,11 +145,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
actionsSubject.send(.presentEncryptionResetScreen)
case .skipRecoveryKeyConfirmation:
state.securityBannerMode = .dismissed
case .confirmSlidingSyncUpgrade:
appSettings.slidingSyncDiscovery = .native
actionsSubject.send(.logout)
case .skipSlidingSyncUpgrade:
state.slidingSyncMigrationBannerMode = .dismissed
case .updateVisibleItemRange(let range):
roomSummaryProvider?.updateVisibleRange(range)
case .startChat:
@ -307,30 +302,18 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
/// Check whether we can inform the user about potential migrations
/// or have him logout as his proxy is no longer available
private func checkSlidingSyncMigration() async {
// Not logged in with a proxy, don't need to do anything
guard userSession.clientProxy.slidingSyncVersion.isProxy else {
guard userSession.clientProxy.needsSlidingSyncMigration else {
return
}
let versions = await userSession.clientProxy.availableSlidingSyncVersions
// Native not available, nothing we can do
guard versions.contains(.native) else {
return
}
if versions.contains(where: \.isProxy) { // Both available, prompt for migration
state.slidingSyncMigrationBannerMode = .show
} else { // The proxy has been removed and logout is needed
// Delay setting the alert otherwise it automatically gets dismissed. Same as the crashed last run one
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.state.bindings.alertInfo = AlertInfo(id: UUID(),
title: L10n.bannerMigrateToNativeSlidingSyncForceLogoutTitle,
primaryButton: .init(title: L10n.bannerMigrateToNativeSlidingSyncAction) { [weak self] in
self?.appSettings.slidingSyncDiscovery = .native
self?.actionsSubject.send(.logoutWithoutConfirmation)
})
}
// The proxy is no longer supported so a logout is needed.
// Delay setting the alert otherwise it automatically gets dismissed. Same as the crashed last run one
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.state.bindings.alertInfo = AlertInfo(id: UUID(),
title: L10n.bannerMigrateToNativeSlidingSyncAppForceLogoutTitle,
primaryButton: .init(title: L10n.bannerMigrateToNativeSlidingSyncAction) { [weak self] in
self?.actionsSubject.send(.logoutWithoutConfirmation)
})
}
}
@ -460,14 +443,3 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
message: L10n.errorUnknown)
}
}
extension SlidingSyncVersion {
var isProxy: Bool {
switch self {
case .proxy:
return true
default:
return false
}
}
}

View File

@ -119,17 +119,13 @@ struct HomeScreenContent: View {
@ViewBuilder
private var topSection: some View {
// An empty VStack causes glitches within the room list
if context.viewState.shouldShowFilters ||
context.viewState.securityBannerMode.isShown ||
context.viewState.slidingSyncMigrationBannerMode == .show {
if context.viewState.shouldShowFilters || context.viewState.securityBannerMode.isShown {
VStack(spacing: 0) {
if context.viewState.shouldShowFilters {
RoomListFiltersView(state: $context.filtersState)
}
if context.viewState.slidingSyncMigrationBannerMode == .show {
HomeScreenSlidingSyncMigrationBanner(context: context)
} else if case let .show(state) = context.viewState.securityBannerMode {
if case let .show(state) = context.viewState.securityBannerMode {
HomeScreenRecoveryKeyConfirmationBanner(state: state, context: context)
}
}

View File

@ -1,68 +0,0 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
import Combine
import SwiftUI
struct HomeScreenSlidingSyncMigrationBanner: View {
var context: HomeScreenViewModel.Context
var body: some View {
VStack(alignment: .leading, spacing: 16) {
VStack(alignment: .leading, spacing: 4) {
HStack(spacing: 16) {
Text(L10n.bannerMigrateToNativeSlidingSyncTitle)
.font(.compound.bodyLGSemibold)
.foregroundColor(.compound.textPrimary)
Spacer()
Button {
context.send(viewAction: .skipSlidingSyncUpgrade)
} label: {
Image(systemName: "xmark")
.foregroundColor(.compound.iconSecondary)
.frame(width: 12, height: 12)
}
}
Text(L10n.bannerMigrateToNativeSlidingSyncDescription)
.font(.compound.bodyMD)
.foregroundColor(.compound.textSecondary)
}
Button(L10n.bannerMigrateToNativeSlidingSyncAction) {
context.send(viewAction: .confirmSlidingSyncUpgrade)
}
.frame(maxWidth: .infinity)
.buttonStyle(.compound(.primary, size: .medium))
}
.padding(16)
.background(Color.compound.bgSubtleSecondary)
.cornerRadius(14)
.padding(.horizontal, 16)
}
}
struct HomeScreenSlidingSyncMigrationBanner_Previews: PreviewProvider, TestablePreview {
static let viewModel = buildViewModel()
static var previews: some View {
HomeScreenSlidingSyncMigrationBanner(context: viewModel.context)
}
static func buildViewModel() -> HomeScreenViewModel {
let clientProxy = ClientProxyMock(.init())
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
return HomeScreenViewModel(userSession: userSession,
analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController)
}
}

View File

@ -22,10 +22,9 @@ final class DeveloperOptionsScreenCoordinator: CoordinatorProtocol {
actionsSubject.eraseToAnyPublisher()
}
init(isUsingNativeSlidingSync: Bool) {
init() {
viewModel = DeveloperOptionsScreenViewModel(developerOptions: ServiceLocator.shared.settings,
elementCallBaseURL: ServiceLocator.shared.settings.elementCallBaseURL,
isUsingNativeSlidingSync: isUsingNativeSlidingSync)
elementCallBaseURL: ServiceLocator.shared.settings.elementCallBaseURL)
viewModel.actions
.sink { [weak self] action in

View File

@ -13,12 +13,7 @@ enum DeveloperOptionsScreenViewModelAction {
struct DeveloperOptionsScreenViewState: BindableState {
let elementCallBaseURL: URL
let isUsingNativeSlidingSync: Bool
var bindings: DeveloperOptionsScreenViewStateBindings
var slidingSyncFooter: String {
"The method used to configure sliding sync when signing in. Changing this setting has no effect until you sign out.\n\nYour current session is using \(isUsingNativeSlidingSync ? "native sliding sync." : "a sliding sync proxy.")"
}
}
// periphery: ignore - subscripts are seen as false positive
@ -42,7 +37,6 @@ enum DeveloperOptionsScreenViewAction {
protocol DeveloperOptionsProtocol: AnyObject {
var logLevel: LogLevel { get set }
var slidingSyncDiscovery: AppSettings.SlidingSyncDiscovery { get set }
var publicSearchEnabled: Bool { get set }
var hideUnreadMessagesBadge: Bool { get set }
var fuzzyRoomListSearchEnabled: Bool { get set }

View File

@ -17,9 +17,9 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
actionsSubject.eraseToAnyPublisher()
}
init(developerOptions: DeveloperOptionsProtocol, elementCallBaseURL: URL, isUsingNativeSlidingSync: Bool) {
init(developerOptions: DeveloperOptionsProtocol, elementCallBaseURL: URL) {
let bindings = DeveloperOptionsScreenViewStateBindings(developerOptions: developerOptions)
let state = DeveloperOptionsScreenViewState(elementCallBaseURL: elementCallBaseURL, isUsingNativeSlidingSync: isUsingNativeSlidingSync, bindings: bindings)
let state = DeveloperOptionsScreenViewState(elementCallBaseURL: elementCallBaseURL, bindings: bindings)
super.init(initialViewState: state)
}

View File

@ -32,18 +32,6 @@ struct DeveloperOptionsScreen: View {
}
}
Section {
Picker("Discovery", selection: $context.slidingSyncDiscovery) {
Text("Proxy only").tag(AppSettings.SlidingSyncDiscovery.proxy)
Text("Automatic").tag(AppSettings.SlidingSyncDiscovery.native)
Text("Force Native ⚠️").tag(AppSettings.SlidingSyncDiscovery.forceNative)
}
} header: {
Text("Sliding Sync")
} footer: {
Text(context.viewState.slidingSyncFooter)
}
Section("Room List") {
Toggle(isOn: $context.publicSearchEnabled) {
Text("Public search")
@ -175,8 +163,7 @@ private struct LogLevelConfigurationView: View {
struct DeveloperOptionsScreen_Previews: PreviewProvider {
static let viewModel = DeveloperOptionsScreenViewModel(developerOptions: ServiceLocator.shared.settings,
elementCallBaseURL: ServiceLocator.shared.settings.elementCallBaseURL,
isUsingNativeSlidingSync: true)
elementCallBaseURL: ServiceLocator.shared.settings.elementCallBaseURL)
static var previews: some View {
NavigationStack {
DeveloperOptionsScreen(context: viewModel.context)

View File

@ -27,55 +27,25 @@ struct AuthenticationClientBuilder: AuthenticationClientBuilderProtocol {
/// Builds a Client for login using OIDC or password authentication.
func build(homeserverAddress: String) async throws -> ClientProtocol {
if appSettings.slidingSyncDiscovery == .forceNative {
return try await makeClientBuilder(slidingSync: .forceNative).serverNameOrHomeserverUrl(serverNameOrUrl: homeserverAddress).build()
}
if appSettings.slidingSyncDiscovery == .native {
do {
return try await makeClientBuilder(slidingSync: .discoverNative).serverNameOrHomeserverUrl(serverNameOrUrl: homeserverAddress).build()
} catch {
MXLog.warning("Native sliding sync not available: \(error)")
MXLog.info("Falling back to a sliding sync proxy.")
}
}
return try await makeClientBuilder(slidingSync: .discoverProxy).serverNameOrHomeserverUrl(serverNameOrUrl: homeserverAddress).build()
try await makeClientBuilder().serverNameOrHomeserverUrl(serverNameOrUrl: homeserverAddress).build()
}
/// Builds a Client, authenticating with the given QR code data.
func buildWithQRCode(qrCodeData: QrCodeData,
oidcConfiguration: OIDCConfigurationProxy,
progressListener: QrLoginProgressListenerProxy) async throws -> ClientProtocol {
if appSettings.slidingSyncDiscovery == .forceNative {
return try await makeClientBuilder(slidingSync: .forceNative).buildWithQrCode(qrCodeData: qrCodeData,
oidcConfiguration: oidcConfiguration.rustValue,
progressListener: progressListener)
}
if appSettings.slidingSyncDiscovery == .native {
do {
return try await makeClientBuilder(slidingSync: .discoverNative).buildWithQrCode(qrCodeData: qrCodeData,
oidcConfiguration: oidcConfiguration.rustValue,
progressListener: progressListener)
} catch HumanQrLoginError.SlidingSyncNotAvailable {
MXLog.warning("Native sliding sync not available")
MXLog.info("Falling back to a sliding sync proxy.")
}
}
return try await makeClientBuilder(slidingSync: .discoverProxy).buildWithQrCode(qrCodeData: qrCodeData,
oidcConfiguration: oidcConfiguration.rustValue,
progressListener: progressListener)
try await makeClientBuilder().buildWithQrCode(qrCodeData: qrCodeData,
oidcConfiguration: oidcConfiguration.rustValue,
progressListener: progressListener)
}
// MARK: - Private
/// The base builder configuration used for authentication within the app.
private func makeClientBuilder(slidingSync: ClientBuilderSlidingSync) -> ClientBuilder {
private func makeClientBuilder() -> ClientBuilder {
ClientBuilder
.baseBuilder(httpProxy: appSettings.websiteURL.globalProxy,
slidingSync: slidingSync,
slidingSync: .discover,
sessionDelegate: clientSessionDelegate,
appHooks: appHooks,
enableOnlySignedDeviceIsolationMode: appSettings.enableOnlySignedDeviceIsolationMode,

View File

@ -19,7 +19,7 @@ class ClientProxy: ClientProxyProtocol {
private let mediaLoader: MediaLoaderProtocol
private let clientQueue: DispatchQueue
private var roomListService: RoomListService?
// periphery: ignore - only for retain
private var roomListStateUpdateTaskHandle: TaskHandle?
@ -135,6 +135,7 @@ class ClientProxy: ClientProxyProtocol {
private let sendQueueStatusSubject = CurrentValueSubject<Bool, Never>(false)
init(client: ClientProtocol,
needsSlidingSyncMigration: Bool,
networkMonitor: NetworkMonitorProtocol,
appSettings: AppSettings) async {
self.client = client
@ -148,6 +149,8 @@ class ClientProxy: ClientProxyProtocol {
notificationSettings = NotificationSettingsProxy(notificationSettings: client.getNotificationSettings())
secureBackupController = SecureBackupController(encryption: client.encryption())
self.needsSlidingSyncMigration = needsSlidingSyncMigration
delegateHandle = client.setDelegate(delegate: ClientDelegateWrapper { [weak self] isSoftLogout in
self?.hasEncounteredAuthError = true
@ -221,16 +224,11 @@ class ClientProxy: ClientProxyProtocol {
client.homeserver()
}
let needsSlidingSyncMigration: Bool
var slidingSyncVersion: SlidingSyncVersion {
client.slidingSyncVersion()
}
var availableSlidingSyncVersions: [SlidingSyncVersion] {
get async {
await client.availableSlidingSyncVersions()
}
}
var canDeactivateAccount: Bool {
client.canDeactivateAccount()
}
@ -263,6 +261,11 @@ class ClientProxy: ClientProxyProtocol {
}
func startSync() {
guard !needsSlidingSyncMigration else {
MXLog.warning("Ignoring request, this client needs to be migrated to native sliding sync.")
return
}
guard !hasEncounteredAuthError else {
MXLog.warning("Ignoring request, this client has an unknown token.")
return

View File

@ -75,9 +75,11 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
var deviceID: String? { get }
var homeserver: String { get }
// TODO: This is a temporary value, in the future we should throw a migration error
// when decoding a session that contains a sliding sync proxy URL instead of restoring it.
var needsSlidingSyncMigration: Bool { get }
var slidingSyncVersion: SlidingSyncVersion { get }
var availableSlidingSyncVersions: [SlidingSyncVersion] { get async }
var canDeactivateAccount: Bool { get }

View File

@ -112,7 +112,8 @@ class KeychainController: KeychainControllerProtocol {
let restorationToken = RestorationToken(session: session,
sessionDirectories: oldToken.sessionDirectories,
passphrase: oldToken.passphrase,
pusherNotificationClientIdentifier: oldToken.pusherNotificationClientIdentifier)
pusherNotificationClientIdentifier: oldToken.pusherNotificationClientIdentifier,
slidingSyncProxyURLString: oldToken.slidingSyncProxyURLString)
setRestorationToken(restorationToken, forUsername: session.userId)
}

View File

@ -14,6 +14,13 @@ struct RestorationToken: Equatable {
let passphrase: String?
let pusherNotificationClientIdentifier: String?
/// The sliding sync proxy URL that was previously encoded in the Session.
/// This is temporary to help make a nicer user migration flow. In the future
/// we will throw when decoding sessions with a sliding sync proxy URL.
let slidingSyncProxyURLString: String?
/// Whether the token is for a session that is using the now unsupported sliding sync proxy.
var needsSlidingSyncMigration: Bool { slidingSyncProxyURLString != nil }
enum CodingKeys: CodingKey {
case session
case sessionDirectory
@ -27,7 +34,7 @@ extension RestorationToken: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let session = try container.decode(MatrixRustSDK.Session.self, forKey: .session)
let sessionWrapper = try container.decode(SessionWrapper.self, forKey: .session)
let dataDirectory = try container.decodeIfPresent(URL.self, forKey: .sessionDirectory)
let cacheDirectory = try container.decodeIfPresent(URL.self, forKey: .cacheDirectory)
@ -38,18 +45,19 @@ extension RestorationToken: Codable {
SessionDirectories(dataDirectory: dataDirectory)
}
} else {
SessionDirectories(userID: session.userId)
SessionDirectories(userID: sessionWrapper.session.userId)
}
self = try .init(session: session,
self = try .init(session: sessionWrapper.session,
sessionDirectories: sessionDirectories,
passphrase: container.decodeIfPresent(String.self, forKey: .passphrase),
pusherNotificationClientIdentifier: container.decodeIfPresent(String.self, forKey: .pusherNotificationClientIdentifier))
pusherNotificationClientIdentifier: container.decodeIfPresent(String.self, forKey: .pusherNotificationClientIdentifier),
slidingSyncProxyURLString: sessionWrapper.slidingSyncProxyURLString)
}
func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(session, forKey: .session)
try container.encode(SessionWrapper(session: session, slidingSyncProxyURLString: slidingSyncProxyURLString), forKey: .session)
try container.encode(sessionDirectories.dataDirectory, forKey: .sessionDirectory)
try container.encode(sessionDirectories.cacheDirectory, forKey: .cacheDirectory)
try container.encode(passphrase, forKey: .passphrase)
@ -57,17 +65,40 @@ extension RestorationToken: Codable {
}
}
/// Temporary struct to smooth the forced migration by keeping a user session.
/// In the future we can remove this and throw a migration error when the URL
/// is decoded to a non-nil value.
private struct SessionWrapper {
let session: MatrixRustSDK.Session
let slidingSyncProxyURLString: String?
}
extension SessionWrapper: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: MatrixRustSDK.Session.CodingKeys.self)
session = try Session(from: decoder)
// TODO: In the future we should decode this in the Session and throw a migration error if it contains a value.
slidingSyncProxyURLString = try container.decodeIfPresent(String.self, forKey: .slidingSyncProxy)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: MatrixRustSDK.Session.CodingKeys.self)
try session.encode(to: encoder)
try container.encode(slidingSyncProxyURLString, forKey: .slidingSyncProxy)
}
}
extension MatrixRustSDK.Session: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let slidingSyncProxy = try container.decodeIfPresent(String.self, forKey: .slidingSyncProxy)
self = try .init(accessToken: container.decode(String.self, forKey: .accessToken),
refreshToken: container.decodeIfPresent(String.self, forKey: .refreshToken),
userId: container.decode(String.self, forKey: .userId),
deviceId: container.decode(String.self, forKey: .deviceId),
homeserverUrl: container.decode(String.self, forKey: .homeserverUrl),
oidcData: container.decodeIfPresent(String.self, forKey: .oidcData),
slidingSyncVersion: slidingSyncProxy.map { .proxy(url: $0) } ?? .native)
slidingSyncVersion: .native)
}
public func encode(to encoder: Encoder) throws {
@ -78,17 +109,9 @@ extension MatrixRustSDK.Session: Codable {
try container.encode(deviceId, forKey: .deviceId)
try container.encode(homeserverUrl, forKey: .homeserverUrl)
try container.encode(oidcData, forKey: .oidcData)
try container.encode(slidingSyncVersion.proxyURL, forKey: .slidingSyncProxy)
}
enum CodingKeys: String, CodingKey {
case accessToken, refreshToken, userId, deviceId, homeserverUrl, oidcData, slidingSyncProxy
}
}
private extension SlidingSyncVersion {
var proxyURL: String? {
guard case let .proxy(url) = self else { return nil }
return url
}
}

View File

@ -64,12 +64,13 @@ class UserSessionStore: UserSessionStoreProtocol {
do {
let session = try client.session()
let userID = try client.userId()
let clientProxy = await setupProxyForClient(client)
let clientProxy = await setupProxyForClient(client, needsSlidingSyncMigration: false)
keychainController.setRestorationToken(RestorationToken(session: session,
sessionDirectories: sessionDirectories,
passphrase: passphrase,
pusherNotificationClientIdentifier: clientProxy.pusherNotificationClientIdentifier),
pusherNotificationClientIdentifier: clientProxy.pusherNotificationClientIdentifier,
slidingSyncProxyURLString: nil),
forUsername: userID)
MXLog.info("Set up session for user \(userID) at: \(sessionDirectories)")
@ -145,15 +146,16 @@ class UserSessionStore: UserSessionStoreProtocol {
MXLog.info("Set up session for user \(credentials.userID) at: \(credentials.restorationToken.sessionDirectories)")
return await .success(setupProxyForClient(client))
return await .success(setupProxyForClient(client, needsSlidingSyncMigration: credentials.restorationToken.needsSlidingSyncMigration))
} catch {
MXLog.error("Failed restoring login with error: \(error)")
return .failure(.failedRestoringLogin)
}
}
private func setupProxyForClient(_ client: ClientProtocol) async -> ClientProxyProtocol {
private func setupProxyForClient(_ client: ClientProtocol, needsSlidingSyncMigration: Bool) async -> ClientProxyProtocol {
await ClientProxy(client: client,
needsSlidingSyncMigration: needsSlidingSyncMigration,
networkMonitor: networkMonitor,
appSettings: appSettings)
}

View File

@ -22,8 +22,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams",
"state" : {
"revision" : "db9ff235cf800bc657c3ed0961078fc231d0f28b",
"version" : "5.2.0"
"revision" : "2688707e563b44d7d87c29ba6c5ca04ce86ae58b",
"version" : "5.3.0"
}
}
],

View File

@ -275,12 +275,6 @@ extension PreviewTests {
}
}
func test_homeScreenSlidingSyncMigrationBanner() async throws {
for preview in HomeScreenSlidingSyncMigrationBanner_Previews._allPreviews {
try await assertSnapshots(matching: preview)
}
}
func test_homeScreen() async throws {
for preview in HomeScreen_Previews._allPreviews {
try await assertSnapshots(matching: preview)

View File

@ -30,10 +30,11 @@ class KeychainControllerTests: XCTestCase {
deviceId: "deviceId",
homeserverUrl: "homeserverUrl",
oidcData: "oidcData",
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
slidingSyncVersion: .native),
sessionDirectories: .init(),
passphrase: "passphrase",
pusherNotificationClientIdentifier: "pusherClientID")
pusherNotificationClientIdentifier: "pusherClientID",
slidingSyncProxyURLString: "https://my.sync.proxy")
keychain.setRestorationToken(restorationToken, forUsername: username)
// Then the restoration token should be stored in the keychain.
@ -49,10 +50,11 @@ class KeychainControllerTests: XCTestCase {
deviceId: "deviceId",
homeserverUrl: "homeserverUrl",
oidcData: "oidcData",
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
slidingSyncVersion: .native),
sessionDirectories: .init(),
passphrase: "passphrase",
pusherNotificationClientIdentifier: "pusherClientID")
pusherNotificationClientIdentifier: "pusherClientID",
slidingSyncProxyURLString: "https://my.sync.proxy")
keychain.setRestorationToken(restorationToken, forUsername: username)
XCTAssertEqual(keychain.restorationTokens().count, 1, "The keychain should have 1 restoration token.")
XCTAssertEqual(keychain.restorationTokenForUsername(username), restorationToken, "The initial restoration token should match the value that was stored.")
@ -74,10 +76,11 @@ class KeychainControllerTests: XCTestCase {
deviceId: "deviceId",
homeserverUrl: "homeserverUrl",
oidcData: "oidcData",
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
slidingSyncVersion: .native),
sessionDirectories: .init(),
passphrase: "passphrase",
pusherNotificationClientIdentifier: "pusherClientID")
pusherNotificationClientIdentifier: "pusherClientID",
slidingSyncProxyURLString: "https://my.sync.proxy")
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
}
XCTAssertEqual(keychain.restorationTokens().count, 5, "The keychain should have 5 restoration tokens.")
@ -98,10 +101,11 @@ class KeychainControllerTests: XCTestCase {
deviceId: "deviceId",
homeserverUrl: "homeserverUrl",
oidcData: "oidcData",
slidingSyncVersion: .proxy(url: "https://my.sync.proxy")),
slidingSyncVersion: .native),
sessionDirectories: .init(),
passphrase: "passphrase",
pusherNotificationClientIdentifier: "pusherClientID")
pusherNotificationClientIdentifier: "pusherClientID",
slidingSyncProxyURLString: "https://my.sync.proxy")
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
}
XCTAssertEqual(keychain.restorationTokens().count, 5, "The keychain should have 5 restoration tokens.")
@ -133,7 +137,8 @@ class KeychainControllerTests: XCTestCase {
slidingSyncVersion: .native),
sessionDirectories: .init(),
passphrase: "passphrase",
pusherNotificationClientIdentifier: "pusherClientID")
pusherNotificationClientIdentifier: "pusherClientID",
slidingSyncProxyURLString: nil)
keychain.setRestorationToken(restorationToken, forUsername: username)
// Then decoding the restoration token from the keychain should still work.

View File

@ -13,38 +13,42 @@ import MatrixRustSDK
class RestorationTokenTests: XCTestCase {
func testDecodeFromTokenV1() throws {
// Given an encoded restoration token in the original format that only contains a Session from the SDK.
let originalToken = RestorationTokenV1(session: Session(accessToken: "1234",
refreshToken: nil,
userId: "@user:example.com",
deviceId: "D3V1C3",
homeserverUrl: "https://matrix.example.com",
oidcData: nil,
slidingSyncVersion: .proxy(url: "https://sync.example.com")))
let originalToken = RestorationTokenV1(session: SessionV1(accessToken: "1234",
refreshToken: nil,
userId: "@user:example.com",
deviceId: "D3V1C3",
homeserverUrl: "https://matrix.example.com",
oidcData: nil,
slidingSyncVersion: .proxy(url: "https://sync.example.com")))
let data = try JSONEncoder().encode(originalToken)
// When decoding the data to the current restoration token format.
let decodedToken = try JSONDecoder().decode(RestorationToken.self, from: data)
// Then the output should be a valid token with the expected store directories.
XCTAssertEqual(decodedToken.session, originalToken.session, "The session should not be changed.")
assertEqual(session: decodedToken.session, originalSession: originalToken.session)
XCTAssertNil(decodedToken.passphrase, "There should not be a passphrase.")
XCTAssertNil(decodedToken.pusherNotificationClientIdentifier, "There should not be a push notification client ID.")
XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, .sessionsBaseDirectory.appending(component: "@user_example.com"),
"The session directory should match the original location set by the Rust SDK from our base directory.")
XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: "@user_example.com"),
"The cache directory should be derived from the session directory but in the caches directory.")
XCTAssertEqual(decodedToken.slidingSyncProxyURLString, "https://sync.example.com",
"The original sliding sync URL should be preserved in order to trigger the migration prompt.")
XCTAssertTrue(decodedToken.needsSlidingSyncMigration, "The migration flag should be set to true.")
}
func testDecodeFromTokenV4() throws {
// Given an encoded restoration token in the 4th format that contains a stored session directory.
let sessionDirectoryName = UUID().uuidString
let originalToken = RestorationTokenV4(session: Session(accessToken: "1234",
refreshToken: "5678",
userId: "@user:example.com",
deviceId: "D3V1C3",
homeserverUrl: "https://matrix.example.com",
oidcData: "data-from-mas",
slidingSyncVersion: .proxy(url: "https://sync.example.com")),
let originalToken = RestorationTokenV4(session: SessionV1(accessToken: "1234",
refreshToken: "5678",
userId: "@user:example.com",
deviceId: "D3V1C3",
homeserverUrl: "https://matrix.example.com",
oidcData: "data-from-mas",
slidingSyncVersion: .proxy(url: "https://sync.example.com")),
sessionDirectory: .sessionsBaseDirectory.appending(component: sessionDirectoryName),
passphrase: "passphrase",
pusherNotificationClientIdentifier: "pusher-identifier")
@ -54,7 +58,7 @@ class RestorationTokenTests: XCTestCase {
let decodedToken = try JSONDecoder().decode(RestorationToken.self, from: data)
// Then the output should be a valid token with the expected store directories.
XCTAssertEqual(decodedToken.session, originalToken.session, "The session should not be changed.")
assertEqual(session: decodedToken.session, originalSession: originalToken.session)
XCTAssertEqual(decodedToken.passphrase, originalToken.passphrase, "The passphrase should not be changed.")
XCTAssertEqual(decodedToken.pusherNotificationClientIdentifier, originalToken.pusherNotificationClientIdentifier,
"The push notification client identifier should not be changed.")
@ -62,18 +66,22 @@ class RestorationTokenTests: XCTestCase {
"The session directory should not be changed.")
XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: sessionDirectoryName),
"The cache directory should be derived from the session directory but in the caches directory.")
XCTAssertEqual(decodedToken.slidingSyncProxyURLString, "https://sync.example.com",
"The original sliding sync URL should be preserved in order to trigger the migration prompt.")
XCTAssertTrue(decodedToken.needsSlidingSyncMigration, "The migration flag should be set to true.")
}
func testDecodeFromTokenV5() throws {
// Given an encoded restoration token in the 5th format that contains separate directories for session data and caches.
let sessionDirectoryName = UUID().uuidString
let originalToken = RestorationTokenV5(session: Session(accessToken: "1234",
refreshToken: "5678",
userId: "@user:example.com",
deviceId: "D3V1C3",
homeserverUrl: "https://matrix.example.com",
oidcData: "data-from-mas",
slidingSyncVersion: .native),
let originalToken = RestorationTokenV5(session: SessionV1(accessToken: "1234",
refreshToken: "5678",
userId: "@user:example.com",
deviceId: "D3V1C3",
homeserverUrl: "https://matrix.example.com",
oidcData: "data-from-mas",
slidingSyncVersion: .native),
sessionDirectory: .sessionsBaseDirectory.appending(component: sessionDirectoryName),
cacheDirectory: .sessionCachesBaseDirectory.appending(component: sessionDirectoryName),
passphrase: "passphrase",
@ -84,7 +92,7 @@ class RestorationTokenTests: XCTestCase {
let decodedToken = try JSONDecoder().decode(RestorationToken.self, from: data)
// Then the output should be a valid token.
XCTAssertEqual(decodedToken.session, originalToken.session, "The session should not be changed.")
assertEqual(session: decodedToken.session, originalSession: originalToken.session)
XCTAssertEqual(decodedToken.passphrase, originalToken.passphrase, "The passphrase should not be changed.")
XCTAssertEqual(decodedToken.pusherNotificationClientIdentifier, originalToken.pusherNotificationClientIdentifier,
"The push notification client identifier should not be changed.")
@ -92,6 +100,9 @@ class RestorationTokenTests: XCTestCase {
"The session directory should not be changed.")
XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, originalToken.cacheDirectory,
"The cache directory should not be changed.")
XCTAssertNil(decodedToken.slidingSyncProxyURLString, "No sliding sync proxy URL should be decoded for native sliding sync.")
XCTAssertFalse(decodedToken.needsSlidingSyncMigration, "The migration flag should not be set.")
}
func testDecodeFromCurrentToken() throws {
@ -105,7 +116,8 @@ class RestorationTokenTests: XCTestCase {
slidingSyncVersion: .native),
sessionDirectories: .init(),
passphrase: "passphrase",
pusherNotificationClientIdentifier: "pusher-identifier")
pusherNotificationClientIdentifier: "pusher-identifier",
slidingSyncProxyURLString: nil)
let data = try JSONEncoder().encode(originalToken)
// When decoding the data.
@ -114,23 +126,86 @@ class RestorationTokenTests: XCTestCase {
// Then the output should be a valid token.
XCTAssertEqual(decodedToken, originalToken, "The token should remain identical.")
}
func assertEqual(session: Session, originalSession: SessionV1) {
XCTAssertEqual(session.accessToken, originalSession.accessToken, "The access token should not be changed.")
XCTAssertEqual(session.refreshToken, originalSession.refreshToken, "The refresh token should not be changed.")
XCTAssertEqual(session.userId, originalSession.userId, "The user ID should not be changed.")
XCTAssertEqual(session.deviceId, originalSession.deviceId, "The device ID should not be changed.")
XCTAssertEqual(session.homeserverUrl, originalSession.homeserverUrl, "The homeserver URL should not be changed.")
XCTAssertEqual(session.oidcData, originalSession.oidcData, "The OIDC data should not be changed.")
}
}
// MARK: - Token formats
struct RestorationTokenV1: Equatable, Codable {
let session: MatrixRustSDK.Session
let session: SessionV1
}
struct RestorationTokenV4: Equatable, Codable {
let session: MatrixRustSDK.Session
let session: SessionV1
let sessionDirectory: URL
let passphrase: String?
let pusherNotificationClientIdentifier: String?
}
struct RestorationTokenV5: Equatable, Codable {
let session: MatrixRustSDK.Session
let session: SessionV1
let sessionDirectory: URL
let cacheDirectory: URL
let passphrase: String?
let pusherNotificationClientIdentifier: String?
}
// MARK: - Session formats
struct SessionV1: Equatable {
var accessToken: String
var refreshToken: String?
var userId: String
var deviceId: String
var homeserverUrl: String
var oidcData: String?
var slidingSyncVersion: SlidingSyncVersionV1
}
enum SlidingSyncVersionV1: Equatable {
case none
case proxy(url: String)
case native
var proxyURL: String? {
guard case let .proxy(url) = self else { return nil }
return url
}
}
extension SessionV1: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let slidingSyncProxy = try container.decodeIfPresent(String.self, forKey: .slidingSyncProxy)
self = try .init(accessToken: container.decode(String.self, forKey: .accessToken),
refreshToken: container.decodeIfPresent(String.self, forKey: .refreshToken),
userId: container.decode(String.self, forKey: .userId),
deviceId: container.decode(String.self, forKey: .deviceId),
homeserverUrl: container.decode(String.self, forKey: .homeserverUrl),
oidcData: container.decodeIfPresent(String.self, forKey: .oidcData),
slidingSyncVersion: slidingSyncProxy.map { .proxy(url: $0) } ?? .native)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(accessToken, forKey: .accessToken)
try container.encode(refreshToken, forKey: .refreshToken)
try container.encode(userId, forKey: .userId)
try container.encode(deviceId, forKey: .deviceId)
try container.encode(homeserverUrl, forKey: .homeserverUrl)
try container.encode(oidcData, forKey: .oidcData)
try container.encode(slidingSyncVersion.proxyURL, forKey: .slidingSyncProxy)
}
enum CodingKeys: String, CodingKey {
case accessToken, refreshToken, userId, deviceId, homeserverUrl, oidcData, slidingSyncProxy
}
}

View File

@ -61,7 +61,7 @@ packages:
# Element/Matrix dependencies
MatrixRustSDK:
url: https://github.com/element-hq/matrix-rust-components-swift
exactVersion: 25.02.11
exactVersion: 25.02.17
# path: ../matrix-rust-sdk
Compound:
url: https://github.com/element-hq/compound-ios