Sync Tables: resetting edits/trying again on a per row basis

Hi @Eric_Koleda ,

I’m trying my hands at building sync tables with two-way sync and have encountered disappointing UI behaviour. So much so, that want to check that it is indeed intentional and not a mis-configuration on my side:

  1. Is it really the intended behaviour that automatic sending updates on sync table stops for all rows after the executeUpdate function returns (not throws) a single UserVisibleError for a specific row?

Further background:

I’m calling my custom API which handles bulk upserts and returns an array of either updated row data if successful or error data which I capture in my pack and to UserVisbileError for that specific row. When bulk editing a column in the UI for the first time and intentionally setting one cells to an invalid value, I do get the UserVisibleError displayed as a little icon the left side of the concerned row as expected, and the the remaining rows update as expected. However, then automatic sending of updates just stops ! So I cannot continue working on my other valid rows or do anything until I reset all edits on the table from the top right button and start over.

This may be acceptable for a single user, but it makes collaborating on sync table impossible. If one of my doc users inputs invalid data in some view on some row. All of the other users will just not be able to do anything else.

  1. Is there really no way to reset edits for a single row ?

Further background:
Back to the collaboration example: if one user messed up a single row, can I provide them with a button to reset just their edits on that button and try again? It seems surreal that the only way right now is to just reset edits at the top level of the table, potentially nullifying every other collaborator’s work.

  1. Is it really the case that a user’s only way to fix a bad input on a single cell is to reset the whole thing?

Further background:
One unintended (and very bad) side effect of coda choosing to stop automatic sending of edits as soon as even just one row comes back with an error is: the user can’t just fix their mistake by typing again a correct value. it won’t sync. Their only option is to reset all edits on the whole table and try again.

I find these issues so glaring and massively crippling to the whole two-way Sync Table concept being sold that I’m telling myself I must be doing something wrong here with how I configured things?

2 Likes

Hi @Nad - Thanks for the all the details. I did some testing, and re #1 it does appear that the table will stop kicking off automatic updates once a single row has a failure. I expect this was intentional, but to your points it’s not the ideal UX when multiple users are collaborating together. I’ll raise this with the team and see if they have any thoughts on how to address it.

Re #2, we currently don’t have the ability to reset an entire row, but you can reset an individual cell. If you right click on a cell you can choose the Reset edit option.

It would make sense to offer something similar for the entire row, and I can log that as a feature request.

I’d note there is also a button at the top of the table that will show the option Filter to rows with errors which was added to allow users to quickly isolate the rows that didn’t update successfully.

Re #3, instead of resetting edits, you can correct the errors on the broken rows and then use the Try again option in the dropdown.

This will manually kickoff a batch of updates, retrying any broken rows as well as including any other rows with edits.

Let me know if that answers your question, or if you have any other feedback.

1 Like

Thanks for taking the time @Eric_Koleda

further thoughts on #1:

I would further argue that automatic sending of edits should never be stopped, even for the offending row. To illustrate, here’s another bad consequence of the current design: let’s say a user starts off with a valid row (image 1 below), inadvertently clear a mandatory select list field by hitting backspace (2), realizes their mistake after the edit is being sent and and does ctrl-z to undo (3) . the current behaviour:

  • The ui thinks the pending edit was cancelled and no longer shows the little blue triangle in the top-left corner of the cell, even though the edit was actually sent on to the API
  • Meanwhile the response from the API/pack comes back with an error which gets associated with the row and stops any more automatic sending of edits
  • The user is now stuck in a limbo: for all purposes they see the row has valid input and no pending edits, but the error tells them otherwise, and they have no way to reset edits (3)

This is clearly a bug, and a direct result of Sync Tables trying to be too smart. Instead, automatic sending of edits should be humble and do what it says on the tin: send every single edit including undos of every single row back to the executeUpdate() function of the pack no matter what.

It is the job of the pack to handle any rate limits or other third party business logic. I can see an argument for a slight delay delay of 1 or 2 seconds ((ideally customizable by the pack) to allow batching of successive cell edits to a single executeUpdate() call , but that’s about it.

Regarding #3:

Yes I noticed that we can Try again at the table level in the ui. However, this is not available in Layout views.

The current “all-or-nothing” approach to Two-way sync fails to consider that in most non-trivial docs, end users usually interact with filtered views and inside layout views where the doc maker is responsible for building the UI.

Since the “unit of work” of the pack’s update, executeUpdate and userVisibleError is the individual row , we absolutely need to have corresponding actions/formulas in Coda to be able to offer consistent UX to end users. A rough sketch of these fomulas:

  • resetSyncTableRowEdits()
  • refreshSyncTableRow(?ignorePendingEdits) : if the row has pending edits, try to apply them again. if not or if IgnorePendingEdits = True then just override with a freshfly fetched row from the pack. (I’m aware of the current bad workarounds for forcing a row refresh, we need something robust)
  • syncRow.pendingEdits : formulaic access to an object that shows whether a sync table row has pending edits

To summarize: I consider not honoring the promise of sending all edits automatically to be a bug, and with really bad unintended consequences, so I urge the team to fix this ASAP :pray:. My other points stand, but they can be considered feature requests (although some of them like single row refresh have been requested for a few years now to no avail…)

(1)


(2)

(3)

2 Likes

Hi @Nad - I chatted with the engineering team and it does appear like the behavior you noticed is a bug. I’ve added it our bug backlog, but given the current priorities I wouldn’t expect to see a fix in the near term. I’ve passed along the impact this bug has on the user experience, and thanks for giving such clear examples. I don’t have any workarounds in the near term unfortunately.

3 Likes

Hello again @Eric_Koleda ,

To get around this bug, I decided to rewrite my pack to accept all edits in the row and return a custom errorsList object as an additional Sync Table column which I could then parse to do conditional formatting and highlight to the end Users where they need to take action/fix inputs.

So far so good… Except I found another bug!

The pack cannot accept an edit whereby a cell was turned to blank (which coda passes as empty string). No matter whether my pack returns an empty str or null for that cell, the ui runs the refresh but still think the edit is pending.

steps to reproduce:

  1. create a pack with the minimal reproducible example code below. the Items sync table does nothing other than accept any value passed for name and send it back.

  2. add the Items sync table, enable two-sync and automatic sending of updates

  3. hit backspace on any cell in the ‘Name’ column.

  4. behold an infinite loop of updates, as the ui things the edit is forever pending !

the only way to break out of the loop is to input a non empty value in the name cell.

Is such a crippling bug considered by Coda as also just something for the backlog ?

pack code to reproduce

// schema for each row
const ItemSchema = coda.makeObjectSchema({
  properties: {
    id: { type: coda.ValueType.String, required: true },
    name: {
      type: coda.ValueType.String,
      required: true,
      mutable: true,
    },
  },
  displayProperty: "id",
  idProperty: "id",
  featuredProperties: ["id", "name"],
});

// sync table definition
pack.addSyncTable({
  name: "Items",
  schema: ItemSchema,
  identityName: "Item",
  formula: {
    name: "SyncItems",
    description: "Syncs basic items with editable names",
    parameters: [],
    execute: async function ([], context) {
      // Sample static data
      const items = [
        { id: "1", name: "First Item" },
        { id: "2", name: "Second Item" },
        { id: "3", name: "Third Item" },
      ];

      return {
        result: items,
      };
    },
    // Handle updates when users edit the name. we just accept any edit sent
    executeUpdate: async function ([], updates, context) {
      let update = updates[0]; // Process one update at a time
      let newRow = update.newValue;

      console.log(`Updating item ${newRow.id} with new name: ${newRow.name}`);

      // Return the updated row
      return {
        result: [newRow]
      };
    },
  },
});
1 Like

one more bug:

After editing a sync table cell , column formulas that immediately depend on the cell recalculate immediately. They recalculate once again if a different cell value is returned by the packs executeUpdate . This behaviour is the sane and expected thing to happen - so far so good.

However,

The cell’s .modified() and .modified_by() attributes currently do NOT get re-calculated after the first edit. They only get re-calculated after the response.

For further context, you can see it’s a recalculation issue and not something else : if you open the formula editor on the formula that contains .modified()/.modified_by() reference while the update being sent, you get the re-calculated fresh value in the formula result preview. Also the ‘activity’ tab accurately keeps track of the two edits history.

EDIT: if you’re wondering why this matters, here’s an example use:
checking if

max(field.modified() for each mutable field in the table) > previous_response_time 

would have been a neat way to detect whether a given row is stale / currently being updated.

1 Like

@Nad - Thanks for the excellent bug report and repro code for the infinite update loop bug. That’s glaringly incorrect and I’ve forwarded it on to the product and engineering team to prioritize and fix.

As for the Modified() bug, I couldn’t reproduce that. When I edit a cell, I see the formula recalculate immediately. Could you provide some repro steps for that issue?

Thanks so much for following up @Eric_Koleda

The infinite update loop bug is a real show stopper, so appreciate it getting on the fast lane. everything else could be worked around with some patience.

The cell.Modified() and cell.ModifiedBy() bug is more subtle, and sorry that I wasn’t too clear in my first report.

Below a doc with pack code and step-by-step guide to reproduce.

Again it’s subtle, but it prevents reliable use of these cell attributes in the ui to detect ongoing updates. It and the previous bug “smell” like they may come from a common underlying issue in the coda ui code.

1 Like

Hi @Nad - The engineering team has been made aware of the infinite loop bug, and it seems to have been caused by some recent refactoring of that part of our codebase. They are working on a fix, but I can’t give you a ETA at this point.

Thanks for the additional detail about the modified behavior, I’ll dig into it.

1 Like