diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 18b086ea5..d6dcc030b 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -91,6 +91,7 @@ 24A75F72EEB7561B82D726FD /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2141693488CE5446BB391964 /* Date.swift */; }; 24BDDD09A90B8BFE3793F3AA /* ClientProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6033779EB37259F27F938937 /* ClientProxyProtocol.swift */; }; 25618589E0DE0F1E95FC7B5C /* EmojiProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */; }; + 274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97CE98208321C4D66E363612 /* ShimmerModifier.swift */; }; 2797C9D9BA642370F1C85D78 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; }; 27E9263DA75E266690A37EB1 /* PermalinkBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB31A32C93D94930B253FBF /* PermalinkBuilderTests.swift */; }; 282A5F3375DDC774AE09B0C3 /* TracingConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1734A445A58ED855B977A0A8 /* TracingConfigurationTests.swift */; }; @@ -293,7 +294,6 @@ 8F2FAA98457750D9D664136F /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 997C7385E1A07E061D7E2100 /* GZIP */; }; 90DF83A6A347F7EE7EDE89EE /* AttributedStringBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */; }; 90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; }; - 916D6679298D6F900071EF0B /* ShimmerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916D6678298D6F900071EF0B /* ShimmerModifier.swift */; }; 91DFCB641FBA03EE2DA0189E /* FilePreviewScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB27E1BE894F9F9F0134372 /* FilePreviewScreen.swift */; }; 9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; }; 92B95779840CD749117B3615 /* EmojiMartStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38AE3617D7619EF30CDD229 /* EmojiMartStore.swift */; }; @@ -709,7 +709,7 @@ 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxyProtocol.swift; sourceTree = ""; }; 471EB7D96AFEA8D787659686 /* EmoteRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineView.swift; sourceTree = ""; }; 475EB595D7527E9A8A14043E /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/Localizable.strings; sourceTree = ""; }; - 478BE8591BD13E908EF70C0C /* DesignKit */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DesignKit; sourceTree = SOURCE_ROOT; }; + 478BE8591BD13E908EF70C0C /* DesignKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DesignKit; path = DesignKit; sourceTree = SOURCE_ROOT; }; 4798B3B7A1E8AE3901CEE8C6 /* FramePreferenceKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramePreferenceKey.swift; sourceTree = ""; }; 47EBB5D698CE9A25BB553A2D /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; 48CE6BF18E542B32FA52CE06 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fa; path = fa.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -842,7 +842,7 @@ 8D6094DEAAEB388E1AE118C6 /* MockRoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineProvider.swift; sourceTree = ""; }; 8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; 8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = ""; }; - 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; sourceTree = ""; }; + 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = ""; }; 8EC57A32ABC80D774CC663DB /* SettingsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenUITests.swift; sourceTree = ""; }; 8ED2D2F6A137A95EA50413BE /* UserNotificationControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationControllerProtocol.swift; sourceTree = ""; }; 8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomSummaryProvider.swift; sourceTree = ""; }; @@ -851,7 +851,6 @@ 9010EE0CC913D095887EF36E /* OIDCService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCService.swift; sourceTree = ""; }; 9080CDD3881D0D1B2F280A7C /* MockUserNotificationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserNotificationController.swift; sourceTree = ""; }; 90A55430639712CFACA34F43 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = ""; }; - 916D6678298D6F900071EF0B /* ShimmerModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShimmerModifier.swift; sourceTree = ""; }; 91FB6F5ECCF51ECE98ACFEEC /* RoomDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsViewModel.swift; sourceTree = ""; }; 9238D3A3A00F45E841FE4EFF /* DebugScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugScreen.swift; sourceTree = ""; }; 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTask.swift; sourceTree = ""; }; @@ -864,6 +863,7 @@ 96C4762F8D6112E43117DB2F /* CustomStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStringConvertible.swift; sourceTree = ""; }; 9772C1D2223108EB3131AEE4 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Localizable.strings"; sourceTree = ""; }; 97755C01C3971474EFAD5367 /* AuthenticationIconImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationIconImage.swift; sourceTree = ""; }; + 97CE98208321C4D66E363612 /* ShimmerModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShimmerModifier.swift; sourceTree = ""; }; 97F893DBB5F88D746C6DCDE5 /* ku */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ku; path = ku.lproj/Localizable.strings; sourceTree = ""; }; 98273EE22BC18E85C645329C /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/Localizable.strings; sourceTree = ""; }; 9873076F224E4CE09D8BD47D /* TemplateScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenUITests.swift; sourceTree = ""; }; @@ -1065,7 +1065,7 @@ EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsViewModelTests.swift; sourceTree = ""; }; ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = ""; }; ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = ""; }; - ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = ""; }; + ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = ""; }; ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRoomTimelineView.swift; sourceTree = ""; }; EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = ""; }; EDB6E40BAD4504D899FAAC9A /* TemplateViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateViewModel.swift; sourceTree = ""; }; @@ -2389,8 +2389,8 @@ E2DA161C142B7AB8CC40F752 /* Animation */ = { isa = PBXGroup; children = ( - 916D6678298D6F900071EF0B /* ShimmerModifier.swift */, EF1593DD87F974F8509BB619 /* ElementAnimations.swift */, + 97CE98208321C4D66E363612 /* ShimmerModifier.swift */, ); path = Animation; sourceTree = ""; @@ -3261,6 +3261,7 @@ DBAA69CC2CE4D44BC8E20105 /* SettingsScreenModels.swift in Sources */, EEC499F9AC7DD6D18760F81D /* SettingsScreenViewModel.swift in Sources */, 50C59870BEB1F29C60252FD4 /* SettingsScreenViewModelProtocol.swift in Sources */, + 274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */, 6E6E0AAF6C44C0B117EBBE5A /* SlidingSyncViewProxy.swift in Sources */, 2276870A19F34B3FFFDA690F /* SoftLogoutCoordinator.swift in Sources */, 214C6B416609E58CCBF6DCEE /* SoftLogoutModels.swift in Sources */, @@ -3315,7 +3316,6 @@ E1F446C6B78A3A0FEA15079C /* UnsupportedRoomTimelineView.swift in Sources */, 7A71AEF419904209BB8C2833 /* UserAgentBuilder.swift in Sources */, 87BD4F95F9D603C309837378 /* UserNotification.swift in Sources */, - 916D6679298D6F900071EF0B /* ShimmerModifier.swift in Sources */, E3291AD16D7A5CB14781819C /* UserNotificationCenterProtocol.swift in Sources */, 5D9F0695DC6C0057F85C12B6 /* UserNotificationController.swift in Sources */, D79F0F852C6A4255D5E616D2 /* UserNotificationControllerProtocol.swift in Sources */, @@ -3720,7 +3720,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.0; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0.19; + MARKETING_VERSION = 1.0.20; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3792,7 +3792,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.0; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0.19; + MARKETING_VERSION = 1.0.20; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -4020,7 +4020,7 @@ repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = "1.0.37-alpha"; + version = "1.0.38-alpha"; }; }; 96495DD8554E2F39D3954354 /* XCRemoteSwiftPackageReference "posthog-ios" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 70d8a4617..44b3ea86f 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -86,8 +86,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-rust-components-swift", "state" : { - "revision" : "79ead18b0db04f898d5ba9ce1a0b609a518f44ec", - "version" : "1.0.37-alpha" + "revision" : "986c400f8d4d0f9d467370f98d70b6b742e90e18", + "version" : "1.0.38-alpha" } }, { diff --git a/ElementX/Resources/Localizations/en.lproj/Untranslated.strings b/ElementX/Resources/Localizations/en.lproj/Untranslated.strings index 6f5fdaebc..ee73a44bb 100644 --- a/ElementX/Resources/Localizations/en.lproj/Untranslated.strings +++ b/ElementX/Resources/Localizations/en.lproj/Untranslated.strings @@ -51,6 +51,8 @@ "session_verification_start" = "Start"; "server_selection_server_footer" = "You can only connect to an existing server"; +"server_selection_sliding_sync_alert_title" = "Server not supported"; +"server_selection_sliding_sync_alert_message" = "This server currently doesn’t support sliding sync."; "login_mobile_device" = "Mobile"; "login_tablet_device" = "Tablet"; diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index dc98396bb..2a2d34483 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -136,9 +136,10 @@ class AppCoordinator: AppCoordinatorProtocol { private func performMigrationsIfNecessary(from oldVersion: Version, to newVersion: Version) { guard oldVersion != newVersion else { return } - if oldVersion < Version(1, 0, 17) { - // Version 1.0.17 hardcoded a new sliding sync proxy for matrix.org - // Force a sign out for the user to log in with the new proxy. + if oldVersion < Version(1, 0, 20) { + // Version 1.0.20 introduced a new format for restoration tokens. + // Remove user data to prevent conflict in the crypto store + // with the newly generated device ID when signing in again. MXLog.warning("Clearing user data for hardcoded proxy.") wipeUserData() } diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 07710a323..8c4fb3295 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -24,7 +24,6 @@ final class AppSettings: ObservableObject { case timelineStyle case enableAnalytics case isIdentifiedForAnalytics - case slidingSyncProxyBaseURLString case enableInAppNotifications case pusherProfileTag } @@ -62,7 +61,12 @@ final class AppSettings: ObservableObject { /// An override of the homeserver's Sliding Sync proxy URL. This allows development against servers /// that don't yet have an officially trusted proxy configured in their well-known. - let slidingSyncProxyURL = URL(staticString: "https://slidingsync.lab.matrix.org") + let slidingSyncProxyURL: URL? = nil + + // MARK: - Authentication + + /// The URL that is opened when tapping the Learn more button on the sliding sync alert during authentication. + let slidingSyncLearnMoreURL = URL(staticString: "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md") // MARK: - Notifications diff --git a/ElementX/Sources/Generated/Strings+Untranslated.swift b/ElementX/Sources/Generated/Strings+Untranslated.swift index c23ebae7e..52ee744e7 100644 --- a/ElementX/Sources/Generated/Strings+Untranslated.swift +++ b/ElementX/Sources/Generated/Strings+Untranslated.swift @@ -128,6 +128,10 @@ extension ElementL10n { public static let screenshotDetectedTitle = ElementL10n.tr("Untranslated", "screenshot_detected_title") /// You can only connect to an existing server public static let serverSelectionServerFooter = ElementL10n.tr("Untranslated", "server_selection_server_footer") + /// This server currently doesn’t support sliding sync. + public static let serverSelectionSlidingSyncAlertMessage = ElementL10n.tr("Untranslated", "server_selection_sliding_sync_alert_message") + /// Server not supported + public static let serverSelectionSlidingSyncAlertTitle = ElementL10n.tr("Untranslated", "server_selection_sliding_sync_alert_title") /// Looks like you’re using a new device. Verify it’s you to access your encrypted messages. public static let sessionVerificationBannerMessage = ElementL10n.tr("Untranslated", "session_verification_banner_message") /// Access your message history diff --git a/ElementX/Sources/Other/SwiftUI/ErrorHandling/AlertInfo.swift b/ElementX/Sources/Other/SwiftUI/ErrorHandling/AlertInfo.swift index 46219710d..121e05154 100644 --- a/ElementX/Sources/Other/SwiftUI/ErrorHandling/AlertInfo.swift +++ b/ElementX/Sources/Other/SwiftUI/ErrorHandling/AlertInfo.swift @@ -38,7 +38,12 @@ struct AlertInfo: Identifiable { struct AlertButton { let title: String + var role: Role = .default let action: (() -> Void)? + + enum Role { + case `default`, cancel, destructive + } } extension AlertInfo { @@ -85,10 +90,19 @@ extension AlertInfo { } private func alertButton(for buttonParameters: AlertButton) -> Alert.Button { - guard let action = buttonParameters.action else { + switch (buttonParameters.role, buttonParameters.action) { + case (.default, nil): return .default(Text(buttonParameters.title)) + case (.default, let action): + return .default(Text(buttonParameters.title), action: action) + case (.cancel, nil): + return .cancel(Text(buttonParameters.title)) + case (.cancel, let action): + return .cancel(Text(buttonParameters.title), action: action) + case (.destructive, nil): + return .destructive(Text(buttonParameters.title)) + case (.destructive, let action): + return .destructive(Text(buttonParameters.title), action: action) } - - return .default(Text(buttonParameters.title), action: action) } } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginCoordinator.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginCoordinator.swift index beb08c51b..501348879 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginCoordinator.swift @@ -124,6 +124,8 @@ final class LoginCoordinator: CoordinatorProtocol { viewModel.displayError(.alert(ElementL10n.authInvalidLoginParam)) case .accountDeactivated: viewModel.displayError(.alert(ElementL10n.authInvalidLoginDeactivatedAccount)) + case .slidingSyncNotAvailable: + viewModel.displayError(.slidingSyncAlert) default: viewModel.displayError(.alert(ElementL10n.unknownError)) } @@ -197,10 +199,8 @@ final class LoginCoordinator: CoordinatorProtocol { /// Presents the server selection screen as a modal. private func presentServerSelectionScreen() { - let userNotificationController = UserNotificationController(rootCoordinator: navigationStackCoordinator) - let parameters = ServerSelectionCoordinatorParameters(authenticationService: authenticationService, - userNotificationController: userNotificationController, + userNotificationController: ServiceLocator.shared.userNotificationController, isModallyPresented: false) let coordinator = ServerSelectionCoordinator(parameters: parameters) diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginModels.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginModels.swift index 5a0f63d7e..bb286c010 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginModels.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginModels.swift @@ -94,6 +94,8 @@ enum LoginErrorType: Hashable { case alert(String) /// Looking up the homeserver from the username failed. case invalidHomeserver + /// An alert that allows the user to learn about sliding sync. + case slidingSyncAlert /// The response from the homeserver was unexpected. case unknown } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginViewModel.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginViewModel.swift index d3dccc2c0..2e25f3746 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginViewModel.swift @@ -62,6 +62,16 @@ class LoginViewModel: LoginViewModelType, LoginViewModelProtocol { state.bindings.alertInfo = AlertInfo(id: type, title: ElementL10n.dialogTitleError, message: ElementL10n.loginSigninMatrixIdErrorInvalidMatrixId) + case .slidingSyncAlert: + let openURL = { UIApplication.shared.open(ServiceLocator.shared.settings.slidingSyncLearnMoreURL) } + state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert, + title: ElementL10n.serverSelectionSlidingSyncAlertTitle, + message: ElementL10n.serverSelectionSlidingSyncAlertMessage, + primaryButton: .init(title: ElementL10n.actionLearnMore, role: .cancel, action: openURL), + secondaryButton: .init(title: ElementL10n.actionCancel, action: nil)) + + // Clear out the invalid username to avoid an attempted login to matrix.org + state.bindings.username = "" case .unknown: state.bindings.alertInfo = AlertInfo(id: type) } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift index eb153af53..3079c2ebc 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift @@ -64,7 +64,6 @@ struct LoginScreen: View { LoginServerInfoSection(address: context.viewState.homeserver.address) { context.send(viewAction: .selectServer) } - .disabled(true) // The button is disabled for this demo. } /// The form with text fields for username and password, along with a submit button. @@ -96,14 +95,6 @@ struct LoginScreen: View { .onSubmit(submit) .accessibilityIdentifier("passwordTextField") - // uncomment this piece of code once forgot password will be available -// Button { context.send(viewAction: .forgotPassword) } label: { -// Text(ElementL10n.ftueAuthForgotPassword) -// .font(.element.body) -// } -// .frame(maxWidth: .infinity, alignment: .trailing) -// .padding(.bottom, 8) - Spacer().frame(height: 32) Button(action: submit) { diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginServerInfoSection.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginServerInfoSection.swift index d23e06bef..7a282edce 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginServerInfoSection.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginServerInfoSection.swift @@ -48,7 +48,6 @@ struct LoginServerInfoSection: View { Image(systemName: "chevron.right") .foregroundColor(.element.tertiaryContent) .padding(.trailing, 16) - .hidden() // The button is disabled for this demo. } .background(RoundedRectangle(cornerRadius: 14).fill(Color.element.system)) } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionCoordinator.swift b/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionCoordinator.swift index edf5e46d6..593de46dd 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionCoordinator.swift @@ -102,6 +102,8 @@ final class ServerSelectionCoordinator: CoordinatorProtocol { switch error { case .invalidServer, .invalidHomeserverAddress: viewModel.displayError(.footerMessage(ElementL10n.loginErrorHomeserverNotFound)) + case .slidingSyncNotAvailable: + viewModel.displayError(.slidingSyncAlert) default: viewModel.displayError(.footerMessage(ElementL10n.unknownError)) } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionModels.swift b/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionModels.swift index ac1f1ba40..2724cc4de 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionModels.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionModels.swift @@ -71,4 +71,6 @@ enum ServerSelectionViewAction { enum ServerSelectionErrorType: Hashable { /// An error message to be shown in the text field footer. case footerMessage(String) + /// An alert that allows the user to learn about sliding sync. + case slidingSyncAlert } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionViewModel.swift b/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionViewModel.swift index 93d42214a..e4ccf4ca3 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelection/ServerSelectionViewModel.swift @@ -45,6 +45,13 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie withElementAnimation { state.footerErrorMessage = message } + case .slidingSyncAlert: + let openURL = { UIApplication.shared.open(ServiceLocator.shared.settings.slidingSyncLearnMoreURL) } + state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert, + title: ElementL10n.serverSelectionSlidingSyncAlertTitle, + message: ElementL10n.serverSelectionSlidingSyncAlertMessage, + primaryButton: .init(title: ElementL10n.actionLearnMore, role: .cancel, action: openURL), + secondaryButton: .init(title: ElementL10n.actionCancel, action: nil)) } } diff --git a/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift b/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift index 6e49a6853..335cd9f21 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift @@ -26,7 +26,9 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol { init(userSessionStore: UserSessionStoreProtocol) { self.userSessionStore = userSessionStore - authenticationService = AuthenticationService(basePath: userSessionStore.baseDirectory.path, passphrase: nil) + authenticationService = AuthenticationService(basePath: userSessionStore.baseDirectory.path, + passphrase: nil, + customSlidingSyncProxy: ServiceLocator.shared.settings.slidingSyncProxyURL?.absoluteString) } // MARK: - Public @@ -51,6 +53,9 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol { self.homeserver = homeserver return .success(()) + } catch AuthenticationError.SlidingSyncNotAvailable { + MXLog.info("User entered a homeserver that isn't configured for sliding sync.") + return .failure(.slidingSyncNotAvailable) } catch { MXLog.error("Failed configuring a server: \(error)") return .failure(.invalidHomeserverAddress) diff --git a/ElementX/Sources/Services/Authentication/AuthenticationServiceProxyProtocol.swift b/ElementX/Sources/Services/Authentication/AuthenticationServiceProxyProtocol.swift index b57ec1069..f9325c94f 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationServiceProxyProtocol.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationServiceProxyProtocol.swift @@ -23,6 +23,7 @@ enum AuthenticationServiceError: Error { case invalidServer case invalidCredentials case invalidHomeserverAddress + case slidingSyncNotAvailable case accountDeactivated case failedLoggingIn } diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 7b3e996b1..83569782c 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -254,7 +254,7 @@ class ClientProxy: ClientProxyProtocol { } do { - let slidingSyncBuilder = try client.slidingSync().homeserver(url: ServiceLocator.shared.settings.slidingSyncProxyURL.absoluteString) + let slidingSyncBuilder = client.slidingSync() // Build the visibleRoomsSlidingSyncView here so that it can take advantage of the SS builder cold cache // We will still register the allRoomsSlidingSyncView later, and than will have no cache diff --git a/ElementX/Sources/Services/UserSession/RestorationToken.swift b/ElementX/Sources/Services/UserSession/RestorationToken.swift index 92c2c10cd..e84f6ef40 100644 --- a/ElementX/Sources/Services/UserSession/RestorationToken.swift +++ b/ElementX/Sources/Services/UserSession/RestorationToken.swift @@ -29,7 +29,8 @@ extension MatrixRustSDK.Session: Codable { userId: try container.decode(String.self, forKey: .userId), deviceId: try container.decode(String.self, forKey: .deviceId), homeserverUrl: try container.decode(String.self, forKey: .homeserverUrl), - isSoftLogout: try container.decode(Bool.self, forKey: .isSoftLogout)) + isSoftLogout: try container.decode(Bool.self, forKey: .isSoftLogout), + slidingSyncProxy: try container.decode(String.self, forKey: .slidingSyncProxy)) } public func encode(to encoder: Encoder) throws { @@ -40,9 +41,10 @@ extension MatrixRustSDK.Session: Codable { try container.encode(deviceId, forKey: .deviceId) try container.encode(homeserverUrl, forKey: .homeserverUrl) try container.encode(isSoftLogout, forKey: .isSoftLogout) + try container.encode(slidingSyncProxy, forKey: .slidingSyncProxy) } enum CodingKeys: String, CodingKey { - case accessToken, refreshToken, userId, deviceId, homeserverUrl, isSoftLogout + case accessToken, refreshToken, userId, deviceId, homeserverUrl, isSoftLogout, slidingSyncProxy } } diff --git a/UnitTests/Sources/KeychainControllerTests.swift b/UnitTests/Sources/KeychainControllerTests.swift index 6ed3c8f66..f97e2a70a 100644 --- a/UnitTests/Sources/KeychainControllerTests.swift +++ b/UnitTests/Sources/KeychainControllerTests.swift @@ -32,7 +32,13 @@ class KeychainControllerTests: XCTestCase { // When adding an restoration token. let username = "@test:example.com" - let restorationToken = RestorationToken(session: .init(accessToken: "accessToken", refreshToken: "refreshToken", userId: "userId", deviceId: "deviceId", homeserverUrl: "homeserverUrl", isSoftLogout: false)) + let restorationToken = RestorationToken(session: .init(accessToken: "accessToken", + refreshToken: "refreshToken", + userId: "userId", + deviceId: "deviceId", + homeserverUrl: "homeserverUrl", + isSoftLogout: false, + slidingSyncProxy: "https://my.sync.proxy")) keychain.setRestorationToken(restorationToken, forUsername: username) // Then the restoration token should be stored in the keychain. @@ -42,7 +48,13 @@ class KeychainControllerTests: XCTestCase { func testRemovingRestorationToken() { // Given a keychain with a stored restoration token. let username = "@test:example.com" - let restorationToken = RestorationToken(session: .init(accessToken: "accessToken", refreshToken: "refreshToken", userId: "userId", deviceId: "deviceId", homeserverUrl: "homeserverUrl", isSoftLogout: false)) + let restorationToken = RestorationToken(session: .init(accessToken: "accessToken", + refreshToken: "refreshToken", + userId: "userId", + deviceId: "deviceId", + homeserverUrl: "homeserverUrl", + isSoftLogout: false, + slidingSyncProxy: "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.") @@ -58,7 +70,13 @@ class KeychainControllerTests: XCTestCase { func testRemovingAllRestorationTokens() { // Given a keychain with 5 stored restoration tokens. for index in 0..<5 { - let restorationToken = RestorationToken(session: .init(accessToken: "accessToken", refreshToken: "refreshToken", userId: "userId", deviceId: "deviceId", homeserverUrl: "homeserverUrl", isSoftLogout: false)) + let restorationToken = RestorationToken(session: .init(accessToken: "accessToken", + refreshToken: "refreshToken", + userId: "userId", + deviceId: "deviceId", + homeserverUrl: "homeserverUrl", + isSoftLogout: false, + slidingSyncProxy: "https://my.sync.proxy")) keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com") } XCTAssertEqual(keychain.restorationTokens().count, 5, "The keychain should have 5 restoration tokens.") @@ -73,7 +91,13 @@ class KeychainControllerTests: XCTestCase { func testRemovingSingleRestorationTokens() { // Given a keychain with 5 stored restoration tokens. for index in 0..<5 { - let restorationToken = RestorationToken(session: .init(accessToken: "accessToken", refreshToken: "refreshToken", userId: "userId", deviceId: "deviceId", homeserverUrl: "homeserverUrl", isSoftLogout: false)) + let restorationToken = RestorationToken(session: .init(accessToken: "accessToken", + refreshToken: "refreshToken", + userId: "userId", + deviceId: "deviceId", + homeserverUrl: "homeserverUrl", + isSoftLogout: false, + slidingSyncProxy: "https://my.sync.proxy")) keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com") } XCTAssertEqual(keychain.restorationTokens().count, 5, "The keychain should have 5 restoration tokens.") diff --git a/changelog.d/410.feature b/changelog.d/410.feature new file mode 100644 index 000000000..85ddb853e --- /dev/null +++ b/changelog.d/410.feature @@ -0,0 +1 @@ +Enable auto-discovery of sliding sync proxy, directing users to more information when their server doesn't support it. \ No newline at end of file diff --git a/project.yml b/project.yml index 07fcbc0ff..3d814969b 100644 --- a/project.yml +++ b/project.yml @@ -26,7 +26,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) BASE_BUNDLE_IDENTIFIER: io.element.elementx KEYCHAIN_ACCESS_GROUP_IDENTIFIER: $(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER) - MARKETING_VERSION: 1.0.19 + MARKETING_VERSION: 1.0.20 CURRENT_PROJECT_VERSION: 1 DEVELOPMENT_TEAM: 7J4U792NQT @@ -40,7 +40,7 @@ include: packages: MatrixRustSDK: url: https://github.com/matrix-org/matrix-rust-components-swift - exactVersion: 1.0.37-alpha + exactVersion: 1.0.38-alpha # path: ../matrix-rust-sdk DesignKit: path: DesignKit