Identifying Browser Tabs

I want to keep track of the current "IDs" of various entities being edited in an app, and save them to the database.

This is simple if I just save a bunch of IDs using the current_user.sid as the row identifier, into a database. However, if the user opens the app in two different browser tabs, I need to keep track of the separate IDs in play in each browser tab. It's the same user using each tab so events occurring in one tab end up overwriting the IDs for the events occurring on the other tab because the current_user.sid is the same.

I need to be able to identify each browser tab, or the session in each browser tab.

eg. currently the database row consists of ...

user_sid, object1_id, object2_id, object3_id

what I need is ...

user_sid, browser_tab_id, object1_id, object2_id, object3_id

How do I uniquely identify each tabbed session please? It's got to be possible, it seems like it should be a really simple thing to do. Like window.currentTabid or something like that, but I can't find anything anywhere.

Even getting the server session-id would do, as I believe these are unique per browser tab.

Hi @xl_MURDOCH_lx ,
You can set a browser_tab_id by setting a unique variable for it and saving it to the database. You can use the uuid.v4() function to create a random, unique string to assign to the browser_tab_id. So,
first create a new variable called browser_tab_id:

Then create a query like this:

if(browser_tab_id.value===null){
   browser_tab_id.setValue(uuid.v4())
}

else throw new Error("browser tab id already created")

In the success handler for this query, save everything to your database, including the current User and the browser_tab_id.

The only issue with this approach is that if a user refreshes their page, they'll get another browser_tab_id. Is this what you want or do you want just ONE browser_tab_id per user per tab, regardless of them hitting refresh on their open tabs?

Hi,

Many thanks for this, I'll give it a go but I don't think it's going to work. I'd have to put this query / code on every page and every module in the app, which doesn't seem practical. Also, I'd like the ID to remain even if the browser is refreshed or the page changes. Is the server SessionID not exposable to code?

Kindest regards,

Guy.

This would all be a lot easier if I could access the app's "Global" variables from a module, but because modules can be used across multiple apps they don't seem to be able to "see" the global variables.

@xl_MURDOCH_lx ,
If you are using a multi page app you can just put the queries in your global scope.

@xl_MURDOCH_lx
Retool does not give you access to the session storage, unfortunately. However, I think I may have a solution for you using search params in your url.

Declare a variable in the global scope called browser_tab_id and set it initially to null:

On one of the pages in your app, set the search url parameter like this:

Then query your database on page load, and in your success handler, run this query:

in its success handler, run the query that adds the data to your database, including the browser_tab_id:

So we start with no search params when we open a new tab. When this happens, we create a search param and add it to the url and then add this to the database. Nothing happens when we switch pages between apps cause this will not cause a page reload. On refresh, the search param will still exist, so the actual browser_tab_id will change, but it will not be added to the database because an error is thrown, so the success event does not happen. (this just avoids it being set to null, which would trigger the database action on the next refresh)

Will this work for you??? :grinning:

Hi! Thank you so much or getting involved in this. Again, this causes more problems than it solves as it involves pasting the same piece of code on every single page in the app. So, if anything changes and I need to adjust the code, I'm facing 100+ pages of code changes.

All I really want is a way of uniquely identifying tabs without having to copy and paste code to every page and module on the app.

Your solution works, of course, so thank you. It just feels a bit clumsy.

Unfortunately, this also seems to involve global variables, which are not readable from modules, as modules can be used in multiple apps (each of which have their own global variables).

Life would be a lot simpler if Retool could expose the SessionID.

Just had an idea, possibly a Feature Request.

Could Retool add the feature to prefix JS commands with the app name, and therefore allow explicit calls to app global variables?

I have an app called "nav" which is the main navigator for the overall tool I'm building. It would be ace to be able to refer to the global variables in that app by prefixing the code with the app name.

eg.

nav.str_ACCOUNT_ID.value
or
nav.global.str_ACCOUNT_ID.value

This would make the whole SessionID / TabID issue null and void, as the global variables are localised to the session / tab anyway.

@xl_MURDOCH_lx ,
You can place all your code (which is the the 3 queries and the variable) in the global scope, so you would just have to do it ONCE because all pages have access to it in a multi-page app. (are you using multi-page app?) I'm not sure why your modules need access to the global variables cause we are simply placing a search parameter on one page (I believe all other pages will have it if you add it to just one page). You are importing modules into your pages, correct? See the screenshot below. I have placed all my queries in the global scope, just once and all three pages have access to it. Note that page3 has the search params in its url even though i have not explicitly set them on page3 (only page1).

I will file a ticket, meanwhile, to give access to session storage.

Hi Linda,

Yes, my app is multi-page, and modules are re-used across multiple pages.

My re-usable modules contain data tables and a side drawer, which are used in multiple places across the app.

When an item is selected in a module's table, or the "edit" button is clicked in the side-drawer, I need to write various IDs to the database, including the tab ID., so I can keep track of the "current state of affairs". As modules cannot read or write to the app's global variables, this makes life pretty difficult and impossible to use your solution to ID the tab, as the modules cannot "see" the global tab id.

The modules are all "search" screens, which search through the database for businesses, people, contracts, products etc... and are repeatedly used across the app either as main screens, or as "tabs" when looking at detailed views. When the user selects an item in the search results, I need to update the database to retain the IDs of the current selection.

I'm afraid your solution doesn't work, as it adds the tab_id to the url hash after the page has loaded so that page is unaware of the param, it is not sent to the page

Access to session variables, where there is a separate session per tab seems the only solution to me.

@xl_MURDOCH_lx ,
I agree that the best solution would be to have session ids, but until we can offer that, I THINK that this solution can be a viable, if cumbersome, workaround. Check out my simple app here which includes a basic module. Create a simple table in retool DB using the attached CSV file and use my app with the app module that are both attached below.

With this setup, I am able to capture when a new user opens the app in a tab. It only saves this to the database once, and will not save again when pages are changed or there is a refresh.

With regard to the modules not having access to global scope, you can pass the browser_tab_id down from the parent page into the module so that you can reference the current browser_tab_id when the user does certain actions from within the module. When a user switches pages or refreshes, the broswer_tab_id will remain the same, and get passed to those pages' modules.

Regarding your comment above, I am not sure what you mean by the params not being sent to the page. On page load, I have the app checking to see if there are url params...If not, a browser tab uuid is created and saved in the db and then the searchParams are added for subsequent page changes or refreshes. That is what we want, isn't it?

tab_id.json (46.5 KB)
tab_id module test.json (8.4 KB)

tabIndexUsers.csv (204 Bytes)

I hope this helps. Let me know.

Thanks loads, I didn't realise the parent page could pass params to the underlying modules. I shall give that a go.

const existing = window.sessionStorage.getItem('tab_id');
if (!existing) {
  const newId = uuid.v4();
  window.sessionStorage.setItem('tab_id', newId);
  utils.openUrl({ url: '/home' });
}

Someone suggested this as a possible solution but unfortunately "window" is sandboxed and I can't access the properties / methods. It would be brilliant if as well as "localStorage" we could access the "sessionStorage" object.

@xl_MURDOCH_lx,
I totally agree that it would be nice to have session storage. There has been a ticket that has been created to address this. Meanwhile, if you need help passing params from the parent to the module, let me know. You can read about how to do this here.

if(str_TAB_ID.value==null){
  console.log("str_TAB_ID is null, setting value...")
  str_TAB_ID.setValue(uuid.v4())
  /* urlparams.hash.tab_id = str_TAB_ID.value; */
  /* utils.openUrl(str_URL_HOME.value, {newTab: false}); */
}
console.log("str_TAB_ID = " + str_TAB_ID.value);

For some reason, this doesn't set the global variable "str_TAB_ID" - the console shows this...

[qry_SET_TAB_ID] TAB_ID is null, setting value...
[qry_SET_TAB_ID] str_TAB_ID = null
qry_SET_TAB_ID ran successfully (0.01s)

What am I doing wrong please? This looks like it should be straightforward but doesn't appear to be working.

str_TAB_ID.setValue(null)

if(str_TAB_ID.value===null){
  console.log("str_TAB_ID is null, setting value...")
  str_TAB_ID.setValue(uuid.v4())
}

console.log("str_TAB_ID = " + str_TAB_ID.value)

When you run this over and over again, the value for str_TAB_ID is null, then it's not, then it's null, then it's not.

I thought the behaviour for this would be that it would return the same value every time, and never be null?!

I got around this in the end but not using ".setValue()" and instead using ".value ="

I think there's an issue with the way .setValue is currently working in ReTool

Hi @xl_MURDOCH_lx Sounds like you may have run into this issue