Skip to content

feat: async state - #737

Draft
devnatan wants to merge 2 commits into
mainfrom
feat/async-state
Draft

feat: async state#737
devnatan wants to merge 2 commits into
mainfrom
feat/async-state

Conversation

@devnatan

@devnatandevnatan commented Apr 27, 2025

Copy link
Copy Markdown
Owner

Generic asynchronous state. TBD.

Today we have a limbo in the flow of opening an view that is at the moment BEFORE opening and AFTER opening, where the information between those steps goes. People often come to me with questions about how to deal with asynchronous data due to the nature of the Inventory Framework views. Async state come to help.

Without an async state we needed to write a UserProfileView that loads a UserProfile from the database we...

  1. Create a initialState that holds a UserProfile type
  2. Opens Inventory Framework view with it as initial state.
classUserProfileViewextendsView {
privatefinalState<UserProfile> profileState = initialState();
@OverridepublicvoidonFirstRender(RenderContextrender) {
finalUserProfileprofile = profileState.get(render);
}
}

This makes the view dependent on the value coming from wherever, given that the view can be opened in multiple ways and makes it incompatible with the idea of ​​creating Flows with inventories, so, if we use for example a context.back() or context.openForPlayer(UserProfileView.class) from another view, we would need to somehow obtain this value (UserProfile) that is being pulled from the database asynchronously and assign it as a parameter of the view but... the problem here is that this view that is calling the UserProfileView view is not asynchronous and it does not have the capacity to process this information.

Now with asynchronous state we...

  1. Create a initialAsyncState that holds a UserProfile type that expects a CompletableFuture<UserProfile> parameter
  2. Assign that state value with values injected from constructor like UserProfileManager or UserProfileDatabase;
classUserProfileViewextendsView {
privatefinalAsyncState<UserProfile> profileState;
publicUserProfileView(UserProfileDatabaseuserProfileDatabase) {
// Creates a new Initial Async State that loads user profile once the view is openprofileState = initialAsyncState(ctx -> userProfileDatabase.loadProfile(ctx.getPlayer().getUniqueId()));
}
@OverridepublicvoidonOpen(OpenContextopen) {
// Waits until profile is loaded before opening the inventory (optional)open.waitFor(profileState);
}
@OverridepublicvoidonFirstRender(RenderContextrender) {
UserProfileprofile = profileState.get(render);
// If we DO NOT use `open.waitFor(profileState)`, inventory will not wait// user profile to load to open so `profileState.get(render)` will return `null`// in that case, we can show a loading state inside the inventory.AsyncValueprofileAsyncValue = profileState.access(render);
// Shows a PAPER if profile stills loading, hides automatically once profile is loadedrender.middleSlot(newItemStack(Material.PAPER))
.displayIf(profileAsyncValue::isLoading)
.updateOnStateChange(profileState);
}
}

Error handling can also be done in the view itself, showing a item for example!

@OverridepublicvoidonFirstRender(RenderContextrender) {
UserProfileprofile = profileState.get(render);
AsyncValueprofileAsyncValue = profileState.access(render);
// Shows a BARRIER if profile load failsrender.middleSlot(newItemStack(Material.BARRIER))
.displayIf(profileAsyncValue.getResult()::isError)
.updateOnStateChange(profileState);
}

I will rewrite State Management on Wiki entirely before merging this PR.

@devnatandevnatan self-assigned this Apr 27, 2025
@devnatandevnatan added the feature New feature or enchancement label Nov 28, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

featureNew feature or enchancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@devnatan