Real-Time Row Selection Management

I have a table where the data comes from an API call. This table is used by multiple users, and each user can select only one row. However, I want to ensure that once a user selects a row, it becomes unavailable to other users—it should either be hidden from them or they should be unable to select it.

My approach is to use WebSocket for real-time communication since I am using APIs.

useEffect(() => {
    const ws = new WebSocket(WS_URL);
    ws.onmessage = (event) => {
      const lockedData = JSON.parse(event.data);
      setLockedRows(Object.fromEntries(lockedData.map(row => [row.row_id, row.locked_by])));
    };
    return () => ws.close();
  }, []);

@Arslan_Doddamani,
Are you asking for help on how to configure the front end so that table rows are unclickable or disappear when another user clicks it? Or are you asking for help on how to set up web sockets?

If it's the web sockets part, here are a couple resources for how to incorporate web sockets into your retool app:

1 Like

Thank you @lindakwoo
I was asking for help on how to configure the front end so that table rows are unclickable or disappear when another user clicks it.

Web sockets was my approach to solve it. If there is any other approach then I am happy to know it.

Yes, @Arslan_Doddamani , I agree with you that websockets is the best way to approach this. You will need to set up your own Websocket server.

1 Like

Got it @lindakwoo.
I have referred the above dco to setup web sockets in retool. But I couldn't get it.

Can you please help me to add a web sockets in a table component by guiding me step-by-step.

@Arslan_Doddamani,
Do you have your backend websockets server all set up?

Yes. It is ready. Just guide me how to do it with retool

@Arslan_Doddamani,
Okay, here is the general approach you would take:

  1. you will add a "locked" property to each of your rows

  2. query this database for all items where locked===false and set it as the data source for your table.

  3. add a row action that uses a "lock" icon and triggers your sendWebsocket query:

  1. Your sendWebsocket query will first update the database, re-query the database in the success handler, and also send the websocket message:

  1. Add a js query to listen for incoming websockets. When one comes in, trigger the query that gets the data for the table. Go to the advanced tab in this js query and click on: "Run this query on page load"

I hope this helps! :smile:

1 Like

Hi @lindakwoo,

I’m not using a database — I only have access to APIs. Here are the APIs I’m working with:

  1. Retrieve Table Data
    • Endpoint: /api/table
    • Method: GET
      * Response: Returns table data as an array of objects:
[
  {
    "id": 1,
    "name": "Item 1",
    "value": "A",
    "lockedBy": null,
    "lockedAt": null
  },
  ...
]
  1. Lock a Row

    • Endpoint: /api/lock-row
    • Method: POST
    • Parameters: { "rowId": 1, "userId": "user-123" }
    • Response: io.emit('update', table);
  2. Unlock a Row

    • Endpoint: /api/unlock-row
    • Method: POST
    • Parameters: { "rowId": 1, "userId": "user-123" }
    • Response: io.emit('update', table);

Mention if there is any changes to be done in the api's or you have any other approach.

Note: I can change only the api server later I will connect to database, trying to perform this task by just a array of table data.

@Arslan_Doddamani,
If you are not querying a database, but instead are using an api, that is fine. Instead of querying the database, make a call to the api and then add a transformer to transform the data to remove the items where lockedAt is not equal to null (unless you want to add another api endpoint to retrieve unlocked rows only). Then use this transformed data as the source data for your table.

I have mocked some data from your api here:

and created the transformer for you:

The table will be the same as before, but instead of triggering a change to the database, you'll trigger the api call that locks the row and then automatically call the api that gets the table when that succeeds. To pass the row id, you can trigger this api with the same additionalScope object that I describe above in triggering the change to the database. And then when you listen for the websocket, you will trigger the api that gets the table data.

Let me know how this goes!

Thank you @lindakwoo for the response.

I got the idea. Here's what I have done so far:

  • Created 3 API calls: one to get the table data, one to lock a row, and one to unlock a row.
  • Created a transformer to process and transform the table data.
  • Created a JavaScript query sendWebSocket, which triggers and fetches the updated tableData.
  • Added a table component to the canvas and connected it to the transformed data from transformer1.
  • Added a row action: when a row is clicked, sendWebSocket is triggered.

Current Output:
The selected row becomes invisible.

So far, everything is working fine.

Now, my task is to implement real-time row selection.
If you've ever used Google Sheets with a team, you'll notice that when someone selects a cell, it's locked for others to select or edit. I want to implement the same functionality—when one user selects a row, others shouldn't be able to select it.

I hope that clarifies my task.

@lindakwoo
I hope you have understand the task, if haven't let me you I will explain it again.

@Arslan_Doddamani ,

This is where you implement a js query that runs on page load that listens for web sockets. When it receives a web socket message, it will then trigger a refetching of api that gets the table data.

So in real time, when one user selects a row, that action will trigger the lock row api call and then a fetching of the table data, and then it will also send a web socket message. All other users will immediately receive that web socket message, which will then trigger then to make the API call which will return the latest data.

@lindakwoo this is the output.
check it here.

I want to implement the functionality—when one user selects a row, others shouldn't be able to select it . when user select other row then it should selected row should be unselected.

I wanted to know some other things too.

  • Where the web sockets is used in this we are just calling the api's. if server emits something it will not display until we reload or call a api.
  • When I implemented this in react.js I had to use the useEffect to have a connection with bi-directional connection.
  • The table has pagination and will require lot of time to fetch the data. So reloading of the page will be not efficient.
  • The user will select the row for just 2 to 3 minutes.

Hi @Arslan_Doddamani,
Sorry, but I was mistaken. You cannot listen for a websocket in the regular retool app. You must create a custom component and set up your useEffect in it like you have done above. Then you import it into your parent app and pass data (the specific row information from the websocket message) from the custom component to the main parent app.

In order to not have to fetch the api every single time, in the parent app, set up a global variable from your initial api fetch that captures the table data. Set this global variable as your data source for the table. When you receive the new data from the custom component, after it receives the websocket message, you can update this global variable to set whatever row needs to be locked.

In the table itself, you can filter the rows by adding a filter that checks if the "Locked" column is true or false:

Here are some docs on how to set up your custom component and then pass the data back to the parent app.

Let me know how it goes.

1 Like

@lindakwoo
Thank you for your response.
This solution is working good for me.

1 Like

@Arslan_Doddamani,
Just curious. Did you end up using the custom component, and would you mind sharing it with me? thanks!

1 Like

Hello @lindakwoo
I have just build a complete coustom component then just draged it to the canvas .

https://arslandoddamani1420.retool.com/apps/b33d46de-175e-11f0-9726-a7ce12b489d3/LabelUI/page1