mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Expose sliding sync proxy URL on the server selection screen; make app more resilient to slidinc sync configuration errors, remove fatal errors
This commit is contained in:
committed by
Stefan Ceriu
parent
2fd0491a18
commit
276daf0355
@@ -20,8 +20,9 @@ final class BuildSettings {
|
|||||||
// MARK: - Servers
|
// MARK: - Servers
|
||||||
|
|
||||||
static let defaultHomeserverAddress = "matrix.org"
|
static let defaultHomeserverAddress = "matrix.org"
|
||||||
static let slidingSyncProxyBaseURL = URL(staticString: "https://slidingsync.lab.element.dev")
|
|
||||||
|
static let defaultSlidingSyncProxyBaseURLString = "https://slidingsync.lab.element.dev"
|
||||||
|
|
||||||
// MARK: - Bug report
|
// MARK: - Bug report
|
||||||
|
|
||||||
static let bugReportServiceBaseURL = URL(staticString: "https://riot.im/bugreports")
|
static let bugReportServiceBaseURL = URL(staticString: "https://riot.im/bugreports")
|
||||||
|
|||||||
@@ -25,13 +25,17 @@ final class ElementSettings: ObservableObject {
|
|||||||
case timelineStyle
|
case timelineStyle
|
||||||
case enableAnalytics
|
case enableAnalytics
|
||||||
case isIdentifiedForAnalytics
|
case isIdentifiedForAnalytics
|
||||||
|
case slidingSyncProxyBaseURLString
|
||||||
}
|
}
|
||||||
|
|
||||||
static let shared = ElementSettings()
|
static let shared = ElementSettings()
|
||||||
|
|
||||||
/// UserDefaults to be used on reads and writes.
|
/// UserDefaults to be used on reads and writes.
|
||||||
static var store: UserDefaults {
|
static var store: UserDefaults {
|
||||||
.standard
|
guard let userDefaults = UserDefaults(suiteName: ElementInfoPlist.appGroupIdentifier) else {
|
||||||
|
fatalError("Fail to load shared UserDefaults")
|
||||||
|
}
|
||||||
|
return userDefaults
|
||||||
}
|
}
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
@@ -59,4 +63,9 @@ final class ElementSettings: ObservableObject {
|
|||||||
|
|
||||||
@AppStorage(UserDefaultsKeys.timelineStyle.rawValue, store: store)
|
@AppStorage(UserDefaultsKeys.timelineStyle.rawValue, store: store)
|
||||||
var timelineStyle = BuildSettings.defaultRoomTimelineStyle
|
var timelineStyle = BuildSettings.defaultRoomTimelineStyle
|
||||||
|
|
||||||
|
// MARK: - Client
|
||||||
|
|
||||||
|
@AppStorage(UserDefaultsKeys.slidingSyncProxyBaseURLString.rawValue, store: store)
|
||||||
|
var slidingSyncProxyBaseURLString = BuildSettings.defaultSlidingSyncProxyBaseURLString
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ struct ServerSelectionViewState: BindableState {
|
|||||||
struct ServerSelectionBindings {
|
struct ServerSelectionBindings {
|
||||||
/// The homeserver address input by the user.
|
/// The homeserver address input by the user.
|
||||||
var homeserverAddress: String
|
var homeserverAddress: String
|
||||||
|
/// The sliding sync proxy address input by the user.
|
||||||
|
var slidingSyncProxyAddress: String
|
||||||
/// Information describing the currently displayed alert.
|
/// Information describing the currently displayed alert.
|
||||||
var alertInfo: AlertInfo<ServerSelectionErrorType>?
|
var alertInfo: AlertInfo<ServerSelectionErrorType>?
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -30,7 +30,9 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
|
|||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
||||||
init(homeserverAddress: String, isModallyPresented: Bool) {
|
init(homeserverAddress: String, isModallyPresented: Bool) {
|
||||||
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress)
|
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress,
|
||||||
|
slidingSyncProxyAddress: ElementSettings.shared.slidingSyncProxyBaseURLString)
|
||||||
|
|
||||||
super.init(initialViewState: ServerSelectionViewState(bindings: bindings,
|
super.init(initialViewState: ServerSelectionViewState(bindings: bindings,
|
||||||
isModallyPresented: isModallyPresented))
|
isModallyPresented: isModallyPresented))
|
||||||
}
|
}
|
||||||
@@ -40,6 +42,10 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
|
|||||||
override func process(viewAction: ServerSelectionViewAction) async {
|
override func process(viewAction: ServerSelectionViewAction) async {
|
||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .confirm:
|
case .confirm:
|
||||||
|
if !state.bindings.slidingSyncProxyAddress.isEmpty {
|
||||||
|
ElementSettings.shared.slidingSyncProxyBaseURLString = state.bindings.slidingSyncProxyAddress
|
||||||
|
}
|
||||||
|
|
||||||
callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
|
callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
callback?(.dismiss)
|
callback?(.dismiss)
|
||||||
|
|||||||
+13
-12
@@ -17,18 +17,8 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ServerSelectionScreen: View {
|
struct ServerSelectionScreen: View {
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
@FocusState var isTextFieldFocused: Bool
|
|
||||||
|
|
||||||
// MARK: Public
|
|
||||||
|
|
||||||
@ObservedObject var context: ServerSelectionViewModel.Context
|
@ObservedObject var context: ServerSelectionViewModel.Context
|
||||||
|
|
||||||
// MARK: Views
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@@ -69,8 +59,8 @@ struct ServerSelectionScreen: View {
|
|||||||
var serverForm: some View {
|
var serverForm: some View {
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
TextField(ElementL10n.ftueAuthChooseServerEntryHint, text: $context.homeserverAddress)
|
TextField(ElementL10n.ftueAuthChooseServerEntryHint, text: $context.homeserverAddress)
|
||||||
.focused($isTextFieldFocused)
|
.textFieldStyle(.elementInput(labelText: ElementL10n.hsUrl,
|
||||||
.textFieldStyle(.elementInput(footerText: context.viewState.footerMessage,
|
footerText: context.viewState.footerMessage,
|
||||||
isError: context.viewState.isShowingFooterError))
|
isError: context.viewState.isShowingFooterError))
|
||||||
.keyboardType(.URL)
|
.keyboardType(.URL)
|
||||||
.autocapitalization(.none)
|
.autocapitalization(.none)
|
||||||
@@ -80,6 +70,17 @@ struct ServerSelectionScreen: View {
|
|||||||
.onSubmit(submit)
|
.onSubmit(submit)
|
||||||
.accessibilityIdentifier("addressTextField")
|
.accessibilityIdentifier("addressTextField")
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
TextField(ElementL10n.ftueAuthChooseServerEntryHint, text: $context.slidingSyncProxyAddress)
|
||||||
|
.textFieldStyle(.elementInput(labelText: "Sliding sync proxy URL"))
|
||||||
|
.keyboardType(.URL)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.submitLabel(.done)
|
||||||
|
.onSubmit(submit)
|
||||||
|
.accessibilityIdentifier("slidingSyncProxyAddressTextField")
|
||||||
|
|
||||||
Button(action: submit) {
|
Button(action: submit) {
|
||||||
Text(context.viewState.buttonTitle)
|
Text(context.viewState.buttonTitle)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ typealias HomeScreenViewModelType = StateStoreViewModel<HomeScreenViewState, Hom
|
|||||||
|
|
||||||
class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol {
|
class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol {
|
||||||
private let userSession: UserSessionProtocol
|
private let userSession: UserSessionProtocol
|
||||||
private let roomSummaryProvider: RoomSummaryProviderProtocol
|
private let roomSummaryProvider: RoomSummaryProviderProtocol?
|
||||||
private let attributedStringBuilder: AttributedStringBuilderProtocol
|
private let attributedStringBuilder: AttributedStringBuilderProtocol
|
||||||
private var roomsForIdentifiers = [String: HomeScreenRoom]()
|
private var roomsForIdentifiers = [String: HomeScreenRoom]()
|
||||||
|
|
||||||
@@ -51,6 +51,25 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
Task {
|
||||||
|
if case let .success(userAvatarURLString) = await userSession.clientProxy.loadUserAvatarURLString() {
|
||||||
|
if case let .success(avatar) = await userSession.mediaProvider.loadImageFromURLString(userAvatarURLString, avatarSize: .user(on: .home)) {
|
||||||
|
state.userAvatar = avatar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Task {
|
||||||
|
if case let .success(userDisplayName) = await userSession.clientProxy.loadUserDisplayName() {
|
||||||
|
state.userDisplayName = userDisplayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let roomSummaryProvider else {
|
||||||
|
MXLog.error("Room summary provider unavailable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Publishers.CombineLatest3(roomSummaryProvider.statePublisher,
|
Publishers.CombineLatest3(roomSummaryProvider.statePublisher,
|
||||||
roomSummaryProvider.countPublisher,
|
roomSummaryProvider.countPublisher,
|
||||||
roomSummaryProvider.roomListPublisher)
|
roomSummaryProvider.roomListPublisher)
|
||||||
@@ -77,20 +96,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
Task {
|
|
||||||
if case let .success(userAvatarURLString) = await userSession.clientProxy.loadUserAvatarURLString() {
|
|
||||||
if case let .success(avatar) = await userSession.mediaProvider.loadImageFromURLString(userAvatarURLString, avatarSize: .user(on: .home)) {
|
|
||||||
state.userAvatar = avatar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Task {
|
|
||||||
if case let .success(userDisplayName) = await userSession.clientProxy.loadUserDisplayName() {
|
|
||||||
state.userDisplayName = userDisplayName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateRooms()
|
updateRooms()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +104,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
override func process(viewAction: HomeScreenViewAction) async {
|
override func process(viewAction: HomeScreenViewAction) async {
|
||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .loadRoomData(let roomIdentifier):
|
case .loadRoomData(let roomIdentifier):
|
||||||
loadDataForRoomIdentifier(roomIdentifier)
|
if state.roomListMode != .skeletons {
|
||||||
|
loadDataForRoomIdentifier(roomIdentifier)
|
||||||
|
}
|
||||||
case .selectRoom(let roomIdentifier):
|
case .selectRoom(let roomIdentifier):
|
||||||
callback?(.selectRoom(roomIdentifier: roomIdentifier))
|
callback?(.selectRoom(roomIdentifier: roomIdentifier))
|
||||||
case .userMenu(let action):
|
case .userMenu(let action):
|
||||||
@@ -118,6 +125,11 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func loadDataForRoomIdentifier(_ identifier: String) {
|
private func loadDataForRoomIdentifier(_ identifier: String) {
|
||||||
|
guard let roomSummaryProvider else {
|
||||||
|
MXLog.error("Room summary provider unavailable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
guard let roomSummary = roomSummaryProvider.roomListPublisher.value.first(where: { $0.asFilled?.id == identifier })?.asFilled,
|
guard let roomSummary = roomSummaryProvider.roomListPublisher.value.first(where: { $0.asFilled?.id == identifier })?.asFilled,
|
||||||
let roomIndex = state.rooms.firstIndex(where: { $0.id == identifier }) else {
|
let roomIndex = state.rooms.firstIndex(where: { $0.id == identifier }) else {
|
||||||
return
|
return
|
||||||
@@ -140,6 +152,11 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateRooms() {
|
private func updateRooms() {
|
||||||
|
guard let roomSummaryProvider else {
|
||||||
|
MXLog.error("Room summary provider unavailable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var rooms = [HomeScreenRoom]()
|
var rooms = [HomeScreenRoom]()
|
||||||
var newRoomsForIdentifiers = [String: HomeScreenRoom]()
|
var newRoomsForIdentifiers = [String: HomeScreenRoom]()
|
||||||
|
|
||||||
|
|||||||
@@ -55,22 +55,16 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
private let clientQueue: DispatchQueue
|
private let clientQueue: DispatchQueue
|
||||||
|
|
||||||
private var slidingSyncObserverToken: StoppableSpawn?
|
private var slidingSyncObserverToken: StoppableSpawn?
|
||||||
private var slidingSync: SlidingSync!
|
private var slidingSync: SlidingSync?
|
||||||
|
|
||||||
var roomSummaryProviderInternal: RoomSummaryProviderProtocol!
|
var roomSummaryProvider: RoomSummaryProviderProtocol?
|
||||||
var roomSummaryProvider: RoomSummaryProviderProtocol {
|
|
||||||
guard let roomSummaryProviderInternal else {
|
|
||||||
fatalError("There is an issue with ClientProxy object initialization")
|
|
||||||
}
|
|
||||||
return roomSummaryProviderInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
// These need to be inlined instead of using stopSync()
|
// These need to be inlined instead of using stopSync()
|
||||||
// as we can't call async methods safely from deinit
|
// as we can't call async methods safely from deinit
|
||||||
client.setDelegate(delegate: nil)
|
client.setDelegate(delegate: nil)
|
||||||
slidingSyncObserverToken?.cancel()
|
slidingSyncObserverToken?.cancel()
|
||||||
slidingSync.setObserver(observer: nil)
|
slidingSync?.setObserver(observer: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
let callbacks = PassthroughSubject<ClientProxyCallback, Never>()
|
let callbacks = PassthroughSubject<ClientProxyCallback, Never>()
|
||||||
@@ -84,7 +78,7 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
|
|
||||||
await Task.dispatch(on: clientQueue) {
|
await Task.dispatch(on: clientQueue) {
|
||||||
do {
|
do {
|
||||||
let slidingSyncBuilder = try client.slidingSync().homeserver(url: BuildSettings.slidingSyncProxyBaseURL.absoluteString)
|
let slidingSyncBuilder = try client.slidingSync().homeserver(url: ElementSettings.shared.slidingSyncProxyBaseURLString)
|
||||||
|
|
||||||
let slidingSyncView = try SlidingSyncViewBuilder()
|
let slidingSyncView = try SlidingSyncViewBuilder()
|
||||||
.timelineLimit(limit: 10)
|
.timelineLimit(limit: 10)
|
||||||
@@ -94,16 +88,18 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
.syncMode(mode: .fullSync)
|
.syncMode(mode: .fullSync)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
self.slidingSync = try slidingSyncBuilder
|
let slidingSync = try slidingSyncBuilder
|
||||||
.addView(v: slidingSyncView)
|
.addView(v: slidingSyncView)
|
||||||
.withCommonExtensions()
|
.withCommonExtensions()
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
self.roomSummaryProviderInternal = RoomSummaryProvider(slidingSyncController: self.slidingSync,
|
self.roomSummaryProvider = RoomSummaryProvider(slidingSyncController: slidingSync,
|
||||||
slidingSyncView: slidingSyncView,
|
slidingSyncView: slidingSyncView,
|
||||||
roomMessageFactory: RoomMessageFactory())
|
roomMessageFactory: RoomMessageFactory())
|
||||||
|
|
||||||
|
self.slidingSync = slidingSync
|
||||||
} catch {
|
} catch {
|
||||||
fatalError("Failed configuring sliding sync")
|
MXLog.error("Failed configuring sliding sync with error: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,15 +146,15 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
slidingSync.setObserver(observer: WeakClientProxyWrapper(clientProxy: self))
|
slidingSync?.setObserver(observer: WeakClientProxyWrapper(clientProxy: self))
|
||||||
slidingSyncObserverToken = slidingSync.sync()
|
slidingSyncObserverToken = slidingSync?.sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopSync() {
|
func stopSync() {
|
||||||
client.setDelegate(delegate: nil)
|
client.setDelegate(delegate: nil)
|
||||||
|
|
||||||
slidingSyncObserverToken?.cancel()
|
slidingSyncObserverToken?.cancel()
|
||||||
slidingSync.setObserver(observer: nil)
|
slidingSync?.setObserver(observer: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func roomForIdentifier(_ identifier: String) async -> RoomProxyProtocol? {
|
func roomForIdentifier(_ identifier: String) async -> RoomProxyProtocol? {
|
||||||
@@ -252,7 +248,7 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
|
|
||||||
private func roomTupleForIdentifier(_ identifier: String) -> (SlidingSyncRoom?, Room?) {
|
private func roomTupleForIdentifier(_ identifier: String) -> (SlidingSyncRoom?, Room?) {
|
||||||
do {
|
do {
|
||||||
let slidingSyncRoom = try slidingSync.getRoom(roomId: identifier)
|
let slidingSyncRoom = try slidingSync?.getRoom(roomId: identifier)
|
||||||
let fullRoom = slidingSyncRoom?.fullRoom()
|
let fullRoom = slidingSyncRoom?.fullRoom()
|
||||||
|
|
||||||
return (slidingSyncRoom, fullRoom)
|
return (slidingSyncRoom, fullRoom)
|
||||||
@@ -271,7 +267,7 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func didReceiveSlidingSyncUpdate(summary: UpdateSummary) {
|
fileprivate func didReceiveSlidingSyncUpdate(summary: UpdateSummary) {
|
||||||
roomSummaryProvider.updateRoomsWithIdentifiers(summary.rooms)
|
roomSummaryProvider?.updateRoomsWithIdentifiers(summary.rooms)
|
||||||
|
|
||||||
callbacks.send(.receivedSyncUpdate)
|
callbacks.send(.receivedSyncUpdate)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ protocol ClientProxyProtocol {
|
|||||||
|
|
||||||
var restorationToken: RestorationToken? { get }
|
var restorationToken: RestorationToken? { get }
|
||||||
|
|
||||||
var roomSummaryProvider: RoomSummaryProviderProtocol { get }
|
var roomSummaryProvider: RoomSummaryProviderProtocol? { get }
|
||||||
|
|
||||||
func startSync()
|
func startSync()
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ struct MockClientProxy: ClientProxyProtocol {
|
|||||||
let homeserver = ""
|
let homeserver = ""
|
||||||
let restorationToken: RestorationToken? = nil
|
let restorationToken: RestorationToken? = nil
|
||||||
|
|
||||||
var roomSummaryProvider: RoomSummaryProviderProtocol = MockRoomSummaryProvider()
|
var roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()
|
||||||
|
|
||||||
func startSync() { }
|
func startSync() { }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Expose sliding sync proxy configuration URL on the server selection screen
|
||||||
Reference in New Issue
Block a user