From cd6810d239012465d997a2721548fd8ccf63e6f1 Mon Sep 17 00:00:00 2001 From: Gordon Brander Date: Mon, 18 Dec 2023 14:20:19 -0500 Subject: [PATCH 1/3] Initial work on early exit when block editor off --- .../UIKit/BlockEditor/BlockEditorModel.swift | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/xcode/Subconscious/Shared/UIKit/BlockEditor/BlockEditorModel.swift b/xcode/Subconscious/Shared/UIKit/BlockEditor/BlockEditorModel.swift index 2032ffba..0f3823c2 100644 --- a/xcode/Subconscious/Shared/UIKit/BlockEditor/BlockEditorModel.swift +++ b/xcode/Subconscious/Shared/UIKit/BlockEditor/BlockEditorModel.swift @@ -34,7 +34,8 @@ extension BlockEditor { ) ) } - + // Is block editor enabled? + var isEnabled = false /// Is editor in loading state? var loadingState = LoadingState.loading /// When was the last time the editor issued a fetch from source of truth? @@ -404,12 +405,20 @@ extension BlockEditor.Model: ModelProtocol { state: Self, environment: Environment ) -> Update { + var model = state + model.isEnabled = AppDefaults.standard.isBlockEditorEnabled + + guard model.isEnabled else { + logger.info("Block editor disabled. Skipping block editor startup.") + return Update(state: model) + } /// Poll and autosave until this store is destroyed. let pollFx: Fx = AppEnvironment .poll(every: Config.default.pollingInterval) .map({ _ in .autosave }) .eraseToAnyPublisher() - return Update(state: state, fx: pollFx) + + return Update(state: model, fx: pollFx) } static func ready( @@ -424,6 +433,11 @@ extension BlockEditor.Model: ModelProtocol { description: MemoEditorDetailDescription, environment: Environment ) -> Update { + guard state.isEnabled else { + logger.info("Editor containing view appeared, but block editor is disabled. Doing nothing.") + return Update(state: state) + } + return update( state: state, action: .loadEditor( From fd42a9cbb37aaea09342f44ac829e09a1c863438 Mon Sep 17 00:00:00 2001 From: Gordon Brander Date: Mon, 18 Dec 2023 15:56:42 -0500 Subject: [PATCH 2/3] Only poll when block editor is on --- .../Components/Detail/MemoEditorDetail.swift | 1 - .../UIKit/BlockEditor/BlockEditorModel.swift | 102 ++++++++++++------ .../Subconscious.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- 4 files changed, 73 insertions(+), 38 deletions(-) diff --git a/xcode/Subconscious/Shared/Components/Detail/MemoEditorDetail.swift b/xcode/Subconscious/Shared/Components/Detail/MemoEditorDetail.swift index fbae4b18..315eebf7 100644 --- a/xcode/Subconscious/Shared/Components/Detail/MemoEditorDetail.swift +++ b/xcode/Subconscious/Shared/Components/Detail/MemoEditorDetail.swift @@ -40,7 +40,6 @@ struct MemoEditorDetailView: View { @StateObject private var blockEditorStore = Store( state: BlockEditor.Model.draft(), - action: .start, environment: AppEnvironment.default, loggingEnabled: true, logger: blockEditorStoreLogger diff --git a/xcode/Subconscious/Shared/UIKit/BlockEditor/BlockEditorModel.swift b/xcode/Subconscious/Shared/UIKit/BlockEditor/BlockEditorModel.swift index 0f3823c2..e7847261 100644 --- a/xcode/Subconscious/Shared/UIKit/BlockEditor/BlockEditorModel.swift +++ b/xcode/Subconscious/Shared/UIKit/BlockEditor/BlockEditorModel.swift @@ -40,6 +40,8 @@ extension BlockEditor { var loadingState = LoadingState.loading /// When was the last time the editor issued a fetch from source of truth? var lastLoadStarted: Date? = nil + /// Is polling? We use this to drive autosaves. + var isPolling = false /// Is editor saved? private(set) var saveState = SaveState.saved @@ -69,14 +71,16 @@ extension BlockEditor { extension BlockEditor { // MARK: Actions enum Action { - /// Sent once when store is created - case start /// View is ready for updates. /// Sent during viewDidLoad after performing first view update for /// initial state and subscribing to changes. case ready /// Sent from SwiftUI land when the wrapping SwiftUI view appears. case appear(MemoEditorDetailDescription) + /// Start polling if needed + case startPolling + /// Perform a single polling step, optionally requesting another step + case poll /// Set document source location case loadEditor( address: Slashlink?, @@ -211,11 +215,6 @@ extension BlockEditor.Model: ModelProtocol { environment: Environment ) -> Update { switch action { - case .start: - return start( - state: state, - environment: environment - ) case .ready: return ready( state: state, @@ -227,6 +226,16 @@ extension BlockEditor.Model: ModelProtocol { description: description, environment: environment ) + case .startPolling: + return startPolling( + state: state, + environment: environment + ) + case .poll: + return poll( + state: state, + environment: environment + ) case let .loadEditor(address, fallback, autofocus): return loadEditor( state: state, @@ -401,53 +410,80 @@ extension BlockEditor.Model: ModelProtocol { } } - static func start( + static func ready( + state: Self, + environment: Environment + ) -> Update { + return Update(state: state) + } + + static func appear( state: Self, + description: MemoEditorDetailDescription, environment: Environment ) -> Update { var model = state model.isEnabled = AppDefaults.standard.isBlockEditorEnabled guard model.isEnabled else { - logger.info("Block editor disabled. Skipping block editor startup.") - return Update(state: model) + logger.info("Block editor is disabled. Skipping appear.") + return Update(state: state) } - /// Poll and autosave until this store is destroyed. - let pollFx: Fx = AppEnvironment - .poll(every: Config.default.pollingInterval) - .map({ _ in .autosave }) - .eraseToAnyPublisher() - - return Update(state: model, fx: pollFx) + + return update( + state: model, + actions: [ + .loadEditor( + address: description.address, + fallback: description.fallback + ), + .startPolling + ], + environment: environment + ) } - - static func ready( + + static func startPolling( state: Self, environment: Environment ) -> Update { - return Update(state: state) + guard state.isEnabled else { + logger.info("Skipping polling start. Block editor is disabled.") + return Update(state: state) + } + guard !state.isPolling else { + logger.info("Skipping polling start. Already polling.") + return Update(state: state) + } + var model = state + model.isPolling = true + + let pollFx: Fx = Just(Action.poll).delay( + for: .seconds(Config.default.pollingInterval), + scheduler: DispatchQueue.main + ).eraseToAnyPublisher() + + return Update(state: state, fx: pollFx) } - static func appear( + static func poll( state: Self, - description: MemoEditorDetailDescription, environment: Environment ) -> Update { - guard state.isEnabled else { - logger.info("Editor containing view appeared, but block editor is disabled. Doing nothing.") - return Update(state: state) - } + let pollFx: Fx = Just(Action.poll).delay( + for: .seconds(Config.default.pollingInterval), + scheduler: DispatchQueue.main + ).eraseToAnyPublisher() - return update( + let autosaveFx: Fx = Just(Action.autosave) + .eraseToAnyPublisher() + + return Update( state: state, - action: .loadEditor( - address: description.address, - fallback: description.fallback - ), - environment: environment + fx: pollFx.merge(with: autosaveFx).eraseToAnyPublisher() ) } - + static func loadEditor( state: Self, address: Slashlink?, diff --git a/xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj b/xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj index 2e0d12db..916e13c8 100644 --- a/xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj +++ b/xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj @@ -2981,8 +2981,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/gordonbrander/ObservableStore"; requirement = { - kind = exactVersion; - version = 0.6.0; + branch = "2023-12-18-manual-fx-management"; + kind = branch; }; }; B82C3A6D26F6B1C000833CC8 /* XCRemoteSwiftPackageReference "swift-collections" */ = { diff --git a/xcode/Subconscious/Subconscious.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/xcode/Subconscious/Subconscious.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 689f45e5..7281ee82 100644 --- a/xcode/Subconscious/Subconscious.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/xcode/Subconscious/Subconscious.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/gordonbrander/ObservableStore", "state" : { - "revision" : "371a014b535fe9941f71041f1bded9e4e7a49fba", - "version" : "0.6.0" + "branch" : "2023-12-18-manual-fx-management", + "revision" : "380b614fdaf6d9f86d352d550e13a7c8492253eb" } }, { From 7e8acee1f57f7be89e15ec725b3c61f29d915504 Mon Sep 17 00:00:00 2001 From: Gordon Brander Date: Tue, 19 Dec 2023 15:42:57 -0500 Subject: [PATCH 3/3] Update ObservableStore to v0.6.1 --- xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj | 4 ++-- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj b/xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj index 05e49c31..f26eed88 100644 --- a/xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj +++ b/xcode/Subconscious/Subconscious.xcodeproj/project.pbxproj @@ -2987,8 +2987,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/gordonbrander/ObservableStore"; requirement = { - branch = "2023-12-18-manual-fx-management"; - kind = branch; + kind = exactVersion; + version = 0.6.1; }; }; B82C3A6D26F6B1C000833CC8 /* XCRemoteSwiftPackageReference "swift-collections" */ = { diff --git a/xcode/Subconscious/Subconscious.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/xcode/Subconscious/Subconscious.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7281ee82..27a2e373 100644 --- a/xcode/Subconscious/Subconscious.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/xcode/Subconscious/Subconscious.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/gordonbrander/ObservableStore", "state" : { - "branch" : "2023-12-18-manual-fx-management", - "revision" : "380b614fdaf6d9f86d352d550e13a7c8492253eb" + "revision" : "41f3356de1b7cd289d2e95fdbff0a15b960e0a6b", + "version" : "0.6.1" } }, {