In this chapter, we're going to implement widget loading. So in order for our customer to use the widget, we're going to have to confirm a couple of steps have verified. These steps will be loading the initial organization ID and then verifying that that organization is real. After that we're going to have to load the contact session ID and verify if that contact session ID is active. So that's what we're going to have to do in this chapter.
So we'll learn how we're going to initialize this widget to allow the user to even begin making any actions. In order to do this, we're going to have to implement the error screen in case something goes wrong, like a missing organization ID or an invalid organization ID as well as the convex functions to validate the organization and to validate the contact session. So let's go ahead and start with a very simple add error screen task. So in order to do that we go inside of our widget. We go inside of modules, widget, atoms.
Inside of the widget atoms, let's go ahead and just add a very simple error. Well actually we don't need a comment for this it's pretty self-explanatory error message atom. So in here I'm gonna go ahead and define this atom as a string or null and by default it's going to be null just like this. So now let's go ahead inside of UI and inside of screens and let's go ahead and let's create widget error screen dot tsx. Now in here what's important is to mark it as use client.
Let's import use atom value from Yodai. Let's go ahead and import our alert triangle icon. Let's import our error message atom from modules widget atoms widget atoms which we just created and let's import our widget header from modules widget ui components widget header. Let's export const widget error screen here And the first thing we're going to do is we're going to grab the error message. And we can grab it very easily by combining the use atom value and error message atom.
And now In here let's go ahead and return empty fragment. Let's add the widget header like this and what we can do is we can just copy the one from the out screen for example. So this just copy it, add it here like that and then in the div here let's go ahead and give it a class name flex flex1 flex column items center justify center gap y4 and padding 4. Let's add the alert triangle icon. Let's give it a class name, text muted foreground.
In fact we can give it to the entire parent div here. And below the icon add a paragraph in which you're going to render the error message or invalid configuration by default. And let's give this a text small so now let's go ahead inside of our widget view component where we initialize our screen components and let's replace the error with widget error screen. Just like that. Make sure you have added the import and I'm just gonna go ahead and copy this.
Once you've done that go inside of your widget atoms and change this to error Go ahead and run turbo dev and make sure you are on localhost 3001 and you should see invalid configuration here and if you go ahead and change this to you will get that text written here. Perfect. Now let's bring this back to auth to make sure it's still working. So just ensure that inside of your widget view which you can locate inside of apps widget modules views widget view you have added a new widget out screen. My apologies widget error screen.
Excellent. So that was the first step. That was an easy win. And now what I want us to do is I want us to implement these convex functions. Organizations get one and contact sessions dot validate.
So let's actually go ahead and let's do the contact sessions first. So I'm going to go inside of my packages, backend, convex. And now in here, we're going to go inside of public contact sessions, and we're going to export const validate mutation. Let's add arguments, contact session ID to be v.id contact sessions. Let's add a handler with context and arguments here.
And let's go ahead and check if contact session exists by doing await context database get arguments contact session ID. So if there is no contact session in the first place, we're gonna go ahead and return an object with a valid false reason contact session not found. Now let's check if contact session expires at is smaller than, I mean less than date.now. Make sure to use contactSession, this variable that we have. And return the same thing with a different message, contact session expired.
Otherwise, return valid true and the contact session. Just like that, perfect. So that is our mutation to validate the contact using the contact session ID. Now what we have to do is we have to find a way to validate the organizations. So let's go ahead and do the following.
Instead of public here, create organizations.ts. And in here, let's import V from convex values. And instead of mutation, we're going to have to import an action so what is the difference between action and mutation well I think it might be a good idea to try and do mutation first you're going to see what happens so let's export const validate here action arguments my apologies I just told we're going to use mutation. So mutation, arguments, organization ID, string, handler, asynchronous function with context and arguments. And inside of here we are going to validate our organization.
The only way we can do that is by pinging a clerk. So what we're going to do is we're going to import createClerkClient from clerkBackend. The only issue is we don't have clerk backend installed. So let's go ahead and let's do pnpm f backend add clerk backend because that's where we have to add it into our backend package and once you've added it you will see a package json modified inside of your package's backend so 2.6.1 is my version And now let's go ahead and let's define the client. Const clerkClient will be createClerkClient, secretKey will be process.environment.clerkSecretKey.
Now the only issue here is that we don't have the clerk secret key added here. So let's go inside of dot environment dot local and let's do clerk secret key here. And we can actually find this secret key inside of our apps web. So inside of here environment local you should have clerk secret key. So go ahead and copy it.
If for whatever reason you cannot find it You can go to your clerk dashboard, into your echo project here, and you can find your API keys here in the configuration. There we go, clerk secret key. So basically just find your clerk secret key and paste it here. And also what I would suggest you do is go inside of your convex dashboard here, go inside of your echo tutorial settings, environment variables, add and add that here as well. Just like that.
Perfect. Now we can go back inside of your organizations here. Now that you have the clerk client and you can also do this here just in case. Let's go ahead and open try and catch block here. Await clerkClient.organizations.getOrganization Organization ID will be arguments organization ID.
Return valid true Like that. And in here return valid, false, reason, organization not found. Like that. And now let's go ahead and let's do TurboDev and let's focus on the backend to see if this will work or not. So I think the functions might actually be completely ready.
That's perfectly fine. But this actually won't work until you try and fire it. So let's test that out by going inside of convex here and going to have your functions and find public organizations validate click run function and inside of organization ID go ahead and pass one two three and click run mutation. Oh looks like it does work Maybe I'm doing something incorrectly here or maybe it works because we're using the convex dashboard. I'm gonna show you what I was trying to demonstrate here.
So Convex has actions which are used to call third-party services, such as Stripe, OpenAI, things like that. So basically, whenever you are doing some kind of fetch to a third-party service, you should be doing action instead of mutation, because mutation doesn't have access to fetch. So what I think happened here might be convex error. So let me go ahead and at least try and log the error here, so I can see if I'm correct or not. And maybe in the logs here.
Can I catch that error? Okay. Never mind. How about we just add it to our project. Let's keep it as a mutation because I am interested now.
Maybe I'm teaching you incorrectly so I want to make sure to improve on that. We can replace this with an empty underscore to indicate that this will not be used. Alright so now that we have added the other two things that we wanted, convex functions, let's go ahead and let's add the loading screen. Let's go instead of apps, widget, Modules, UI, Screens, and you can copy the error screen and paste it here and rename it to Widget Loading Screen. Change this to Widget Loading Screen.
Just like that. Now, The widget loading screen will be a little bit different. The first thing we're going to have here is a prop, organization ID, and it's going to be either a string or null. And go ahead and pass the organization ID here like that and now when you have the widget loading screen go back inside of your widget view component where you added the widget error screen and add it to the loading and pass the organization ID here. And technically it's also kind of optional here.
So that's important. If you want to, you can put loading in the first item here. It doesn't matter. The order doesn't matter. It's just an object, but just like mentally to help you loading will be the first route that's actually loading here.
So for now, let's go ahead and change this to be loading like that. And you can remove the, you can use the loader icon from Lucid React and you can give it class name animate spin like that and remove the alert triangle icon And then go inside of your widget atoms and change the default screen to be loading. Make sure you have widget running on localhost 3001. And there we go. Loading is the first screen that appears.
So now what we have to do is we have to do a series of steps that will help us Ensure that the widget is ready and functional for the user to use it. So let's go back instead of the widget loading screen. And in here let's create a type Init step. Let's make it either Storage or Organization or Session or Settings or WAPI and finally Done. And now let's go ahead and add some states here.
So I'm going to remove... Okay, this is what I'm going to do. I'm just going to switch the useAtom value to be useSetAtom from Yotai. Like that. And this will then be set error message, like this.
And then in here, I'm gonna do step, set step, use state, which is a type of init step. And the first step will actually be organization. Like that. So let me just remove this. We actually don't need storage.
It's just going to be organization and import use state from react like that let's also add session valid and set session valid here to be use state and by default let's go ahead and let's make it false. Now let's quickly go back instead of our widget atoms and just as we've added the error message atom let's do export const loading message atom, atom, null by default, string null. Like that. And then we're going to go ahead and do loading message, use atom value, loading message Adam just like that. All right.
So now let's just modify this slightly. So instead of this it's going to be either loading message or loading like that. So now it should just be loading. Perfect. And now let's go ahead and let's use use effect to try and validate the organization.
So I'm going to open useEffect here from react, make sure you import it. And just above this I'm going to say validate organization and I'm going to do use mutation here from convex react. And I'm going to pass in the API from workspace back and generated API dot public dot organizations validate like that. And what I'm going to do now is I'm going to check if this is the step it needs to be so if step is not organization we are not going to do anything in this function so we immediately break it and then what I'm going to do is I'm going to check if there is no organization id present so if what this was passed isn't present for whatever reason set error message immediately to be organization ID is required and set screen which I have to add so let's just do const set screen here use set atom screen atom make sure you have imported screen atom from modules widget atoms widget atoms and set screen to be error like that so this is so let me just check And here I have to add step and I have to add organization ID.
Yeah, linting is not perfect in this project. So make sure you have added step and organization ID here. And I think that technically you also have to add set error message and set screen. Because these are not... Usually you don't have to add setters but since this is from a third-party package I'm not sure.
So right now let's go ahead and let's refresh you will see the error organization ID is required if you haven't passed anything. But if you try and add organization ID and make it 1, 2, 3 you will just be stuck in the loading phase. Right? Perfect. And what you can actually do is you can do set loading message.
You also need that here. So set loading message. Loading message Adam. Set loading message to be loading organization ID. Like that.
And then you can see for the first step you have loading organization ID. So if you accidentally don't pass it you get an error organization ID is required. That's how we are going to enforce that or maybe you can do just loading organization first and then let's go ahead and do the following set loading message verifying organization like that let me add three dots here too and then what we're going to do here is basically the following we're going to use the validate organization here So validate organization and passing the organization ID like that. And let's go ahead and and let's check if result as valid question mark boolean question mark dot valid okay I think I did this incorrectly. Let me just check.
Actually, I think you should be able to just do if result that valid. Yeah, I think you can just do that. Set organization. Let's go ahead and let's add this setter, const setOrganizationId, use setAtom, organization id atom. So the reason we are setting the organization id again is because the only way we can currently pass it around is through props.
But if we just once set it in the atom we can access it everywhere that's why I want to do that here. So technically you know we could also do that here the moment we pass that So set organization ID and let's go ahead and let's make it. Okay, actually, yeah, we can do it inside of here when we know it's valid. So set organization ID, organization ID, like that. Perfect.
And actually this can call an early return if there is an organization ID we set the screen to error and no point in going further and that way this function is safe. Perfect. So if result is valid then we set the organization ID and we can also set the step here. Well, we'll do that in a second, but let's go ahead and first do else. Let's go ahead and do set error message.
And let's go ahead and use the result.reason or invalid configuration and setScreen to be error like that so there we go You can now see something here is going on. And let me just go ahead and add one more thing. So I also want to add a catch method here just in case. So if it fails for some other reason, we're also gonna do unable to verify organization and set screen to be error. Like that, perfect.
Okay, So now let's see what's going on by focusing on the back end here. Okay. So it says organization not found. Let me just see. Inside of validate organization.
I think that's actually all correct. I was convinced that I need to use the action for this but looks like I don't but I will show you how you can do it with action as well because this is true organization not found let's the organization not valid and refresh Verifying organization, it still says organization not found. Just a second. Okay, let's try again. There we go.
So I guess this needed to restart. Perfect. So organization not valid if you pass just a random one, two, three number. But if I go inside of my organizations here and just copy the id of one of them in my clerk dashboard so I have a valid organization id let's check that ah see organization not valid well I think that now we are encountering the problem that I was telling you about. I think that this needs to be an action.
So let me just go ahead and check just in case. Console log arguments organization ID. Okay, let's wait for the this to deploy and then let's try. Okay, let's go inside of logs here. You can see that the organization ID is fine.
So I think that this should be working just fine, but it's not throwing errors. Let's try and replace this with action as intended and then we just have to modify instead of use mutation to use action here from convex react and remove use mutation. Let's see if this changes now. Let's first wait for all of the functions to deploy. Let's refresh.
Okay, and yes, make sure you're using the proper ID. So always double check that. I have a bunch of localhost opens. So yeah, make sure you are using just a single one. And there we go.
Now it's staying at verifying organization. So it looks like it needs to be an action. Let me try one more time. I'm changing this to mutation and I'm changing use action to use mutation. I have the proper organization ID and you can see it says organization not valid, but that's not correct.
This is a valid organization, but you need to use use action and inside of your organizations you need to use action instead of mutation. That is because mutation environment doesn't have access to fetch and Clerk client in the background uses fetch to their API to get the organization. The only thing I'm kind of concerned about is that we're not getting any errors about that because usually I get alerted whenever I try to use fetch within mutation. So let me just try moving this outside like this and let's always return valid true let's do const organization like that maybe so if organization is present, valid true else return valid false reason organization not valid. Will this maybe trigger it or not?
Okay, so it still says that everything is fine. Use mutation. Let me refresh. Now it says unable to verify organization and we have an error and it says, now it's behaving properly, okay? And I think that we finally got the error that I wanted to get this entire time.
Basically, this is the type of error that you would get. The problem was we encapsulated it within try-catch, right? So that was the problem. But you can see that you get errors if you try to use a fetch or any SDK which wraps fetch instead of mutation. So that's why we're using action.
So my apologies for doing all this just to prove a point. But I want you to understand that I don't want you to just blindly follow what I do. That's why I want you to learn convex. Whenever you are calling a third-party SDK you need to use action instead of mutation and you need to use use action on the front end. Let's refresh again.
There we go. Now it works. Right? No errors. Perfect.
So make sure you're using action in the organizations.ds file and use action on the front end, beautiful. And now that we have done that, if we succeed, let's go ahead and let's do set step session like that. And that's gonna be another use effect here. So I'm kind of creating sequential use effects here. And also we should add all of these.
So we have step, we have organization ID, set error message, set screen. We should also do Set organization ID. We should do set step. Basically everything that we are using here. And let's also add validate organization here like that.
I think the only thing that's missing is loading message set loading message. Add that to the reason you need to add this is because during build you will most likely get errors. It will fail because it's missing. It won't be able to lint that. All right.
So that was step one, right? Let's go ahead and mark it as step one, validate organization. And now we're going into step two, validate session if it exists. Like that. So what we need to do here is we need to add validate contact session and this will be use mutation.
From api.public contactSessions.validate. So why is this useMutation? Well, because our validate contactSession inside of packages backend public contactSessions doesn't use any third-party API. This just uses the convex database. That's it.
That's why we can put it inside of mutation. That's why we do that. All right, now back instead of the widget loading screen here, the first thing we do is the protection. If this step is not about the session, let's look into an early return. There's nothing for us to do inside of here.
And now what we have to do is we have to add the contact session id atom. So let's go inside of the widget atoms here and let's go ahead and let's add organization scoped contact session atom and I'm going to explain what this is in a second so const contact session ID is going to be the following it actually can't be just this it needs to be export const contact session id atom family and then use atom family which you have to import from yotai utils so import atom family from yotai utils and also import atom with storage so we can immediately store it in the local storage So atom with family will accept an organization id which is a type of string and it's required and it's going to return atom with storage like this and inside of here what we're going to do is we're going to use our constants which is the contact session key so let's go ahead and open our backticks here so contact session key like that And then go ahead and add another underscore here. Organization ID like that. And then Let's go ahead and add a third argument.
Is this third argument? Let me just check. Here. There we go. Like that.
Add null. So what just happened here? What is this? Well, here's the problem. You can just use atom with storage like a normal constant contact session id atom with storage some key.
No, you can do that, right? But the problem is we need to store different keys for different organization ids. We can't do an object and then org id inside and then the session because local storage values have limits. We are better off using index DB for that. But we can still simply create an atom family to derive atom with storage and a specific organization ID.
So now our local storage will be full of the following examples. We're gonna have echo contact session and then organization id and then that's going to hold your user id inside. That's how it's going to look like. That's why we created the contact session id atom family. And now we can go back instead of the widget loading screen and here at the top we can extract that atom So I'm going to do const contact session ID that we need is use atom value.
Contact session ID Adam family and passing the organization ID. And organization is available from here like that. And this is use Adam value like this. And let me just see. So yeah, this is a little bit tricky because we are trying to protect ourselves against organization id not being null So we can use like this little trick, I guess, because even if this happens, if it happens that this is empty, we are simply going to show the error screen.
The problem is we need to initialize the hook above that step, right? So let's just leave it like this for now. And basically now we have the contact session ID extracted from that specific local storage value with combination of the organization id. So each organization has its own key in the local storage. And now what we're going to do inside of the step two, which is to validate the contact session, we're first going to check if we don't have the contact session id which is perfectly fine we're just going to set the is session valid to false this is our internal set state here because that's completely fine the user doesn't have to be authenticated right the session can expire And inside of here you would return early and simply set step to be done because we are finished in this case.
There's nothing more for us to do. And Now let's do set loading message here, validating session. And we can actually do, let's be consistent here. So this will be finding organization ID. Let's now do the same thing here.
Finding contact session ID because that's what we're doing in this step. You can remove this error loading messages later but I just want us to know what we're doing at each step. Now we're validating it. So let's go ahead and do validate contact session, contact session ID, contact session ID, like that. Then result.
And let's go ahead and do set session valid will be result.valid. And I think that there is a type error here that we are not seeing. ContactSessionId requires id contactSessions but this looks like it's a type of never. So let's just go ahead and give it a type of as ID contact sessions and import ID from workspace backend generated data model. So the reason I'm fixing this is because we're going to get errors in build.
That's why we need to take care of these things before they happen. And now let's go ahead and let's do set step here to be done. Because we're done at this point. And let's add a catch. Just in case this fails for some unknown reason, let's do set session valid to false and set step here to be settings.
Like that. Perfect. And now let's go ahead and let's add all the dependency arrays that we need. So step contact session ID, validate contact session and set loading message. Like that, perfect.
So let's see what's going on now. So you can see now it's finding contact session ID and it can never find the contact session ID. So yours should be stuck in finding contact session ID as well because after that what we do is we do an early return. We couldn't find the contact session ID. So in your application here, if you go, and inside of local storage, now you can see that I have a key, ignore that.
That's because of my testing of this app, right? But yours is empty. So it cannot find the contact session id so after it determines that it cannot find the contact session id you have to define what to do and my apologies not settings done in here in the cache method. Done. And now let's go ahead and let's do a use effect here.
Another one. If step is not done, early return. Otherwise, let's check if we have a valid session by checking if we have the contact session ID and if the session is valid and then set screen dynamically So if has valid session is going to be a selection, otherwise it's going to be out. And let's go ahead and let's add all the necessary dependency array items. Step, contact ID, session valid and set screen because that's all that we are using here.
And if you refresh now, you should be redirected to the out screen, right? So every single time that you go into localhost 3001 with valid organization ID in the URL without anything in your local storage, you will be redirected here. But if you accidentally forget your organization ID, it won't even let you create an account because We have no idea why you came here. Or if you accidentally enter the wrong organization, you will be unable to verify the organization. Perfect.
So only if you have the correct organization ID, are you able to proceed further. So now let's go ahead and do the following. Let's go inside of widget ALF screen right here and now instead of the widget ALF screen let's go ahead and let's change let's save the contact session ID. So this is what I'm going to do now. I'm going to add const set contact session ID to be used set Adam from Yotai, make sure you import it.
Contact session ID Adam family and pass in the organization ID which we currently don't have so let's obtain it organization ID use Adam value. Organization ID Adam At this point we should already have the OrganizationId because of our loading screen, right? Because we would show an error. But we are still going to have to because of types, pass in OrganizationId or empty string, but only because of types. And now that we have that, let's go ahead and go instead of set contact session ID.
And let's simply set it here. Contact session ID. Let me just say so this is okay. It has a specific type. So let me just see how can we improve this instead of public contact sessions create.
This is a type of ID contact sessions and we return that back. Wait, create contact session, but in here it's described as this. Okay. So can I do as string here? Or as ID?
Contact sessions. Let me just check. Okay. Why is this throwing a type error? So the first thing I'm actually going to do is I'm going to go back inside of my widget atoms.
And how about we give this atom family and atom with storage here a proper type. So you can do that by going here Adam with storage before the parentheses and open pointy brackets and give it a type of id Open pointy brackets again, contact sessions or null. Like that. So let me try and, I'm going to try and make this function more readable for you because it's very hard to understand what's going on here. Is this any easier?
If this is any easier for you I don't know. Basically the atom family function needs to return atom with storage. And that atom with storage can be a type of ID from workspace back and generated data model of contact sessions, or it can be empty, stored under this key and with the default value of null. So I think that technically if you add like one, two, three, this should be an error because it's not assignable to type contact sessions. So that's how you know if you did it correctly.
And now go back instead of the widget out screen and you can see that now this doesn't give us any errors but if I tried one two three this gives us an error because it's not a type of id contact sessions but this type and this type seem to be compatible even though this one looks different but I guess that is the exact same thing. I don't know if you're still having problems you can add as ID contact sessions. But for me it works this way. So now let's try this. Let's refresh.
Let's try test and test example.com and let's click continue. And there we go. You can see now that we have echo contact session underscore and then our organization ID and then our value. So if I try refreshing now there we go I'm redirected to to do selection. Perfect And let me just double check something inside of my widget loading screen here.
So I'm just not sure what is the type of my contact session ID here. It's perfect. It is exactly what I want it to be. So down here in the validating session, it's the correct type. And I don't think I no longer need this now.
There we go. I can even use the shorthand operator like this and maybe even make it like that. Perfect. So now I can remove the ID import. That is exactly what I wanted us to do.
Perfect. So now since you have a valid organization ID token stored, you will always be redirected to selection, but only if this contact is valid. Because remember, this contact can become expired if 24 hours pass, because inside of the contact session create, we expire it 24 hours from now. So we're later going to create additional methods which will refresh that token. But if you want to what you can do is you can try just adding maybe 5000 that's five seconds so let's remove this token from here let's refresh and let's do five seconds five seconds at example.com.
Let's click continue. Let's quickly refresh. Now it works. Let's wait, let's wait, let's wait. And there we go.
You can see that even though I have the session ID now, 5 seconds has passed. So I have to log in again. That is how our out system works. So make sure to bring this back to 24 hours, but you can see that it works. So even though we have a completely valid session ID instead of our local host, since it's expired, we are no longer treating it seriously.
Right? And even now, after you change this to 24 hours, that's still an expired session. We no longer need it. So you're going to have to create a new one. Oops, I reversed the two, didn't I?
And now this was updated and let's refresh. There we go. And now we are redirected to selection because we have a valid account. So that is exactly what I wanted us to do. Amazing, amazing job.
So now we have a working loading screen, which is used to find the organization ID, verify the organization using Clerk, find the contact session ID, which is dynamically generated using Adam family and the correct organization ID and then verify that session. So we achieved exactly what we planned out to do in what I believe was 10 widget session. This is exactly what we're doing now, right? So we are validating the session using that stored contact session ID in the local storage except the contact session ID is stored along with the organization key. So we know exactly for what organization it was made.
And we are also checking expiry here. Amazing, amazing job. I believe that marks the end of this chapter. We of course have to review our changes because we did a lot of them but I am super happy with how this turned out. It was a complex one, but I think you learned a lot and especially of how we practically implemented our own out here.
I think that's always interesting. It's obviously not super, super complex, But it's a good lesson in making these types of things, right? Because this is supposed to be very very low friction. So 12 widget loading. Let's add all changes.
12 widget loading. Let's go ahead and let's commit. Let me open a new branch 12 widget loading. Let's publish the branch and then let's go ahead and let's review our changes. And here we have the summary by CodeRabbit.
We introduced comprehensive loading and error screens with dynamic messages for improved user feedback during widget initialization. We added validation for organization and contact session IDs, ensuring only valid sessions proceed. Exactly. Widget now reacts to organization changes and manages session state more robustly. Exactly what we did and we also added back in support for organization and contact session validation as well as dependency to include clerk.
So in here of course file by file change summary but what I think is more interesting is the diagram here. Let's take a look at it. So the user loads the widget with specific organization ID. The widget loading screen is the first screen that appears because our atom, screen atom, is set to loading. That means that the first use effect that fires is a validate organization ID.
The convex then runs the get organization, clerk, let me just see, I'm not sure what's behind this, and basically clerk responds back with either organization found or not found and depending on that we return back valid, false, false or true. So if the organization is invalid we set the error message and set the screen to error because you cannot proceed further. You are either missing the organization ID or the organization ID belongs to some organization we don't have or it's completely wrong. But if the organization is valid we go into step 2. We extract the contact session ID from the local storage and we validate the compact session ID.
We do the same logic as here just using our database and return true or false and if the session is valid in here I think it kind of understood incorrectly We never actually set anything to error. We don't even set the error message. Even if this call fails, we're going to set the screen to off, right? So if the session is valid, we set the screen to selection. Right.
So I think it got confused here, probably because we're using inside of here, ValidateContactSession, we're using catch. So maybe it thinks that this set session valid false somehow determines the error, but yes, that's not what we do. So you can see that when we validate the contact session, If result is valid, we simply set the session valid to true and we set the step to done. And then in here, if we have a valid session, we do selection. Otherwise we do out.
So this is where he got out from and they probably generated some incorrect logic but we know that's not what happens. But still super impressive how it managed to explain this to us. And we do have some comments and I knew it was going to comment this. Yes I hate how we handle organization IDs right now. I hate how we have to do this for empty because technically we know organization ID is guaranteed at that point.
So I'm going to look into how I can help that. Something like this, an early return could help, but you can't do it exactly like this because useSetAtom is a hook and you can't do conditional hook rendering. So this will not work. This would actually throw an error, but I'm not sure CodeRabbit knows that this is a hook. In here it recommends checking for this.
Well, okay, yeah, sure. And in here it recommends validating the organization ID. Well, we are pinging clerks API, not ours, so it's fine. And in here 2.6.1 is probably like the most up-to-date version. So his registry is probably not refreshed yet, but 2.6.1 is perfectly valid version.
Great, so yeah, I really dislike this. I fully agree. I don't like this organization ID hack that we're doing. So let's just go ahead and merge this for now. We've done enough for one chapter.
Amazing, amazing job. And now let's go back inside of our main and let's click the synchronize changes button here. And after they have synchronized, let's go inside of Graph to confirm widget loading and then we merged it. Amazing, amazing job. That wraps up this chapter and see you in the next one.