diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index dd8b49a18..0a77903cc 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -95,6 +95,7 @@ 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 */; }; + 2835FD52F3F618D07F799B3D /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7310D8DFE01AF45F0689C3AA /* Publisher.swift */; }; 28410F3DE89C2C44E4F75C92 /* MockBugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E7BF8F7BB1021F889C6483 /* MockBugReportService.swift */; }; 290FDB0FFDC2F1DDF660343E /* TestMeasurementParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4048041C1A6B20CB97FD18 /* TestMeasurementParser.swift */; }; 297CD0A27C87B0C50FF192EE /* RoomTimelineViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE384418EB1FEDFA62C9CD0 /* RoomTimelineViewFactoryProtocol.swift */; }; @@ -801,6 +802,7 @@ 71D52BAA5BADB06E5E8C295D /* Assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = ""; }; 72D03D36422177EF01905D20 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = ""; }; 72F37B5DA798C9AE436F2C2C /* AttributedStringBuilderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilderProtocol.swift; sourceTree = ""; }; + 7310D8DFE01AF45F0689C3AA /* Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Publisher.swift; sourceTree = ""; }; 733FEDC1AE17806318A4BE56 /* FormSectionHeaderStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormSectionHeaderStyle.swift; sourceTree = ""; }; 73FC861755C6388F62B9280A /* Analytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Analytics.swift; sourceTree = ""; }; 748AE77AC3B0A01223033B87 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; @@ -1476,6 +1478,7 @@ E26747B3154A5DBC3A7E24A5 /* Image.swift */, 4E2245243369B99216C7D84E /* ImageCache.swift */, 2AFEF3AC64B1358083F76B8B /* List.swift */, + 7310D8DFE01AF45F0689C3AA /* Publisher.swift */, 40B21E611DADDEF00307E7AC /* String.swift */, A9FDA5344F7C4C6E4E863E13 /* Swipe.swift */, A40C19719687984FD9478FBE /* Task.swift */, @@ -3195,6 +3198,7 @@ 80D00A7C62AAB44F54725C43 /* PermalinkBuilder.swift in Sources */, 9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */, DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */, + 2835FD52F3F618D07F799B3D /* Publisher.swift in Sources */, 743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */, 8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */, C76892321558E75101E68ED6 /* ReadableFrameModifier.swift in Sources */, diff --git a/ElementX/Sources/Other/Extensions/Publisher.swift b/ElementX/Sources/Other/Extensions/Publisher.swift new file mode 100644 index 000000000..274f5921e --- /dev/null +++ b/ElementX/Sources/Other/Extensions/Publisher.swift @@ -0,0 +1,25 @@ +// +// Copyright 2023 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. +// + +import Combine + +extension Publisher where Self.Failure == Never { + func weakAssign(to keyPath: ReferenceWritableKeyPath, on object: Root) -> AnyCancellable { + sink { [weak object] value in + object?[keyPath: keyPath] = value + } + } +} diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index d14607a8f..42c2d8663 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -79,10 +79,9 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol return self.contextMenuActionsForItemId(itemId) } - ServiceLocator.shared.settings.$timelineStyle.sink { [weak self] timelineStyle in - self?.state.timelineStyle = timelineStyle - } - .store(in: &cancellables) + ServiceLocator.shared.settings.$timelineStyle + .weakAssign(to: \.state.timelineStyle, on: self) + .store(in: &cancellables) buildTimelineViews() } diff --git a/ElementX/Sources/Screens/Settings/SettingsScreenViewModel.swift b/ElementX/Sources/Screens/Settings/SettingsScreenViewModel.swift index 84dd26172..689243e70 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreenViewModel.swift @@ -33,7 +33,9 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo showSessionVerificationSection: !(userSession.sessionVerificationController?.isVerified ?? false)), imageProvider: userSession.mediaProvider) - listenToSettingsChange(publisher: ServiceLocator.shared.settings.$timelineStyle, keyPath: \.timelineStyle) + ServiceLocator.shared.settings.$timelineStyle + .weakAssign(to: \.state.bindings.timelineStyle, on: self) + .store(in: &cancellables) Task { if case let .success(userAvatarURL) = await userSession.clientProxy.loadUserAvatarURL() { @@ -78,16 +80,4 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo ServiceLocator.shared.settings.timelineStyle = state.bindings.timelineStyle } } - - private func listenToSettingsChange(publisher: AnyPublisher, - keyPath: WritableKeyPath) where Value: Equatable { - publisher.sink { [weak self] newValue in - guard newValue != self?.state.bindings[keyPath: keyPath] else { - return - } - - self?.state.bindings[keyPath: keyPath] = newValue - } - .store(in: &cancellables) - } }