CallumBoase / plasmic-supabase

GNU General Public License v3.0
7 stars 5 forks source link

SupabaseProvider & SupabaseUserGlobalContext element actions need to optionally wait for completion & return values for next ACTIONS #3

Open CallumBoase opened 4 months ago

CallumBoase commented 4 months ago

Context

When adding interaction actions (eg a click handler on a button) in Plasmic studio, actions have access to the return value of previous actions via the $steps variable (in code) or "Previous step results" in datapicker.

This can be replicated by going to Plasmic studio, adding a button, assigning a click handler, and then adding 2 code steps. In the 2nd code step, if you click the "code" box, you should see "Previous step results" available in the data picker.

image

Strings of actions setup in Plasmic studio will wait for the previous action to finish. So if action 1 is an async function, action 2 won't run until action 1 has returned.

Problem

The SupabaseProvider component element actions return instantly (while promises proceed in the background), and never returns a value.

While this is OK for some cases, it limits uses. For example, it's not possible to wait for a mutation to finish successfully before redirecting the user to another page.

Another example of what's not possible: run an "Add row" mutation, get the ID back from Supabase, then use that ID in a further API call.

Solution

When running element actions in SupabaseProvider, give the user choice about whether the action returns instantly (with no value) or whether to wait for the mutation to finish and return the result of the mutation.

A pattern that should work is seen here: https://github.com/CallumBoase/Plasmic-code-components-v2/blob/supabase-storage-modified/components/PromisesPattern.tsx

CallumBoase commented 3 months ago

When signing up a new users via the SupabaseUserGlobalContext global actions in Plasmic studio, if we were able to add subsequent actions that receive the value of the signup request & only run once the signup request has run, we would then be able to do things with the value of those fields Eg sign a user up (1) and then create a profile (2) as a separate action via a normal supabase database API call.

Consider whether this can be tackled at the same time

ryanmouritz commented 2 months ago

To note when implementing this change: By default the supabase API doesn't return the record for insert, upsert, update or delete. In order to do so, we will need to chain a .select() to the relevant API call. This may be assumed (hardcoded) or optional.

CallumBoase commented 2 months ago

Supabase does not automatically return inserted or updated rows in response to an insert or update API call.

Therefore, in element actions to insert or update row, we should give the user the option to run a select() of new row after insert/update. This selected value would be returned from the add/edit row element action so that subsequent actions have access to the inserted/updated row.

If the user does not opt-in to run select() after insert/update, we would not be able to return the add/edit row for use in subsequent actions, but still could return the optimistic row and a status of success or error. If the user DOES opt-in to select() after insert/update, then we would return the row and subsequent actions would have access to that row along with success or error status.

Note, if running select() after running insert/update element actions, I recommend NOT attempting to populate the data cache after mutation (even though useSWR gives this an an option).

Reason: I tried extensively to make this work in a similar component library (Plasmic-Knack -> KnackProvider) and it caused issues when paired with optimistic updates, where only the last edited/added row would populate into the cache, leaving stale data in the cache.

To avoid populating cached data with returned rows, ensure that populateCache is false when running mutate() in element actions

Eg for addRow, here is the correct options object:


//eg inside the addRow element action
const result = await mutate(addRow(rowForSupabase), {
    optimisticData: //function here as normal
    populateCache: false,//This avoids the return value of addRow populating the cached data
    revalidate: true,//This ensures that after mutation we refetch all rows from Supabase to repopulate the cache
    rollbackOnError: true
})