Skip to content

Commit

Permalink
Expose sliding sync proxy URL on the server selection screen; make ap…
Browse files Browse the repository at this point in the history
…p more resilient to slidinc sync configuration errors, remove fatal errors
  • Loading branch information
stefanceriu committed Nov 17, 2022
1 parent 2fd0491 commit 8c0e9f5
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 54 deletions.
4 changes: 4 additions & 0 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@
CC736DA1AA8F8B9FD8785009 /* ScreenshotDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5C4AF6E3885730CD560311C /* ScreenshotDetector.swift */; };
CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */; };
CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */ = {isa = PBXBuildFile; fileRef = D2F7194F440375338F8E2487 /* Untranslated.strings */; };
CE2D4E320AF0B0F5FA753518 /* UserDefaultsBackedPropertyWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDEFD3835499224318EDE0BC /* UserDefaultsBackedPropertyWrapper.swift */; };
CE7148E80F09B7305E026AC6 /* OnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1198B925F4A88DA74083662 /* OnboardingViewModel.swift */; };
CEB8FB1269DE20536608B957 /* LoginMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B41FABA2B0AEF4389986495 /* LoginMode.swift */; };
CF82143AA4A4F7BD11D22946 /* RoomTimelineViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB6C5E4950B6C9842F35A38 /* RoomTimelineViewProvider.swift */; };
Expand Down Expand Up @@ -752,6 +753,7 @@
BB33A751BFDA223BDD106EC0 /* OnboardingModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingModels.swift; sourceTree = "<group>"; };
BC9B05D6B293A039EB963CA7 /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/Localizable.strings; sourceTree = "<group>"; };
BCBE603A7EB2C93E81BA6415 /* MediaPlayerModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerModels.swift; sourceTree = "<group>"; };
BDEFD3835499224318EDE0BC /* UserDefaultsBackedPropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsBackedPropertyWrapper.swift; sourceTree = "<group>"; };
BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposerTextField.swift; sourceTree = "<group>"; };
BEBA759D1347CFFB3D84ED1F /* UserSessionStoreProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreProtocol.swift; sourceTree = "<group>"; };
BEE6BF9BA63FF42F8AF6EEEA /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sr; path = sr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1794,6 +1796,7 @@
C2DE30233B57761F8AFEB415 /* ReversedScrollView.swift */,
BB3073CCD77D906B330BC1D6 /* Tests.swift */,
1F2529D434C750ED78ADF1ED /* UserAgentBuilder.swift */,
BDEFD3835499224318EDE0BC /* UserDefaultsBackedPropertyWrapper.swift */,
44BBB96FAA2F0D53C507396B /* Extensions */,
8F9A844EB44B6AD7CA18FD96 /* HTMLParsing */,
06501F0E978B2D5C92771DC7 /* Logging */,
Expand Down Expand Up @@ -2663,6 +2666,7 @@
086C2FA7750378EB2BFD0BEE /* UITestsRootCoordinator.swift in Sources */,
071A017E415AD378F2961B11 /* URL.swift in Sources */,
7A71AEF419904209BB8C2833 /* UserAgentBuilder.swift in Sources */,
CE2D4E320AF0B0F5FA753518 /* UserDefaultsBackedPropertyWrapper.swift in Sources */,
87BD4F95F9D603C309837378 /* UserNotification.swift in Sources */,
5D9F0695DC6C0057F85C12B6 /* UserNotificationController.swift in Sources */,
D79F0F852C6A4255D5E616D2 /* UserNotificationControllerProtocol.swift in Sources */,
Expand Down
13 changes: 11 additions & 2 deletions ElementX/Sources/Application/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,20 @@
import Foundation

final class BuildSettings {
static var defaults: UserDefaults = {
guard let userDefaults = UserDefaults(suiteName: ElementInfoPlist.appGroupIdentifier) else {
fatalError("[RiotSettings] Fail to load shared UserDefaults")
}
return userDefaults
}()

// MARK: - Servers

static let defaultHomeserverAddress = "matrix.org"
static let slidingSyncProxyBaseURL = URL(staticString: "https://slidingsync.lab.element.dev")


@UserDefault(key: "slidingSyncProxyBaseURLString", defaultValue: "https://slidingsync.lab.element.dev")
static var slidingSyncProxyBaseURLString

// MARK: - Bug report

static let bugReportServiceBaseURL = URL(staticString: "https://riot.im/bugreports")
Expand Down
69 changes: 69 additions & 0 deletions ElementX/Sources/Other/UserDefaultsBackedPropertyWrapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Taken from https://www.swiftbysundell.com/articles/property-wrappers-in-swift/

import Foundation

@propertyWrapper
struct UserDefault<Value> {
private let key: String
private let defaultValue: Value
private let storage: UserDefaults

init(key: String, defaultValue: Value, storage: UserDefaults = .standard) {
self.defaultValue = defaultValue
self.key = key
self.storage = storage
}

var wrappedValue: Value {
get {
let value = storage.value(forKey: key) as? Value
return value ?? defaultValue
}
set {
if let optional = newValue as? AnyOptional, optional.isNil {
storage.removeObject(forKey: key)
} else {
storage.setValue(newValue, forKey: key)
}
let tmpKey = key
DispatchQueue.main.async {
NotificationCenter.default.post(name: .userDefaultValueUpdated,
object: tmpKey)
}
}
}
}

extension UserDefault where Value: ExpressibleByNilLiteral {
init(key: String, storage: UserDefaults = .standard) {
self.init(key: key, defaultValue: nil, storage: storage)
}
}

private protocol AnyOptional {
var isNil: Bool { get }
}

extension Optional: AnyOptional {
var isNil: Bool { self == nil }
}

extension Notification.Name {
static let userDefaultValueUpdated = Notification.Name("userDefaultValueUpdated")
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ struct ServerSelectionViewState: BindableState {
struct ServerSelectionBindings {
/// The homeserver address input by the user.
var homeserverAddress: String
/// The sliding sync proxy address input by the user.
var slidingSyncProxyAddress: String
/// Information describing the currently displayed alert.
var alertInfo: AlertInfo<ServerSelectionErrorType>?
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
// MARK: - Setup

init(homeserverAddress: String, isModallyPresented: Bool) {
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress)
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress,
slidingSyncProxyAddress: BuildSettings.slidingSyncProxyBaseURLString)

super.init(initialViewState: ServerSelectionViewState(bindings: bindings,
isModallyPresented: isModallyPresented))
}
Expand All @@ -40,6 +42,10 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
override func process(viewAction: ServerSelectionViewAction) async {
switch viewAction {
case .confirm:
if !state.bindings.slidingSyncProxyAddress.isEmpty {
BuildSettings.slidingSyncProxyBaseURLString = state.bindings.slidingSyncProxyAddress
}

callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
case .dismiss:
callback?(.dismiss)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,8 @@
import SwiftUI

struct ServerSelectionScreen: View {
// MARK: - Properties

// MARK: Private

@FocusState var isTextFieldFocused: Bool

// MARK: Public

@ObservedObject var context: ServerSelectionViewModel.Context

// MARK: Views

var body: some View {
ScrollView {
VStack(spacing: 0) {
Expand Down Expand Up @@ -69,8 +59,8 @@ struct ServerSelectionScreen: View {
var serverForm: some View {
VStack(alignment: .leading, spacing: 12) {
TextField(ElementL10n.ftueAuthChooseServerEntryHint, text: $context.homeserverAddress)
.focused($isTextFieldFocused)
.textFieldStyle(.elementInput(footerText: context.viewState.footerMessage,
.textFieldStyle(.elementInput(labelText: ElementL10n.hsUrl,
footerText: context.viewState.footerMessage,
isError: context.viewState.isShowingFooterError))
.keyboardType(.URL)
.autocapitalization(.none)
Expand All @@ -80,6 +70,17 @@ struct ServerSelectionScreen: View {
.onSubmit(submit)
.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) {
Text(context.viewState.buttonTitle)
}
Expand Down
49 changes: 33 additions & 16 deletions ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ typealias HomeScreenViewModelType = StateStoreViewModel<HomeScreenViewState, Hom

class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol {
private let userSession: UserSessionProtocol
private let roomSummaryProvider: RoomSummaryProviderProtocol
private let roomSummaryProvider: RoomSummaryProviderProtocol?
private let attributedStringBuilder: AttributedStringBuilderProtocol
private var roomsForIdentifiers = [String: HomeScreenRoom]()

Expand Down Expand Up @@ -51,6 +51,25 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
}
.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,
roomSummaryProvider.countPublisher,
roomSummaryProvider.roomListPublisher)
Expand All @@ -77,20 +96,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
}
.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()
}

Expand All @@ -99,7 +104,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
override func process(viewAction: HomeScreenViewAction) async {
switch viewAction {
case .loadRoomData(let roomIdentifier):
loadDataForRoomIdentifier(roomIdentifier)
if state.roomListMode != .skeletons {
loadDataForRoomIdentifier(roomIdentifier)
}
case .selectRoom(let roomIdentifier):
callback?(.selectRoom(roomIdentifier: roomIdentifier))
case .userMenu(let action):
Expand All @@ -118,6 +125,11 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
// MARK: - Private

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,
let roomIndex = state.rooms.firstIndex(where: { $0.id == identifier }) else {
return
Expand All @@ -140,6 +152,11 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
}

private func updateRooms() {
guard let roomSummaryProvider else {
MXLog.error("Room summary provider unavailable")
return
}

var rooms = [HomeScreenRoom]()
var newRoomsForIdentifiers = [String: HomeScreenRoom]()

Expand Down
38 changes: 17 additions & 21 deletions ElementX/Sources/Services/Client/ClientProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,16 @@ class ClientProxy: ClientProxyProtocol {
private let clientQueue: DispatchQueue

private var slidingSyncObserverToken: StoppableSpawn?
private var slidingSync: SlidingSync!
private var slidingSync: SlidingSync?

var roomSummaryProviderInternal: RoomSummaryProviderProtocol!
var roomSummaryProvider: RoomSummaryProviderProtocol {
guard let roomSummaryProviderInternal else {
fatalError("There is an issue with ClientProxy object initialization")
}
return roomSummaryProviderInternal
}
var roomSummaryProvider: RoomSummaryProviderProtocol?

deinit {
// These need to be inlined instead of using stopSync()
// as we can't call async methods safely from deinit
client.setDelegate(delegate: nil)
slidingSyncObserverToken?.cancel()
slidingSync.setObserver(observer: nil)
slidingSync?.setObserver(observer: nil)
}

let callbacks = PassthroughSubject<ClientProxyCallback, Never>()
Expand All @@ -84,7 +78,7 @@ class ClientProxy: ClientProxyProtocol {

await Task.dispatch(on: clientQueue) {
do {
let slidingSyncBuilder = try client.slidingSync().homeserver(url: BuildSettings.slidingSyncProxyBaseURL.absoluteString)
let slidingSyncBuilder = try client.slidingSync().homeserver(url: BuildSettings.slidingSyncProxyBaseURLString)

let slidingSyncView = try SlidingSyncViewBuilder()
.timelineLimit(limit: 10)
Expand All @@ -94,16 +88,18 @@ class ClientProxy: ClientProxyProtocol {
.syncMode(mode: .fullSync)
.build()

self.slidingSync = try slidingSyncBuilder
let slidingSync = try slidingSyncBuilder
.addView(v: slidingSyncView)
.withCommonExtensions()
.build()

self.roomSummaryProviderInternal = RoomSummaryProvider(slidingSyncController: self.slidingSync,
slidingSyncView: slidingSyncView,
roomMessageFactory: RoomMessageFactory())

self.roomSummaryProvider = RoomSummaryProvider(slidingSyncController: slidingSync,
slidingSyncView: slidingSyncView,
roomMessageFactory: RoomMessageFactory())

self.slidingSync = slidingSync
} catch {
fatalError("Failed configuring sliding sync")
MXLog.error("Failed configuring sliding sync with error: \(error)")
}
}

Expand Down Expand Up @@ -150,15 +146,15 @@ class ClientProxy: ClientProxyProtocol {
return
}

slidingSync.setObserver(observer: WeakClientProxyWrapper(clientProxy: self))
slidingSyncObserverToken = slidingSync.sync()
slidingSync?.setObserver(observer: WeakClientProxyWrapper(clientProxy: self))
slidingSyncObserverToken = slidingSync?.sync()
}

func stopSync() {
client.setDelegate(delegate: nil)

slidingSyncObserverToken?.cancel()
slidingSync.setObserver(observer: nil)
slidingSync?.setObserver(observer: nil)
}

func roomForIdentifier(_ identifier: String) async -> RoomProxyProtocol? {
Expand Down Expand Up @@ -252,7 +248,7 @@ class ClientProxy: ClientProxyProtocol {

private func roomTupleForIdentifier(_ identifier: String) -> (SlidingSyncRoom?, Room?) {
do {
let slidingSyncRoom = try slidingSync.getRoom(roomId: identifier)
let slidingSyncRoom = try slidingSync?.getRoom(roomId: identifier)
let fullRoom = slidingSyncRoom?.fullRoom()

return (slidingSyncRoom, fullRoom)
Expand All @@ -271,7 +267,7 @@ class ClientProxy: ClientProxyProtocol {
}

fileprivate func didReceiveSlidingSyncUpdate(summary: UpdateSummary) {
roomSummaryProvider.updateRoomsWithIdentifiers(summary.rooms)
roomSummaryProvider?.updateRoomsWithIdentifiers(summary.rooms)

callbacks.send(.receivedSyncUpdate)
}
Expand Down
Loading

0 comments on commit 8c0e9f5

Please sign in to comment.