How to attach anonymous data to existing user after they log in?

Let’s say a person comes to my site, does some stuff that saves data to the user (as an anonymous user because they’re not logged in), and subsequently logs into their existing user account.

How can I attach that new data they saved to their existing user account?

It works fine if the user is a new user who creates some data and then registers - the data is attached to them. But if they create some data and log in, the data is not attached to them (presumably it stays with the anonymous user, who remains anonymous forever and ever!).

5 Likes

I ran into exactly this recently and solved it with a simple cookie (read on for deets).

As you note, for new users it works fine to save data to the “Current User” which then gets attached automagically when that anonymous user converts to a registered user – at least within the lifetime of the cookie/anonymous user in the database [something like 3 days, I believe?].

However, this does not magically work for an existing registered user who visits your site, does some stuff, and then later in the session logs in. This is particularly problematic if you are using OAuth authentications as your page is going to be reloaded after authentication is complete – so the state of things on the page will be reset.

In my case (a reservations type site), I wanted to make it so that we don’t force the user to log in before they start to make an inquiry/reservation. However, we want to log them in or register them at some point in that workflow. So this is a nice, bounded problem that’s perfect for a session cookie.

Here’s the most “bubbly” way I could think of to solve this issue:

  1. When a non-logged-in user goes to submit an inquiry, we continue to store info on the user, however, we also create a new database object. In my case, I call this a “Temporary Inquiry” which stores all of the data we will need to reconstruct the state of the inquiry after the user logs in. (In my case, this is stuff like dates they’ve selected, what listing it is they are inquiring about, the number of guests, etc.)

  2. We grab the unique ID of that new object and store that unique ID in a session cookie. So this is super-lightweight, right? Just a simple text string (but it points to an object in the database that could actually be very, very complex).

  3. If the page is reloaded and there is data in this session cookie, we know that the user was in the process of making a reservation and we can rebuild the state of that inquiry for them so they can continue where they left off.

Here are the details of how I implement that:

COOKIE

The library I’m using for this is js-cookie, which is super-simple and easy to use (and has defaults that perfectly match this type of use case). That library is documented here: js-cookie - npm

To use it, we will need the toolbox plugin (so we can Run Javascript) and we will also need to include the library on our page. So, in an HTML element, we include js-cookie like this:

<script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.2.0/js.cookie.min.js"></script>

Syntax is super simple as you can see from the docs page I link to above. For example, to set a session cookie for the site, we just call:

Cookies.set('name', 'value');

Creating the Temporary Thing and Storing its ID in a Cookie

Here’s an example of a workflow. What’s highlighted here is what happens when a non-logged-in user clicks the submit button in my inquiry widget:

Step 1. I’m still saving some data on Current User (in my case this is more-or-less optional now, but it’s not hurting nobody, right?)

Step 2. Create a New Thing in the database (in my case, it’s all the reservation data they’ve input, along with the unique ID of this “listing” page). If you wanted to get fancy, you could, add a step after here to schedule a workflow to delete this temporary item at some point in the future.

Step 3. Run javacript to set the cookie. The contents of that step look like this:

The first line just echoes data to the console for debug purposes. The second line is where the action happens:

Cookies.set('SIID'), '_unique ID of the thing we created in step 2_');

That creates a site-wide session cookie called “SIID” (“Submitted Inquiry ID”, see?) which contains a text that is the Temp Inquiry’s unique ID in our database.

Step 4. It’s now safe to display the Signup/Login Popup.

Restoring the Inquiry State on Page Reload

Depending on how the user signs up or logs in, the page might be reloaded and we might need to pick up where the user left off. To handle that, we do this:

Step 1. Run javascript to read the cookie. The action here takes place in lines 2 and 3. Line 2 reads the cookie and shoves its value (which, remember is just a text value of the Temp Inquiry’s unique ID). Line 3 pushes that value back to Bubble using the JavaScript to Bubble element (also part of the “Toolbox” plug-in).

The JS to Bubble element is configured on my page like this:

When we execute bubble_fn_cookie(value); that value is published back to Bubble as element “JS Cookie’s value”.

You may be wondering: What if the cookie does not exist? Well, if that’s the case, js-cookie just returns a value of undefined (what in Bubble terms is called “is empty”). So again that library is perfect for our use case.

Step 2. I fire off a Custom Event called “Restore Submitted Inquiry”. This custom event is configured as taking a thing of type “Temp Inquiry”. We find it in the database this way: We “Do a Search for… Temp Inquirys… where the Temp Inquiry’s unique ID is equal to what we got back from the cookie”. That search will return a list, of course, but it will only have 1 item in it (hence “:first item” appended to the end):

The “Only When” condition is to handle the following issue:

  1. I’m not being fancy with this cookie. It only stores a single value (though it could have been an array of values or whatnot) and only one will exist for the session. So we can be guaranteed that this cookie represents ONLY this user. Further, if we find a value here (it’s not undefined/empty) the user did reach the point of submitting an inquiry and we should reconstruct their inquiry so they can finish submitting it.

  2. However, what we don’t know is this: The user may have hit this point ON A DIFFERENT LISTING PAGE. Imagine that the user looked at Listing A and started to submit their information. They clicked submit and then, when presented with the login/sign-up pop-up, they abandoned the inquiry. However, they then continued to Listing B.

  3. So, we need to check that their Temp Inquiry is about the Listing that is related to THIS page. When I create the Temp Inquiry, one of the pieces of info in it is the Listing that the inquiry relates to. So here, we trigger the “Restore Submitted Inquiry” custom event ONLY WHEN… The Temp Inquiry’s Listing ID is equal to THIS Page’s Listing ID.

My custom event (“Restore Submitted Inquiry”) looks like this:

You’ll note that (even though I also did this in the trigger) I put an “Only When…” condition here so that, if the workflow thing (Temp Inquiry) is empty, we don’t bother trying to do all of this stuff. All of the workflow steps are just all of the things I need to do to reconstruct the state of the page/user’s in-progress inquiry.

After all of that is complete, the user (who is now Logged In) can complete their inquiry.

When they do, I execute the following workflow. Now they are Logged In, and clicking the Submit button will create a full-fledged inquiry associated with the logged in user and we can clean up the temp stuff and cookie (should we desire and should they exist):

The relevant steps are these:


Again, just a simple function call deletes the cookie (which, because its a session cookie, will be deleted when the user ends their session / closes the browser anyway… depending on your use case you might want to keep the cookie around for the rest of the session… but in my use case I’m done with it and so we remove it).

While the above is very specific to my use case, I’m sure you can imagine many scenarios where this general technique would be useful.

Session cookies in particular are so useful that I am A BIT surprised that there’s not a built-in Bubble element / workflow actions to do simple cookie tricks like this, but with js-cookie being so simple to use, it’s practically the same thing.

Aside: the Run Javascript action and the JS to Bubble element from the Toolbox plug-in are super-handy! Once you understand the basics of using them (and the example above is enough to get you started, for sure), the palette of things you can do in Bubble expands greatly.

Most of what I use them for is executing very simple little scripts like this.

36 Likes

BTW, I did a short screen demo to put my comments above in context:

vimeo.com/277521133

1 Like

This video is helpful, may be worth adding to Stakk.co

Wow, above and beyond, thanks @keith! I’m going to try one other method first that I think will be simpler, albeit not as cool as yours, but I think your approach is going to be perfect for some people, especially when Oauth is involved. I’ll post here if my method works.

I literally had just gotten that working, so perfect timing. Would love to hear what you come up with. I’m sure there are several other ways to skin that same cat!

Nice in depth tutorial @Keith

Before I go down the rabbit hole with this, would this method help with addressing the issue with Safari ITP? I need to basically set a cookie on session before redirect as outlined here: https://help.shopify.com/en/api/guides/itp-impact

Thanks in advance!

Sorry @dan8, while I skimmed that link, I don’t have much interest in Shopify and I don’t really understand what the application here is.

However, you can test, right? And actually experimenting with libraries like the one I mention in this thread is easier than ever. Search for my post on my (“Karma-Ware”) plug-in “LOADERR” for a way to load scripts even on free accounts (it’s actually a good solution anyway, as if you decide not to use the library in question, you just delete the element and don’t have to remember to remove it from your page header (LOADERR is cool like that).

1 Like

Thanks @keith I will check that out. I worked out that the solving of the issue vs time vs benefit isn’t worth it right now so will address it further down the line.

It’s an interesting one though as it’s affecting a number of websites and applications out there. It’s based on Apple’s Safari browser on 13 and up. Called “ITP”, it’s to prevent tracking scripts mainly from the marketing side of things. The problem is that in doing that, they block the ability for iframes to embedded on other domains and load any session data unless the user interacts with the application on the parent url first. The work around is you redirect the user to the parent url and set a cookie from a user interaction and then send them back.

Hi Keith,

Is there any extra step to be done here for initialising Cookies?

I am getting such errors.

Thanks,
Mukesh

I managed to get it solved.

Two key learnings:

  1. While putting javascript code that has dynamic elements, putting single quotes is very important. e.g.
    It will work:
    Cookies.set(‘TempID’, 'Result of step 1 (Create a new Cust…)‘s unique id’);

This will NOT work:
Cookies.set(‘TempID’, Result of step 1 (Create a new Cust…)'s unique id);

  1. When doing the javascript bit which does cookies.get and bubble_fn_cookie, it is important to put the “Current user is logged in” condition within the high level condition instead of within “Run Javascript” i.e.
    This will work:
    Screenshot 2020-05-09 at 7.19.01 PM

This will NOT work:
Screenshot 2020-05-09 at 7.19.22 PM

1 Like

Hi Keith,

Your tip was great and I went that route for a two months. However, I kept running into a lot of problems and finally found another solution. Posting it here for others’ reference.

So, the problems I ran into was that the “JS To Bubble” bit is not very reliable.

  • Either the plugin has issue or Bubble doesn’t work very well with it.
  • Many a times I would try to read value from it and it would fail and some unpredictable behaviour is shown (I have a few threads on it, none of them conclusive)
  • Unpredictable behaviour would be like app crashing, or mail not going, or temp enquiry that we are creating, not getting saved, breakpoints not working, different behaviour different times etc.
  • I spent a lot of days and nights on it. I also took professional help and he couldn’t fix it either.

Solution that I have come down to would not work in some people’s cases, but would work for many.

  • So, what I have done is something similar to Keith’s solution. I create a temp thing, and later on when user logs in, I connect the temp thing to logged in user.
  • However, instead of using cookies to store the temp value, I use the database itself.
  • Key thing in my case is that the enquiry has a mobile number field which I have kept as mandatory. So in a temp_enqiry_mapping table, I store a map of mobile to temp enquiry id apart from creating the temp enquiry in main enquiry table.
  • Now when user logs in, I look up the temp_enquiry_mapping table to see if there is an enquiry for that user in the table. If it is there, then I use it, otherwise I proceed usually.

This is working so far. Hope it helps. I don’t know why I spent so many days without realising I could do this.

UPDATE
And I am back to square one :frowning:

Turns out I don’t have mobile number for most number of users when they sign-up/login, and hence I can’t make the linkage.

I wonder how others are solving this problem without cookies.

Thanks,
Mukesh

You, Sir, are a gosh darn hero

Hi Mukesh,

I had few bugs that i couldn’t fix with the method of Keith. I guess it has something to do with the Toolbox plugin.

What i did to get to the same results without a cookie is to send a parameter to my login page with the unique id of the “temporary” thing that i created.

Step 1 → I create my temporary thing with the values that i will assign to my users as soon as the sign up
Step 2 → I go to my signup/login page with a parameter that stores my temporary thing’s unique id

Then in my signup workflow, when the user click on register, i fill each field of my user with the value stored in my temporary thing by doing a search of temporary things which unique id match the parameter value in url.


Then when it is done, the next step is to delete the temporary thing by doing the same search of temporary thing that match the parameter in the url.

Hope this helps.

Fabrice

Thanks Fabrice (@fmuhirwa ). This would work I guess, but it would only work for normal sign-ups, right?

Problem is the case of oauth based third party sign-ups like Google sign in or FB sign ins. In those cases the page gets refreshed.

Looks awesome tho I can’t achieve this by following your steps :sweat_smile:

Basically want to save data for products as wish-list, orders, etc… Pretty simple front end but much more complex back end.

Probably need to focus on the “temporary inquiry” set up first and how to have custom state on yes again for the wish-list :heart:

Edit: OK cookies work and restoring data as well. Just need to tweak it a bit now!

Someone (@berksngn1 ) just liked this post for the first time in a while, so I’d point out that, if I were doing this today, I’d use my Floppy plugin to do this in a more Bubble-friendly way.

(Also, in today’s browser landscape, this is a task better suited to sessionStorage or localStorage, which is primarily [but not exclusively] what Floppy provides. sessionStorage, for example survives a page refresh in the same window – as in my example where I needed to recover some Thing after logging in with Facebook (and then, when the session closes, that stored value just magically goes away). While remembering this with a cookie is OK - and still works - in today’s world we would just put that value in the browser’s own storage and read it back on page refresh.)

2 Likes

You are a legend, @keith

1 Like

thanks a lot @keith !

1 Like