-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix how editor handles dependencies loading #24956
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
faced0e
38762ae
91f54a4
7ad2ab0
b0c6b7b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,46 +10,6 @@ import WebKit | |
| import CocoaLumberjackSwift | ||
|
|
||
| class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor { | ||
|
|
||
| enum EditorLoadingState { | ||
| /// We haven't done anything with the editor yet | ||
| /// | ||
| /// Valid states to transition to: | ||
| /// - .loadingDependencies | ||
| case uninitialized | ||
|
|
||
| /// We're loading the editor's dependencies | ||
| /// | ||
| /// Valid states to transition to: | ||
| /// - .loadingCancelled | ||
| /// - .dependencyError | ||
| /// - .dependenciesReady | ||
| case loadingDependencies(_ task: Task<Void, Error>) | ||
|
|
||
| /// We cancelled loading the editor's dependencies | ||
| /// | ||
| /// Valid states to transition to: | ||
| /// - .loadingDependencies | ||
| case loadingCancelled | ||
|
|
||
| /// An error occured while fetching dependencies | ||
| /// | ||
| /// Valid states to transition to: | ||
| /// - .loadingDependencies | ||
| case dependencyError(Error) | ||
|
|
||
| /// All dependencies have been loaded, so we're ready to start the editor | ||
| /// | ||
| /// Valid states to transition to: | ||
| /// - .ready | ||
| case dependenciesReady(EditorDependencies) | ||
|
|
||
| /// The editor is fully loaded and we've passed all required configuration and data to it | ||
| /// | ||
| /// There are no valid transition states from `.started` | ||
| case started | ||
| } | ||
|
|
||
| struct EditorDependencies { | ||
| let settings: String? | ||
| let didLoadCookies: Bool | ||
|
|
@@ -125,9 +85,7 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor | |
| private var suggestionViewBottomConstraint: NSLayoutConstraint? | ||
| private var currentSuggestionsController: GutenbergSuggestionsViewController? | ||
|
|
||
| private var editorState: EditorLoadingState = .uninitialized | ||
| private var dependencyLoadingError: Error? | ||
| private var editorLoadingTask: Task<Void, Error>? | ||
| private var editorLoadingTask: Task<Void, Never>? | ||
|
|
||
| // TODO: remove (none of these APIs are needed for the new editor) | ||
| func prepopulateMediaItems(_ media: [Media]) {} | ||
|
|
@@ -215,8 +173,6 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor | |
| configureNavigationBar() | ||
| refreshInterface() | ||
|
|
||
| startLoadingDependencies() | ||
|
|
||
| SiteSuggestionService.shared.prefetchSuggestionsIfNeeded(for: post.blog) { | ||
| // Do nothing | ||
| } | ||
|
|
@@ -228,56 +184,16 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor | |
| // DDLogError("Error syncing JETPACK: \(String(describing: error))") | ||
| // }) | ||
|
|
||
| loadEditor() | ||
| onViewDidLoad() | ||
| } | ||
|
|
||
| override func viewWillAppear(_ animated: Bool) { | ||
| super.viewWillAppear(animated) | ||
|
|
||
| if case .loadingDependencies = self.editorState { | ||
| self.showActivityIndicator() | ||
| } | ||
|
|
||
| if case .loadingCancelled = self.editorState { | ||
| startLoadingDependencies() | ||
| } | ||
| } | ||
|
|
||
| override func viewDidAppear(_ animated: Bool) { | ||
| super.viewDidAppear(animated) | ||
|
|
||
| if case .loadingCancelled = self.editorState { | ||
| preconditionFailure("Dependency loading should not be cancelled") | ||
| } | ||
|
|
||
| self.editorLoadingTask = Task { [weak self] in | ||
| guard let self else { return } | ||
| do { | ||
| while case .loadingDependencies = self.editorState { | ||
| try await Task.sleep(nanoseconds: 1000) | ||
| } | ||
|
|
||
| switch self.editorState { | ||
| case .uninitialized: preconditionFailure("Dependencies must be initialized") | ||
| case .loadingDependencies: preconditionFailure("Dependencies should not still be loading") | ||
| case .loadingCancelled: preconditionFailure("Dependency loading should not be cancelled") | ||
| case .dependencyError(let error): self.showEditorError(error) | ||
| case .dependenciesReady(let dependencies): try await self.startEditor(settings: dependencies.settings) | ||
| case .started: preconditionFailure("The editor should not already be started") | ||
| } | ||
| } catch { | ||
| self.showEditorError(error) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| override func viewWillDisappear(_ animated: Bool) { | ||
| super.viewWillDisappear(animated) | ||
| if case .loadingDependencies(let task) = self.editorState { | ||
| task.cancel() | ||
| } | ||
|
|
||
| self.editorLoadingTask?.cancel() | ||
| if isBeingDismissedDirectlyOrByAncestor() { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The easiest way to verify it works is by setting a breakpoint. |
||
| editorLoadingTask?.cancel() | ||
| } | ||
| } | ||
|
|
||
| private func setupEditorView() { | ||
|
|
@@ -296,7 +212,7 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor | |
| setContentScrollView(editorViewController.webView.scrollView) | ||
| } | ||
|
|
||
| // MARK: - Functions | ||
| // MARK: - Helpers | ||
|
|
||
| private func configureNavigationBar() { | ||
| navigationController?.navigationBar.accessibilityIdentifier = "Gutenberg Editor Navigation Bar" | ||
|
|
@@ -361,51 +277,6 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor | |
| } | ||
| } | ||
|
|
||
| func startLoadingDependencies() { | ||
| switch self.editorState { | ||
| case .uninitialized: | ||
| break // This is fine – we're loading for the first time | ||
| case .loadingDependencies: | ||
| preconditionFailure("`startLoadingDependencies` should not be called while in the `.loadingDependencies` state") | ||
| case .loadingCancelled: | ||
| break // This is fine – we're loading after quickly switching posts | ||
| case .dependencyError: | ||
| break // We're retrying after an error | ||
| case .dependenciesReady: | ||
| preconditionFailure("`startLoadingDependencies` should not be called while in the `.dependenciesReady` state") | ||
| case .started: | ||
| preconditionFailure("`startLoadingDependencies` should not be called while in the `.started` state") | ||
| } | ||
|
|
||
| self.editorState = .loadingDependencies(Task { | ||
| do { | ||
| let dependencies = try await fetchEditorDependencies() | ||
| self.editorState = .dependenciesReady(dependencies) | ||
| } catch { | ||
| self.editorState = .dependencyError(error) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| @MainActor | ||
| func startEditor(settings: String?) async throws { | ||
| guard case .dependenciesReady = self.editorState else { | ||
| preconditionFailure("`startEditor` should only be called when the editor is in the `.dependenciesReady` state.") | ||
| } | ||
|
|
||
| let updatedConfiguration = self.editorViewController.configuration.toBuilder() | ||
| .apply(settings) { $0.setEditorSettings($1) } | ||
| .setTitle(post.postTitle ?? "") | ||
| .setContent(post.content ?? "") | ||
| .build() | ||
|
|
||
| self.editorViewController.updateConfiguration(updatedConfiguration) | ||
| self.editorViewController.startEditorSetup() | ||
|
|
||
| // Handles refreshing controls with state context after options screen is dismissed | ||
| editorContentWasUpdated() | ||
| } | ||
|
|
||
| // MARK: - Keyboard Observers | ||
|
|
||
| private func setupKeyboardObservers() { | ||
|
|
@@ -473,7 +344,36 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor | |
| } | ||
|
|
||
| // MARK: - Editor Setup | ||
| private func fetchEditorDependencies() async throws -> EditorDependencies { | ||
|
|
||
| private func loadEditor() { | ||
| editorLoadingTask = Task { @MainActor in | ||
| await actuallyLoadEditor() | ||
| } | ||
| } | ||
|
|
||
| @MainActor | ||
| private func actuallyLoadEditor() async { | ||
| showActivityIndicator() | ||
|
|
||
| let dependencies = await fetchEditorDependencies() | ||
| startEditor(dependencies: dependencies) | ||
| } | ||
|
|
||
| private func startEditor(dependencies: EditorDependencies) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved this with no changes. |
||
| let configuration = editorViewController.configuration.toBuilder() | ||
| .apply(dependencies.settings) { $0.setEditorSettings($1) } | ||
| .setTitle(post.postTitle ?? "") | ||
| .setContent(post.content ?? "") | ||
| .build() | ||
|
|
||
| editorViewController.updateConfiguration(configuration) | ||
| editorViewController.startEditorSetup() | ||
|
|
||
| // Handles refreshing controls with state context after options screen is dismissed | ||
| editorContentWasUpdated() | ||
| } | ||
|
|
||
| private func fetchEditorDependencies() async -> EditorDependencies { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not actually throw any errors – removing |
||
| let settings: String? | ||
| do { | ||
| settings = try await blockEditorSettingsService.getSettingsString(allowingCachedResponse: true) | ||
|
|
@@ -527,7 +427,7 @@ extension NewGutenbergViewController: GutenbergKit.EditorViewControllerDelegate | |
| // is still reflecting the actual startup time of the editor | ||
| editorSession.start() | ||
| } | ||
| self.hideActivityIndicator() | ||
| hideActivityIndicator() | ||
| } | ||
|
|
||
| func editor(_ viewContoller: GutenbergKit.EditorViewController, didDisplayInitialContent content: String) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If local cache read using
try Data(contentsOf: fileURL)were ever to fail, it would preventfetchSettingsFromAPIfrom running. The cache lookup should not stop the request.