Skip to content
This repository has been archived by the owner on Aug 11, 2024. It is now read-only.

Introduce pending state for unresolved follows #648

Merged
merged 21 commits into from
May 22, 2023

Conversation

bfollington
Copy link
Collaborator

@bfollington bfollington commented May 16, 2023

Builds on #647 because I already threaded app into the profile view there.

  • Pull to refresh should dispatch .syncAll on app
  • Follow user form is not being reset between usages
  • Track which petnames are in-flight
    • Render this as an icon on the card
  • Stop waiting if petname is removed from addressbook (check getPetname before resolvePetname)
  • Move pending state into AddressBookService, provide a method to query the status and do that check when refreshing the profile
image

Demo

Screen.Recording.2023-05-16.at.5.46.10.pm.mov

Fixes #555
Fixes #492

Related to #604

@bfollington bfollington changed the base branch from main to 2023-05-16-pull-to-refresh May 16, 2023 04:49
@bfollington bfollington force-pushed the 2023-05-16-pending-follow-ux branch from a8f4897 to 610ff22 Compare May 16, 2023 04:51
@bfollington bfollington changed the base branch from 2023-05-16-pull-to-refresh to main May 16, 2023 04:52
@bfollington bfollington force-pushed the 2023-05-16-pending-follow-ux branch 2 times, most recently from e3d28fc to bf00658 Compare May 17, 2023 00:33
@bfollington bfollington changed the base branch from main to 2023-05-16-fix-json-profile-editor May 17, 2023 00:33
@bfollington bfollington force-pushed the 2023-05-16-pending-follow-ux branch from 606dd84 to f3bb630 Compare May 17, 2023 00:40
Base automatically changed from 2023-05-16-fix-json-profile-editor to main May 18, 2023 00:52
@bfollington bfollington force-pushed the 2023-05-16-pending-follow-ux branch from 28869b0 to 2f183eb Compare May 18, 2023 00:57
Comment on lines 408 to 410
switch action {
case .refresh:
app.send(.syncAll)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure everything is refreshed if a user is trying to manually refresh their following list.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way we do this is through the slightly more compact/fancy approach of:

.onReceive(
    store.actions.compactMap(AppAction.from),
    perform: app.send
)

where AppAction.from is an extension that creates an app action from a local action, or else nil. The advantage being that the AppAction.from extension can be tested.

Non-blocking.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean! I like that this moves the mapping nearer to the model and away from the view.

@bfollington
Copy link
Collaborator Author

@gordonbrander not related to this work but I think I'll disable the swiftlint github check, it just adds too much noise to the PR review. I'm using it in Xcode locally so I might update #573 to remove the github action as well.

@bfollington bfollington marked this pull request as ready for review May 19, 2023 00:31
@bfollington bfollington requested a review from gordonbrander May 19, 2023 00:31
Copy link
Collaborator

@gordonbrander gordonbrander left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a couple questions

Comment on lines 408 to 410
switch action {
case .refresh:
app.send(.syncAll)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way we do this is through the slightly more compact/fancy approach of:

.onReceive(
    store.actions.compactMap(AppAction.from),
    perform: app.send
)

where AppAction.from is an extension that creates an app action from a local action, or else nil. The advantage being that the AppAction.from extension can be tested.

Non-blocking.

@@ -116,6 +129,10 @@ struct StoryUserView: View {
}
.contentShape(.interaction, RectangleCroppedTopRightCorner())
.onTapGesture {
guard story.resolutionStatus == .resolved else {
return
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preference for this no-op to belong to the update function, rather than the view. When actions handle their own no-op based on model state, it's more easily testable. It also makes sending the action foolproof.

Non-blocking.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I introduced a new action requestNavigateToProfile which can result in pushDetail if resolved or failPushDetail if not. This logs a debug message and does nothing else atm.

@@ -58,6 +62,8 @@ struct Func {
) async throws -> T? {
do {
return try await perform(attempts)
} catch RetryError.cancelled {
return nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open question, but would it be better to have this retry with backoff logic be part of an update function instead? (If fx fails, send another after a growing delay).

The advantage would be that retry would be forced to fold with app state at each step. The actions would be sent through the logs, and current backoff value would be written to the model where we can inspect it.

Non-blocking.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the value of this but it could be a little over-complicated right now.

The main issue I see is that multiple of these processes can be running at once (rapidly following multiple users before the last one resolves), so tracking the state will require attaching an ID to each background job etc.

I'd like to think a bit more about the right abstraction to make it trivial for different components to sign up for this functionality and share the code, put some initial thoughts down here: #658

@@ -7,6 +7,12 @@

import Foundation

enum PetnameResolutionStatus: Equatable, Hashable, Codable {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qs:

  • Should this be a general enum, similar to LoadingState?
    • Is this the same semantics as LoadingState? It's ok if subtly different.
    • If different, still worth generalizing this I bet. We have a lot of places that have the same network resolution lifecycle.
  • Is there a resolved success/failure? (resolved(Result))

Non-blocking.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dropped the Petname prefix and extracted the type and added a Cid payload to resolved since I currently view the definition of resolution as "take this address and give me the CID on the other end".

@bfollington bfollington merged commit 78c67c2 into main May 22, 2023
@bfollington bfollington deleted the 2023-05-16-pending-follow-ux branch May 22, 2023 00:11
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sync after following user Kick off gateway sync after adding new follower
2 participants