We Need to Taco 'Bout Your Choices
with Emma Bostian
In this episode, Emma Bostian and Jason will build an app together to settle their taco differences with your help. Make sure to mark your calendar and help settle this debate! 🌮🌮🌮
Resources & Links
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
JASON LENGSTORF: Hello, everyone. And, welcome to another episode of Learn With Jason. Today, we're going to argue about taco with Emma Bostian. Emma, thank you so much for hanging out with us today.
EMMA BOSTIAN: You're so welcome. Please excuse the fact that I'm eating pizza. Why am I not eating tacos? That's because they don't have Taco Bell in this future and I refuse to eat anything else.
JASON LENGSTORF: It's going to be so easy to win this debate because it's so obviously wrong that Taco Bell is -- more or less -- better than any other taco.
I'm super-excited to have you on the show. I'm happy to have you back. For those of you who don't know, Emma and I used to work together, but way back in the day, we both worked at IBM.
Do you want to give a background and a story?
EMMA BOSTIAN: Storytime!
(Laughter). Jason believed in me when I was a baby developer. I started at IBM in 2015. I remember, you believed in my mentorship. I was starting to do an executive mentorship program at IBM and Jason was one of four people who believed in me. I was like, how do you get to speak at conferences, you get to travel the world. I want to do that. You inspired me. I was lived in Germany. Now I'm in Sweden. New country, new me.
JASON LENGSTORF: No kidding.
EMMA BOSTIAN: The sparks notes edition of how we met.
JASON LENGSTORF: You're at Spotify now. Is this your second or third foreign country that you've lived in now?
EMMA BOSTIAN: Well, if you count study abroad, it would be three, because I studied in London, loved in Germany for three years and lived in Sweden for four and a half months.
JASON LENGSTORF: Of the three, which is your favorite?
EMMA BOSTIAN: Sweden. Don't get me wrong, I love Germany. I didn't love the city I was in. It was kind of boring, no offense.
JASON LENGSTORF: I have found that there are cities that feel like home and there's city that feel like the kind of place you don't -- you never really settle it. I felt that way about Berlin. I thought it was going to be home. It just did not feel that way for me.
Um, but --
EMMA BOSTIAN: Yeah. (Laughter).
JASON LENGSTORF: La Neta, is that (Indiscernible).
EMMA BOSTIAN: My coworkers in the chat, quite a few. They're asking you why didn't you order from La Neta. You would have won by default and I am stubborn. (Laughter).
JASON LENGSTORF: Okay. So, this all kind of stemmed from an argument where you -- you posted, on Twitter, that Taco Bell was the best taco and I couldn't let that stand. So, we argued until we decided that this had to actually get settled. So today, we're going to build an app to settle this debate. This is going to rely on audience participation. We need votes. So, the plan that we have is, we're going to build a polling app. And this is actually going to be kind of inspired by an app that Sarah built called "is this a sandwich." It's a click-through poll. We're going to aggregate these scores and show a winner. So, this is our plan; neither one of us does a ton with data so we may or may not get there. We're going to do our best.
EMMA BOSTIAN: We might get to the Create React part and say, screw it, we're done. (Laughter).
JASON LENGSTORF: More or less a rap battle where we'll talk about tacos at each other.
On that note, let's twitch over to pair programming mode. If you're not already following Emma, you should go and do that. It is a good time. Emma is full of good insights and good jokes and bad takes on tacos.
EMMA BOSTIAN: And if you're not already following Jason, make sure to hit that "subscribe" button.
JASON LENGSTORF: Ring that bell!
(Laughter). Let's also make sure that we give a shout-out to our sponsors. So, we have live captioning being done by Vanessa right now. That is over at lwj.dev/live. That's made possible by Netlify, Fauna, and Auth0. I very much appreciate.
We're going to use Hasura today to do our voting.
If we want to get started, here's the way I see this going...you're a React pro. So you're going to lead --
EMMA BOSTIAN: Oh, that's dangerous, but let's do it!
JASON LENGSTORF: We need to get some images. The vision that we have is you're going to look at a taco and answer, is this a taco? Is this the best taco, yes or no? This is not going to be a particularly scientifically accurate show. You're going to see if you want to troll me or Emma. We're going to go to Unsplash and grab taco images.
EMMA BOSTIAN: This is not safe for work. (Laughter).
JASON LENGSTORF: Okay. So, here's what we've got, we've got -- here's our taco photos. So, we got some Taco Bell photos. I couldn't find a "here's a Taco Bell press ones." Please don't sue me, Taco Bell. Emma's all for it. This is 50% good press.
Now -- so, we've got these four Taco Bell images and we're going to grab four non-Taco Bell images. Let's see -- let's look at -- how about this one and -- we're using Unsplash because, again, we want to make sure -- first of all, that the creators get credit. And second, I don't want this episode to get taken down later.
EMMA BOSTIAN: Al Pastor is the best.
JASON LENGSTORF: The effort is there.
EMMA BOSTIAN: You know the first time I had an Al Pastor taco, we rolled up to a gas station in the middle of Austin, off a highway. And it was -- it was not as good at Taco Bell, but I will give it a 9 out of 10. (Laughter).
JASON LENGSTORF: So, I'm going to download these and we are going to, uh -- we're going to work on these. So, just a quick shout-out to the creators. That is very much appreciated. I'm going to put them into this -- not that. This directory, here. Oh, I really don't like that it's -- oh, man. Why is that not organized? Be organized.
EMMA BOSTIAN: I hate that so much.
JASON LENGSTORF: It's killing me. (Laughter). So, we've got these four images. I feel like we probably want to process these a little bit because they are very, very large. I'm going to use Squoosh.App. I think it was the Chrome Dev Rel team. It looks like it got an update. That's really nice-looking.
EMMA BOSTIAN: Look at that!
JASON LENGSTORF: That's beautiful. Okay. So what we should be able to do with this, then, is any one of these, we can drag in and we can resize it. Let's make it 800 pixels wide. And then I'm going to compress it. And, look, we're going to get an 88% savings on these files, which is unbelievable. So, there's one. Let's get the next one --
EMMA BOSTIAN: I always do this through Figma and it's such a pain in the butt.
JASON LENGSTORF: It really is. It's really, really nice to be able to come here for this kind of stuff. Um, so, yeah, another one. We're going to get an 88% savings. And, like, you can see, we resized it so you can see the quality difference here. But if we go down to the size it's going to be at, like 800 pixels, you can barely see the difference. This is 12% of the size it is supposed to be. That's a really important -- did I do the Fidel Fernando one twice? Let it be 75 again. All right. Is it just -- it's continually doing the same one. I'm going to reload, because I think maybe I -- Fidel Fernando one is in there.
EMMA BOSTIAN: That's the hardest thing is keeping your assets organized, I swear.
JASON LENGSTORF: It really is. Like...you good? 98% savings on this one holy buckets!
EMMA BOSTIAN: You know that show, "Extreme Cheapskates"? That's us. I feel like we need copyright music in the background.
JASON LENGSTORF: The problem with the music is when we're talking and there's music and people play sound effects, the show can get really overstimulating.
EMMA BOSTIAN: It's a shame I don't have my trumpet. We could have played. (Laughter). (Music playing in the background).
JASON LENGSTORF: 99% savings on that one. Now that we've got these, I'm going to take these giant ones out, these giant files. And instead, we're going to drop these ones back in. And I think this is the one that doesn't have -- yep. Okay. So, now we've got these images and they're significantly smaller. So, this is going to be way easier to manage. And so, with that, we can start a new site. So, you were thinking Create React App?
EMMA BOSTIAN: Yes, please.
JASON LENGSTORF: So, let's -- NPX -- wait. Is it NPM Create?
EMMA BOSTIAN: We're going to find out.
JASON LENGSTORF: Let's see what happens. And we're going to call this one "We Need to Taco 'Bout Your Choices." This is the best repo I'll ever create. And it's not a monorepo so I never have to type it again. It's just here to make me smile. Okay. So, we are installing everything that we need. And...hopefully this won't take very long. I got the good internet so that I didn't have to, you know, really spend any time on this.
EMMA BOSTIAN: I swear my cats are going to be unruly during this and it's just because they know when mom is livestreaming. (Laughter).
JASON LENGSTORF: They lie and wait.
EMMA BOSTIAN: I feel like we should make awkward elevator conversation. Do you have any fun questions?
JASON LENGSTORF: Yeah, where are you at, chat?
EMMA BOSTIAN: Yeah, chat. Ask us anything. Oh, God.
JASON LENGSTORF: You're going to regret that. (Laughter).
EMMA BOSTIAN: How's the weather? Dark!
JASON LENGSTORF: How much sunshine do you have?
EMMA BOSTIAN: I don't want to talk about it. Corn or flour tortilla. It has to be a flour if it's a quesadilla. There's nothing better than a potato, egg and cheese.
JASON LENGSTORF: Maybe only the folks that have been to Austin can appreciate, breakfast tacos, outside of Texas, are just garbage. I got a --
EMMA BOSTIAN: We can agree on that.
JASON LENGSTORF: I got a breakfast taco in Portland and I was so sad.
For anyone not familiar with tacos, it's a Mexican dish, especially in Europe, they're hard to find. A taco is not necessarily the hard shell. Traditionally, they don't have a hard shell. But, you can take a tortilla that's usually about yay big and you would serve that with meat or grilled toppings. Al Pastor, they do beef?
EMMA BOSTIAN: It's pork and it's marinated.
JASON LENGSTORF: They'll put fresh onions, cilantro and then hard-shell tacos are flash-fried tortillas and Americans really got into that because we like hand-held food.
EMMA BOSTIAN: There is a Gordita Crunch and you wrap it in a soft layer of cheese. That is why I love Taco Bell.
JASON LENGSTORF: The chat is very into this. (Laughter). I think you're all wrong, but the -- yeah. So, the -- (Laughter). Oh, chat, you're killing me. You're killing me right now.
EMMA BOSTIAN: (Away from mic).
JASON LENGSTORF: Al Pastor is like Chili's. You know the cone of meat you would see in a kabob shop? It's that. It's a vertical rotisserie grill. Yes, I love those. So, now that we've got this, I'm actually --
EMMA BOSTIAN: No! Why -- why is that on the right side? Are you -- are --
JASON LENGSTORF: I'll show you why. Watch. This is why. It's so that when I open this and then I hide my sidebar, my code doesn't jump around. I learned this from Nikki, in the chat, and I know that it's upsetting at first, but it's a game changer.
EMMA BOSTIAN: I cannot proceed with this livestream. (Laughter).
JASON LENGSTORF: People are so upset.
Okay. We're all making choices that some of us don't agree with us today. We'll disagree and commit here, I think.
So, the first thing we need to do is probably just define our list of tacos. Right? So we can start with, like, tacos and that can be an array and then each one is going to need, um -- we can call it, like, source. That will be the path to the image. It's going to need alt text. Do we want to give them names or anything? We should probably give them an ID of some sort.
EMMA BOSTIAN: I don't think they'll need names, because I think we'll run out of names. Like, Saucy Boy 1? Big Boy 2?
JASON LENGSTORF: Okay. So, then if we've got these, I can take all of these images. I'm just going to copy them. And...do you know how static images work in Create React App?
EMMA BOSTIAN: That's a "no" for me. I think you just import them.
JASON LENGSTORF: You just import them?
EMMA BOSTIAN: You import "taco" from the relative path.
JASON LENGSTORF: What?!?
EMMA BOSTIAN: I don't know, I'm not a programmer. (Laughter).
JASON LENGSTORF: We need to call this "images." And I'll put all these photos in here. Is that going to work for me? It's not going to work for me, which means I got to drag them in. That's fine. So, let's take all of these. We'll put them into this images folder. Okay.
EMMA BOSTIAN: Beautiful. Doritos Locos. (Laughter).
JASON LENGSTORF: Oh, that's not going to work.
EMMA BOSTIAN: You can copy the path, though. Look at that!
JASON LENGSTORF: Beautiful. Oh, it's beautiful. Okay. So, we'll be able to go from app into there. And, we can say, uh, "Taco Bell Cheesy Double Decker Taco." Okay. So, that's one. And then, we can duplicate this out. So the next one we need is the cheesy thing, whatever this is. Okay. I'm sorry, these are not going to be the most helpful, um, alt text because I don't want to spend the whole episode trying to configure this object. So, we will do our best. In a production app, we would take more time on this.
EMMA BOSTIAN: They want us to name one "Fabulous Fatty." (Laughter).
JASON LENGSTORF: And, what was the last one? Just "Taco Bell Tacos."
EMMA BOSTIAN: Sounds good. I'm not sure if it'll work like this. I don't know if we need to import them and set the source.
JASON LENGSTORF: Good point.
EMMA BOSTIAN: But, I think that this is worth a shot.
JASON LENGSTORF: Name one "Cheesy Chester."
EMMA BOSTIAN: Like we're naming children.
JASON LENGSTORF: I feel like if we named one "Cheesy Chester," it would get bullied.
EMMA BOSTIAN: Oohhhh.
JASON LENGSTORF: This is going to go poorly, I think.
EMMA BOSTIAN: I wonder if we should validate our assumption before we just keep making --
JASON LENGSTORF: You're probably right. Let's see if we can get these to show on-screen. So...I guess we can just do, like, a...all right. So, let's do, like, a main and then inside, we can do, um, "taco.map." And, why don't we get, like, an image and it'll need a key, which we can use the ID. Not like that, it doesn't. And then, is this going to work? "Taco source"?
EMMA BOSTIAN: I don't know. Are you using (Indiscernible) as your font?
JASON LENGSTORF: Operator Mono. We should see an immediate explosion. No!!!! Wait, what does that mean? Did it not, like -- it didn't print them, even.
EMMA BOSTIAN: It says "map needs a return." A-ha! I did this the other day.
JASON LENGSTORF: Every time. Every single time. Now what?
EMMA BOSTIAN: It's probably not -- yeah.
JASON LENGSTORF: Unexpected token.
EMMA BOSTIAN: I'm surprised that ESLint didn't remove that. You can set it to component or image.
JASON LENGSTORF: So, hold on. If I do "Cheesy Chester" here, we're going to import that from here.
EMMA BOSTIAN: Yeah.
JASON LENGSTORF: Then, is that the source or do I just, like, throw this in as --
EMMA BOSTIAN: No, I think it's your source. I'm pretty sure.
JASON LENGSTORF: Uh-oh.
EMMA BOSTIAN: I like how I told you I was pretty sure and you were like, "I'm going to try it anyway."
JASON LENGSTORF: I like to see what happens. (Laughter). You were absolutely right. It just brought in the path and I was trying to create that path as a component. (Laughter).
EMMA BOSTIAN: People are saying that the images should be in the public folder, as well.
JASON LENGSTORF: Oh, look at this public folder we should have used. Okay. So, wait, then -- so, if that's the case, then we could just do this?
EMMA BOSTIAN: Potentially, yeah. Let's try it.
JASON LENGSTORF: Let's try it with one. Let me take my Cheesy Chester out of here.
EMMA BOSTIAN: I appreciate (Indiscernible) telling you to listen to me.
JASON LENGSTORF: I am listening.
EMMA BOSTIAN: I'm kidding.
JASON LENGSTORF: Why did that just work for all of them, though?
EMMA BOSTIAN: Probably because we moved it into the public folder.
JASON LENGSTORF: Oh, and it's relative. Okay. Okay. So --
EMMA BOSTIAN: That's good.
JASON LENGSTORF: And so the other way we could have done this would have been to, uh, to import those -- to import those images directly and then we could have put them as the source. But this -- this feels more correct, I guess?
EMMA BOSTIAN: Yeah. It's less clutter at the top, definitely.
We got Fidel. Let's drop the public out. Let's get Jordan. I love this relative path. This is new to me.
EMMA BOSTIAN: It's nice, right?
JASON LENGSTORF: Wonderful. Okay. All right. And, last one, Christian.
EMMA BOSTIAN: Ladies! I'm working!
JASON LENGSTORF: Your cats?
EMMA BOSTIAN: Yeah. It's like the Daytona 500 in here. (Laughter).
JASON LENGSTORF: These are not Taco Bell tacos. I'm so sorry for those alt descriptions. I want to make sure we get a chance to finish and we have limited time.
EMMA BOSTIAN: Can you stop! Sorry. (Laughter).
JASON LENGSTORF: So, we have that and then we've got our app CSS. So let's take this app CSS and change -- I don't want it to be 100v height. We'll add padding. That seems a little -- yes. That's a little easier to deal with. Maybe a little bit shorter. Good. Okay. So then we need, like, a container for our images. Um...do you have opinions on this? What's your preferred way, right now?
EMMA BOSTIAN: How are you going to display them? Are you going to display them in a grid or display them one-by-one?
JASON LENGSTORF: Oh, we should display them one-by-one.
EMMA BOSTIAN: Have a hook that says "active taco" and set it to the ID of the current --
JASON LENGSTORF: Okay. So, we can import use state from React and then down here, we'll create active taco -- "set active taco." I'm pretty sure that my favorite -- my favorite variable names are going to come out of this.
EMMA BOSTIAN: Yeah. (Laughter).
JASON LENGSTORF: Also, I'm realizing I made a HUGE mistake by not zero indexing these.
EMMA BOSTIAN: Yeah.
JASON LENGSTORF: But then again, maybe I also just want to --
EMMA BOSTIAN: I mean, yeah, move that up to the top if you're going to get upset about the fact that it starts down there.
JASON LENGSTORF: I'm going to get upset. (Laughter). You know me well. (Laughter).
EMMA BOSTIAN: I mean, I was going to be upset, too, but I wasn't going to cancel the livestream. (Laughter).
JASON LENGSTORF: I was. I was just going to stop. Like, we're done.
EMMA BOSTIAN: Jason Lengstorf canceled due to his choice in indexing. (Laughter).
JASON LENGSTORF: Instead of showing this, we can start by doing, like -- we would need maybe a div with a class of current vote. Class name of, like, current vote, I think. And then inside of this, we need...the image, um, a form, and under the form, we need a, like, "yes/no," and, like a question heading.
EMMA BOSTIAN: You know what I'm thinking? Instead of saving the index as the active taco, let's save the whole index.
JASON LENGSTORF: Okay. We'll go "tacos zero." We'll just start there. Okay. That works. Oh, yeah, we can do "active taco dot." So to start, we can go back to our image, we'll do a source of "active taco.source." "Active taco.alt." We'll do styling relative to this container, I think. So, then, we need "form." And, that form will probably -- nah, we'll just figure that out later. So, to start, we need a question. "Is this the most delicious taco?" And then we need -- okay. Should we do this as a -- as buttons? As a radio?
EMMA BOSTIAN: I think it needs to be a range of, like, 1 to 5, like, a slider.
JASON LENGSTORF: Oooookay.
EMMA BOSTIAN: Because then, we can see which ones have the highest overall rating. Because the thing -- yeah. If you say, "is this the most delicious?" They may not know. They may not have seen the other ones.
JASON LENGSTORF: I get what you're saying. We'll label this with an HTML4 for amount and choose one for not delicious --
EMMA BOSTIAN: Garbage.
JASON LENGSTORF: Choose "1" for garbage. (Laughter).
EMMA BOSTIAN: Choose 5 for (Indiscernible). (Laughter).
JASON LENGSTORF: Well, I don't think anybody's going to give Taco Bell a Michelin star. How about 5 is "I will die for this taco."
EMMA BOSTIAN: Sounds good.
JASON LENGSTORF: Input range --
EMMA BOSTIAN: I would have to look this up.
JASON LENGSTORF: Max -- I just did one of these with -- with Lindsay a little while ago and we styled this up, so I'm trying to remember what she taught me. Min/max and I think the step will default to one. Nope, I broke it already. Yeah, that is great.
EMMA BOSTIAN: Why are there no labels on it, though?
JASON LENGSTORF: What do you mean?
EMMA BOSTIAN: There's no indexing numbers. Is that the default by HTML inputs?
JASON LENGSTORF: Maybe it is. Actually what we should do is do like a const -- tastiness. We'll start at the middle. We'll start at 3 and you can choose which direction you want to go. And so, we will set the value --
EMMA BOSTIAN: Wait, you can set list equal to tick marks as an attribute. Oh, my god, this is so cool. I'm going to send you this in the Zoom chat. This is super-cool. You have a data list element. You set it to the ID of the list -- the list attribute. And then you can set different options, different steps with different labels so that it's fully-accessible. Yeah, if you scroll down towards the bottom, you'll see the example with -- keep going. Keep going. It's pretty far down. Yeah. This one, here. Right there. You just past it. Go up again.
JASON LENGSTORF: Oh, nice! Oh, wow. Wait, so to do that, we need to input type range -- oh, so tick marks is the ID. I get it! I get it! I get it! So, that makes sense. So, then we're going to -- oh, I love that! So, I guess we can probably put it in the -- in the thing? Let's do a data list ID of tick marks. And then, we're going to get an option value 1. Okay.
EMMA BOSTIAN: I just also want to call this out because Craig made a great point. Currently no browser fully-supports these features.
JASON LENGSTORF: Let's do five of these and go every other will get its value. So, this one will be "2." This one will be "3." 4. And, 5. What does this look like? Okay. So, we're getting closer. We need to add and styles, here, because this is getting a little too -- for now, I guess, let's --
EMMA BOSTIAN: That's awesome, though. I hope that they make that extensible to all browsers quite soon.
JASON LENGSTORF: Okay. So, this doesn't quite work, but it works pretty well. We need an "on change" for this and so "on change." I don't remember how to do this.
EMMA BOSTIAN: Yeah. Yeah. Exactly. We're going to have to set tastiness.
JASON LENGSTORF: Target value? Let's try.
EMMA BOSTIAN: We going to find out.
JASON LENGSTORF: Oh, my goodness! That is beautiful. Okay. So now what we can do is we can send this vote to um...we can send this vote to a database, which means we need a database. For that, we're going to use Hasura. Let's maybe get the clicking through working, first, and we can store all the results and then we'll submit all the results at once.
So -- okay. So, that means we probably need -- this is where now I'm wondering if we need, like, something more than just "use state," but we can -- we can do, like, um...score -- or, we can do "responses." And we'll do "set responses." And that can be "use state." Like, an array? And then, down here, we'll have...uh...okay. We'll save and rate the next taco.
EMMA BOSTIAN: Uh-huh.
JASON LENGSTORF: Good. And then, when we click that, we're going to need an "on submit." And this is going to need, like, logic. So...because we're going to have to, like -- when you still have tacos left in the list, we need to update -- okay. So, let's write this down. "Handle submit."
EMMA BOSTIAN: I wonder if we should actually have -- can you -- in the default state, can you reference another state value? Like, if we have active taco index, can we use that inside of the -- like, do we need -- you know what I mean? I think we should actually keep the index so we can check whether or not we've actually reached the end.
JASON LENGSTORF: What we should -- hmmmm...yeah. Maybe we do want, um -- maybe we do want the index, because then we could be like --
EMMA BOSTIAN: Yeah, I think that's probably better. Yeah.
JASON LENGSTORF: So let's keep an index. And then what we can do, down here, is...tacos. Active taco. And then we'll change all of these. Okay. So, then what -- oh, what we probably also need is a hidden input. Well, no, we don't. We can grab that out of the state. When we save our taco, we need to save the current taco ID and its rating. Then, we need to check if this is the last -- last taco. If not, show the next taco. If so, save the response to the database. I'm going to say "DB" so I don't have to spell "database." So, we can save these in "responses." So I think the first thing we would get is, um, we would do, like..."set responses." And then we would have the current responses. And we can do, like, um...no, wait. It needs to be an array. So, we could return an array, with the current responses, and then set a new one with an ID of --
EMMA BOSTIAN: Why can't we just push it? Oh, yeah, because we're not mutating it directly. Gotcha.
JASON LENGSTORF: I think it has to be immutable. It would be "tacos." "Active taco." The ID and...tastiness, we can just store directly.
EMMA BOSTIAN: Cool.
JASON LENGSTORF: Right? Yes. And then...it would be "if tacos.last index." Is that the right thing?
EMMA BOSTIAN: Ummmm, no. It would be, uhhhh, "active taco is equal to -- where's array?
JASON LENGSTORF: Tacos.
EMMA BOSTIAN: Tacos.link, minus one.
JASON LENGSTORF: Okay. Then --
EMMA BOSTIAN: (Away from mic). (Laughter). She's awful!
JASON LENGSTORF: So, if it is, then here...
EMMA BOSTIAN: Nice. For science. For bragging rights. (Laughter).
JASON LENGSTORF: Okay. So, if we get there, if not, then we would set active taco to be "active taco, plus one."
EMMA BOSTIAN: Uh-huh.
JASON LENGSTORF: Okay. So, okay. So, I think that -- that meets our goals.
EMMA BOSTIAN: We should just see if it works right now.
JASON LENGSTORF: Yeah. I think this will let us advance through --
EMMA BOSTIAN: Oh, we should also reset the input to zero.
JASON LENGSTORF: Yes, you're right.
EMMA BOSTIAN: Or, to middle.
JASON LENGSTORF: Set "tasty" to three. Okay. We added this to "on submit." Yes. Okay. So, let's give this a try. So, this taco, uh, I get to run the control, so I get to make the choices right now. Oh, no! Oh, we need to prevent default.
EMMA BOSTIAN: Yeaaaahhhhh.
JASON LENGSTORF: "Prevent default." Okay. Let's try this one more time. Here. Save. No.
(Laughter). We need to style this.
EMMA BOSTIAN: Yeah.
JASON LENGSTORF: Actually, that one does (Away from mic). Absolutely not. No. Yes, I would die for this taco. (Laughter). I'm definitely getting tacos for lunch. Okay. So, I broke something. Oh, it didn't -- oh.
EMMA BOSTIAN: We probably have an invalid index. We probably have our check incorrect.
JASON LENGSTORF: Yeah. You're probably right. So...
EMMA BOSTIAN: So, it's still trying to render, even after we're done.
JASON LENGSTORF: Oh, we didn't return! Like, silly cotton headed ninny mugs. If I go through and do a quick -- yes! So, that -- that actually did the thing. Let's -- I'm realizing now, we probably should have -- let's style this so that it's faster to click through.
So, what do you think we should do with this?
EMMA BOSTIAN: Style-wise?
JASON LENGSTORF: Yeah.
EMMA BOSTIAN: We need to make all these photos the same width and height and give them absolute positioning so they're not jumping around.
JASON LENGSTORF: Yes.
EMMA BOSTIAN: Yeah, let's do the height of those.
JASON LENGSTORF: Add these responses in so we can see them. So, in the app.CSS, what did we call this container? We called it "current vote." So let's go down here and we'll style-up "current vote." So, with that, we can maybe see if we need to do anything with that. But, let's do a current vote image. You said you want to give them a width of -- we set those ones to 800. Let's make them a little bit smaller. We'll get high DPI out of that. For height, you said -- like, 3X4, do you think? Do you freeze? Oh, no, Emma's frozen.
And then, I'm going to try something...I've offended her so deeply that she's gone now. Look at that go! Okay. So, this is, like, my favorite CSS property when you don't control the images, you can "object fit cover. "I hope she's coming back. The cats finally took vengeance and went for her. (Laughter). There she is. Zoom is out to get us! We will get her back in, in just a moment. Here she comes. Oh, you can do it. Come on --
EMMA BOSTIAN: You know what I love is when Zoom decides that it needs to update and quits the app. (Laughter).
JASON LENGSTORF: Like, just mid -- yeah, good.
EMMA BOSTIAN: It was like, "Zoom has an update, you have to restart." It didn't even give me a choice.
JASON LENGSTORF: That does make me really productive, when that happens.
I'll get rid of this spacer, now that we're doing a little bit of styling. Yeah, check that out! Okay. What I did is I used "object fit cover" as an aspect ratio.
EMMA BOSTIAN: Would love to get the -- the text breaking on to new lines.
JASON LENGSTORF: Whoa. Hold on. We missed one.
EMMA BOSTIAN: Someone said we had an "off by one" error.
JASON LENGSTORF: Oh, because our -- because our -- wait! If our active taco --
EMMA BOSTIAN: Maybe remove the "minus one"?
JASON LENGSTORF: We actually want the last one. It's when we get to the one after the last one that we would want to update. Okay. So, maybe one -- oh, no! Hush. Uhhh, so, one more quick thing we should probably do is just style these up a little bit. So, let's do it right in the browser.
EMMA BOSTIAN: Flex Box.
JASON LENGSTORF: All right. So, talk me through Flex Box.
EMMA BOSTIAN: Form, you're going to want a display flex and flex direction column. Nice. Nice. And then...uhhhh...ooohh, that input field though.
JASON LENGSTORF: So I wrapped it with the label so I can probably pull it out, actually, because I realized I also made an accessibility error, I did an HTML 4 and I didn't give this an ID but now that it has an ID, if we pull it out of the label, it's still connected because of this HTML 4 and the ID. And now these work. So, we've got our form, display flex, flex direction column.
EMMA BOSTIAN: We might want to add -- I don't know if a line center or justify content center is going to allow -- okay. I guess center-lining is possibly the best. Yeah, nice. That's good enough.
JASON LENGSTORF: Yeah, I think for this particular use case, we're going to be okay. I'm trying to give some breathing room so you can see what's going on. But, yeah, that actually came out just a little bit -- what is, what? Five lines of CSS and just made this look about 1,000 times better. If we go to the button, we can add button styles. We'll go "margin-top." Wow, come on, spell. Give it a little bit of breathing room and then we can make it, like, background --
EMMA BOSTIAN: Salmon. Oh, that's awful. Cool. Love it. (Laughter).
JASON LENGSTORF: Give it some padding.
EMMA BOSTIAN: Let's add a nice, little transition on there.
JASON LENGSTORF: Oohhh. Okay.
EMMA BOSTIAN: Well, you're probably going to have to pull that out of the Editor. Pull it out of Dev Tools and pull it out of the Editor.
JASON LENGSTORF: Once you've written styles in the browser, if you click this Inspector Style Sheet, it'll show you everything you've written so you can copy-paste it.
EMMA BOSTIAN: Copy pasta.
JASON LENGSTORF: I love copy pasta.
EMMA BOSTIAN: Can you make sure that black text is accessible on that salmon background? We can do that in Chrome, as well. I think you can click the color.
JASON LENGSTORF: I think we have to actually click the color. Contrast is --
EMMA BOSTIAN: Look at that! If we set it to white, it's definitely not going to be AA/AAA. Glad we sorted that one.
Go back to the style sheet, real quick. So, this is CSS. So, on button hover and button focus...let's do a" transform scale." Let's do 1.1, maybe and add transition transform. I think it's "transform. "It may be "scale." We'll do .2 seconds and see how that looks. Nice!
JASON LENGSTORF: Nice! Okay.
EMMA BOSTIAN: All right.
JASON LENGSTORF: That's fun. Cool. So, now, we have, like -- oops. Uh-oh, uh-oh. Let's try that again. Click, click, click, click, click, click, click, click. Where's our last --
EMMA BOSTIAN: It's the last case, I think. So, um, I think it's still probably trying to set -- well, we have eight. Are seven being rendered or eight? What is up with our indexing?
JASON LENGSTORF: I'm not sure. Let me pull this off to the side.
EMMA BOSTIAN: The minus 1 was correct.
JASON LENGSTORF: But the minus 1 couldn't have been correct because we were missing one? Did I just miss this? One, two, three, four, five, six, seven. So that would be index eight. We're initializing at zero. So, where it should --
EMMA BOSTIAN: So if "active taco" is seven, that's our last one -- wait, hold on.
JASON LENGSTORF: When it's seven --
EMMA BOSTIAN: We need a negative 1 on there. Minus 1.
JASON LENGSTORF: Isn't that what caused us to be missing one?
EMMA BOSTIAN: I don't think so.
JASON LENGSTORF: One, two, three, four, five, six, seven, eight. 0hhhh! It's because of this! We have, like, a race condition, where this is being set but -- okay. So, to beat that, we need to --
EMMA BOSTIAN: We need to move it under the "if" statement, no? Oh, wait. No. No.
JASON LENGSTORF: I think we have to do something like this...and then, we can do "responses." And then...we'll just set the responses to "new responses." Really -- if we need this, then we're going to need --
EMMA BOSTIAN: Oh, I got you. Okay.
JASON LENGSTORF: That way, we beat the race condition just by having the object copied.
EMMA BOSTIAN: Can you explain what that is, for my sake, but also for people listening?
JASON LENGSTORF: Yes. So, when React runs a use state update, it -- the state is not immediately consistent everywhere, which means because we declared responses and set responses, when we run the set responses, we can't guarantee that responses will be updated to this value when this code runs. So, that means that we would need to do -- we'd need to do something more -- we'd need a use effect that watch the responses value, or something like that. This solves the same problem because we get the new value. What this wouldn't solve is if this were to randomly fails. If this randomly fails, we've got much bigger problems because I don't think it's theoretically possible in the React codebase. But it keeps it consistent. And that way, we can work on that updated value, even if we don't have the absolute latest and greatest from use state.
This absolutely will get us into trouble in a more complex app, though. I would not ship this to production, this copy of a copy here. You would want to rely on the React lifecycle to make sure you don't end up one response here and one response there and they're not quite consistent, except when they are. You'll be sad. Since we only have one component that is doing one thing, this is an acceptable workaround. But, you know, your mileage may vary.
EMMA BOSTIAN: I think it's database time now.
JASON LENGSTORF: I think it is. Let me run this, one more time, to make sure we get all eight. And I'm going to change some of them. So, there's all eight. And it's storing them as a string, so let's fix that. We want -- we want the values to be numbers. So, what if I just change these...
EMMA BOSTIAN: Oh, I don't know if you can do that; can you?
JASON LENGSTORF: Let's find out, because that would be the easiest solution.
EMMA BOSTIAN: Yeah, that's true.
JASON LENGSTORF: No, that doesn't work. So, instead, we got to go in -- we'll parse "int." And then, now...we should see that first one's five is an integer. Yeah! Okay. We can go in here. We've already got the ID and we've got a tastiness score. We're going to do a pretty insufficient thing where we're going to store these in the database. We are going to do that because we've got 30 minutes left and I want to make sure we get it done.
So, I'm going to log in --
EMMA BOSTIAN: This is the first time I'm actually making a full-stack app.
JASON LENGSTORF: I'm very excited.
EMMA BOSTIAN: I took database in college and stuff. But in isolation, I've never fully put one together.
JASON LENGSTORF: 0hhhh, taco choices. I'm going to use their free tier, which is the U.S. East Region. When I continue to the database setup, I have this option to try with Heroku. I'm logged into my Heroku account. If you're not, they'll take you over to log in. Now, ta-da!
EMMA BOSTIAN: What type of database is Postgres?
JASON LENGSTORF: SQL. If you've used MySQL, the database interaction would look very similar. Fortunately, we don't have to worry about it. There's no SQL required for Hasura. We use GraphQL.
I'm going to set this so the chat doesn't hack the crap out of us, because they always do. (Laughter).
You hackers! You -- you dirty hackers!
JASON LENGSTORF: Yes, that's you, chat. You are the hackers.
So, then, what I'm going to do is I'm going to go over to this data tab. Thank goodness, it was a little laggy yesterday and it's snappy as hell today, so that's good news. We're going to call this "taco votes." And we'll give it a column of ID. This is going to go an integer and we'll just not auto-increment it. And then we need -- actually...
EMMA BOSTIAN: Maybe we shouldn't auto-increment that. It might be buggy.
JASON LENGSTORF: We need a primary key. This is going to be not the thing. We're going to, instead, use a taco ID. That will be an integer. We need the Tastiness score.
EMMA BOSTIAN: What we're missing is a boolean.
JASON LENGSTORF: We forgot that the whole point was to troll each other. (Laughter). We'll go add a flag for "is Taco Bell." So now when I click this, we'll be able to go to Graphical. We'll be able to insert new taco votes and we can insert like this. We'll be able to, um -- no, not update. Insert. And the objects is an array so we can do "is Taco Bell, Taco ID and Tastiness" score. Our taco ID would be zero. Tastiness score would be three. "Is Taco Bell false" is good. So then, we'll see, we created one taco vote row. And if I go over to "data," and look at this taco votes, we can see, now, we've got one vote. So, I'm going --
EMMA BOSTIAN: Because you actually ran that mutation. I gotcha.
JASON LENGSTORF: That is everything we're going to need. We'll just call this "add taco vote," so it's easy for us to figure out what's going on. Let's set this up to actually run. I guess the first we need to do is add this "is Taco Bell" flag.
EMMA BOSTIAN: Do we need to add any dependencies for this?
JASON LENGSTORF: We'll add one for node fetch. I'm going to copy/paste this. This one is -- and these three are. This one's not.
EMMA BOSTIAN: I'm glad we caught that because we would have completely defeated the purpose of this.
JASON LENGSTORF: Wouldn't that have been heartbreaking?
(Laughter). Okay. The other thing we're going to need is the -- so...will be tacos "active taco." And then we can do the taco ID. And..."is Taco Bell" will be "Taco is Taco Bell." Now we should get the full object we need and we'll be able to send these off. So, what I am going to do is, um, I'm going to just get this set up for, um, Netlify functions because we're writing to a database, we need to have some kind of protection of the database keys. Because we need that admin key in order to write to this database and we don't want anybody to be able to write to our database from anywhere, which means we can't put this in our client code because they could open the source viewer and skew the results, which, you know, we don't want to do bad science here.
EMMA BOSTIAN: I'm going to warn you, I got a notification that Zoom will quit in 15 minutes. No option to delay this. What I've learned is Zoom is horrific in terms of -- it's blocking our fun.
JASON LENGSTORF: Where does this build to when we build it? I'm going to find out. I think it's going to go to "dist." It goes to "build." So, the publish folder is "build." The command is "yarn build." And our functions are going to live in a folder called "functions." And that should be everything we need to get this set up. I'm also going to NPM -- or, I guess "add yarn fetch." Then I create functions and add -- let's do, like, (Indiscernible) add spots. And in here, we can get fetch, which will be "require node fetch." And then we're going to export a handler. This is the AWS flavor or Netlify. Other serverless flavors will do it differently. So what I want to get out is the responses, which will be in the event body, but they are stringified. So, we're going to "event body, JSON parse that." And then we need to send this off to Hasura. We need a few things. First, I'm just going to copy-paste a little helper file that I have, that makes this a little bit easier. So, let's go to "Learn With Jason." I'm working on a new version of my site and I just added in some Hasura stuff. This will make our lives a little bit easier.
And what this function does is, if we do it this way -- actually, let's just do it this way. We'll get it out like this. We're going to send a fetch request off to our Hasura URL, which we need to grab as an environment variable. Then we've got our secret, which we set in the cloud dashboard and we can send off our query and our variables. So, our query is going to be that mutation that we wrote. And, our variables are going to be the responses. So, the responses -- if we take a look at these again -- are -- let's refresh and run a new one of these, now that we've got the "is Taco Bell" flag in there. We've got ID, tastiness and challenge true. This ID is not actually the taco ID, which is what we'll need in the database, which means we're going to have to map this. So, let's grab that mutation. And this, we can almost drop right in. The main difference is we're going to turn this into a variable.
So, if -- an easy way to do that is to come over here and look as our tacos and we can just hit that "variable" button and it becomes objects and we can see, here, it tells us what it needs to be. It did all that stuff for us, that's cool. I'm going to copy this out and put this right at the top here. And I'm going to make it required instead of making it optional, which is what Hasura did by default. Then our objects are going to go here and that means that our variables need to include objects because it maps these to these and that needs to be an array. So, we're going to get our responses.map and for each response, we're going to return a taco ID, which will be the response ID. And then, we're going to return the, um -- I guess we can just do the rest of it. Actually, we can't, because that would override our ID. So, instead, we're going to include --
EMMA BOSTIAN: Well, can't you just spread response first, and then overwrite that one? Oh, I see what you're saying.
JASON LENGSTORF: We'd have to delete it or something, so instead, we'll do it the long way.
EMMA BOSTIAN: Couldn't we just rename it -- I mean, you can rename it.
JASON LENGSTORF: Wait, you can rename keys in an object?
EMMA BOSTIAN: We abstantiated it. Can't we change it in the object definitions?
JASON LENGSTORF: I mean, you could, I guess. (Laughter). No, that's a much better idea. (Laughter). Okay. So, let's fix that. Um, here we go. We've got our IDs. I missed one. So, there's our taco ID. And then down here, we need taco ID and taco ID. Do we use ".ID" anywhere else?
EMMA BOSTIAN: We're not mapping anymore.
JASON LENGSTORF: Yeah. We're not mapping anymore. Okay. Okay. Let's just verify that, one more time, and make sure that's what we want. Boop, boop, boop, boop, boop, boop, boop. Copy ID tastiness. Perfect. That will be significantly easier to handle because we can just drop this directly in.
And that means that whatever we get back, we're going to just get back the number of affected rows. I'm going to return a status code of 200 and a body of "okay." And we're going to be fine with that.
Um, yeah, we've got 18 minutes. Once we have that, I'm going to initialize this for work with Netlify. So, we've got our site, here. I'm going to get "add everything." Good.
EMMA BOSTIAN: Do we need to add, like, the URL?
JASON LENGSTORF: We do, but I'm going to set it up as an environment variable and in order to do that, I could put it in a local ENV file, but I'm going to set it up in Netlify so we can deploy it.
Okay. Almost ready to judge Emma's taco choices. And then, we can -- did I already create this one? "GitHub, repo, create." And we can copy-paste this. Make it public. Yes. Good. Then, we're going to get "push." Set the upstream so I don't have to keep typing this. And then I'm going to Netlify "init." And we want to create and configure a new site. We'll put it on my team. We're going to call this "We Need to Taco 'Bout Your Choices." Good. Leave that default. Go build. Good. So, now, I can "Netlify open." It opened the right window! Look at that! Now, I can go into my settings and my build and deploy environment and I'm going to add environment variables.
EMMA BOSTIAN: (Away from mic).
JASON LENGSTORF: It's so much nicer than an N file. So, I'm going to copy this one and this one and thanks to Sarah, I have --
EMMA BOSTIAN: That's amazing.
JASON LENGSTORF: It's so nice. I can just type in here and not worry about you hackers stealing all my secrets. Okay. And so, the GraphQL is here. Now that I've done that, check this out, I can do "Netlify dev" and it pulled those environment variables from Netlify. So now if I was working on a team, my team members would be able to run Netlify dev, as well. So we don't have to DM each other. It's really nice.
Oops, I have it runs in two places, so let's close this one and let's run it again. Da-da-da. Okay. So, now that this is running, we can call this, um, this function with our response value. So, if we go back out here, to this one, um, I think I can just -- I think you can just do this. Does that work? It sure does. Okay. Perfect. So, now what I can do is I'm going to open, like, Postman. You could use Curl. Because this is running locally, I can actually test this function by sending real data to Hasura. This is a nice way to do it so I'm going to go to http, local host 8888. It's always go to be at .Netlify/functions. We called this "Hasura add response," I think? "Hasura add response." Yes. I can send -- as a body -- we'll go with raw, JSON and I'm going to paste this in here. And I think that might work or it might explode. We'll find out.
So, I'm just going to pass in -- no, I need more. We needed to call this "responses," I think? Yes, because it's looking for responses. So, we'll call this "responses." And then, here -- let's see if that just works. I think it might explode -- oh, no, this is valid JSON because we're not sending any strings or anything so I just need to make that valid. Yes! Here we go...we got a 500 error. Field tastiness not found in insert input. Oh, no, we named it something different, didn't we? Tastiness Score. Oh! Frustrating! This is what I get for trying to be -- so, our tastiness is going to be "tastiness score, is tastiness good." Let's try that one more time. Boop, boop, boop, boop, boop, boop. Okay. So, now I can go back and let's get all of these out of here. Now we've got a tastiness score. So, if I send, we got a 200 "okay." And that should be that if I go back to Hasura and I look at our data, look at that! We've got votes!
So, now I am just pleased as punch because we can set this up to actually run. So, what we would do, now, is we would say, um...new responses. We'll make this async and we will await fetch, Netlify, functions, Hasura, add response and we're going to send it as a post request and the body is going to be, um -- wait...we're going to JSON stringify responses, but we're going to set that to "new responses" so it's the right value. Then what we should see here, if I go in and I actually complete this --
EMMA BOSTIAN: Uh-huh?
JASON LENGSTORF: So, let's do this. We'll set a couple of these. We'll just move them around, get some different scores. Okay. So, we don't have, like, an end state but what we can do, then, is we can go and look at this. We'll run the query. And now...we have more scores. So, this works. This is actually functional and that means we can deploy it and the chat can actually start voting on tacos.
What did I do? Oh, crap. So, pushing that change and this should build pretty quickly. And, while we're waiting for it to build, let's get a chat for the link. Oh, no, Emma! She's back?
(Laughter). Just constantly betrayed by Zoom.
EMMA BOSTIAN: I'm over it. (Laughter).
JASON LENGSTORF: So, we're getting a link for the chat to go and vote on these, because we are just about in business here. There we go. It's live. So, chat, go and vote on tacos. And, we've got nine minutes, so we probably don't have time to actually show how we would pull this in, but actually, this -- this could make a really fun follow-up, where maybe we will do a graph that we can show, where, like, once you -- once you finish voting, we can pull in those results and show you a score of, like, whether or not people are fans of Taco Bell.
Um...but, yeah, I'm so excited. I'm really glad we got this running.
EMMA BOSTIAN: (Away from mic).
JASON LENGSTORF: Chat, that's really what you're after, right? We were able to -- pretty quickly -- three people are voting. (Laughter). Getting a lot of taco votes here, so we're going to have really good, scientific data. (Laughter). So, Emma, uh, are you down to come back? I know I'm kind of putting you on spot in public. Do you want to display our results?
EMMA BOSTIAN: We have to give the people what we want, which is to watch me sit here, like, "what is happening?"
JASON LENGSTORF: We should add a "thanks" page. If we get to the last taco, so, this would update our active taco, here, two tacos length is greater than one. So what we could also do is "set is done. Set is done." And we'll do use state false. And then if you get here, we will -- after you finish -- actually, let's do it before. Set is done to true. And, if you are done, what we can do is change this out. And we'll do, like, a --
EMMA BOSTIAN: Marque.
JASON LENGSTORF: Does that still work?
(Laughter). Okay. So, we can -- we'll put this in a div.
EMMA BOSTIAN: Oh, my god, somebody put a Marque in your chat.
JASON LENGSTORF: Oh, no! That is bad news. (Laughter). This is going to go so poorly. Okay, but, for real, though, don't do anything bad. (Laughter). So, we can do some results and then let's just -- we can really quickly style this up a tad. And, while we're doing this, Emma, where should people go if they want to learn more about you --
You hackers! You -- you dirty hackers!
JASON LENGSTORF: Or about tacos?
EMMA BOSTIAN: I mean, you can find me crap-posting on Twitter. But I'm really trying to share more on Instagram. So, like, follow me on there, because --
JASON LENGSTORF: Is it the same?
EMMA BOSTIAN: Yeah. It's private though. You know why I privated it? Not because I care, but sometimes my friends will follow me and I'll miss it so I have to, like, accept everyone, I can see -- then I don't miss my friends.
JASON LENGSTORF: I gotcha. I gotcha. I think I still technically have an Instagram.
EMMA BOSTIAN: You do, I tagged you in one of my stories. (Laughter).
JASON LENGSTORF: This is so bad.
EMMA BOSTIAN: It's so great. (Laughter).
JASON LENGSTORF: Okay. That's atrocious. I'm going to commit this. My favorite commit message.
EMMA BOSTIAN: Dear lord. The chat is trying to hack Twitch now.
JASON LENGSTORF: Stop hacking, chat! I don't think colors will work, I think I fixed that. The site used for resizing images is Squoosh.
Shout-out to White Coat Captioning, and Vanessa, who's been helping us out all day. Thank you so much for taking the time to be here and caption the show. That is made possible by Netlify, Hasura, Fauna and Auth0.
Make sure you also go out and check out the schedule because we've got a lot of really fun stuff coming up. We're going to learn Minecraft next week. Literally, I'm going to play the game, Minecraft, for the first time, on stream, with Lindsay. I'm going to dig myself into a hole quite literally.
Then, Jay is going to come up and we're going to do kind of a really interesting SVG animation -- oh, you broke my whole stream. You hackers. Get out! I'm so mad at all of you. Noooooo! What is that. All right. Okay. Jeez. What is this? What is this? We're just going to refresh that.
You hackers! You -- you dirty hackers!
JASON LENGSTORF: Did it even work? You're all on timeout. That's how I feel about this right now.
EMMA BOSTIAN: You know you're never going to have a solid stream again now. They set display "none."
JASON LENGSTORF: What?
EMMA BOSTIAN: I got you.
JASON LENGSTORF: You're all the worst. That's how I feel about you all now.
(Laughter). You know what? Just stop. Just stop. You're all -- you're all done. Um -- (Laughter).
EMMA BOSTIAN: That was the best thing that's ever happened.
JASON LENGSTORF: I was going to teach you stuff and now I'm over it. You're all on timeout.
EMMA BOSTIAN: I feel like that's a Twitch security flaw.
JASON LENGSTORF: It's a "me" security flaw. I wrote this chat box.
EMMA BOSTIAN: Ohhhh. You're screwed.
JASON LENGSTORF: The overlay is just a website. I would have never expected that my chat is so full of monsters that I would have to protect my chat box against SSS attacks. Well, now that I've been thoroughly betrayed, Emma, thank you so much for taking the time to hang out us with. Chat, thank you for being terrible. Vanessa, thank you for doing captioning today.
You're all terrible and wonderful and I treasure every minute. We're going to raid. Emma, thank you, again, I'm going to end this before we both drown.
EMMA BOSTIAN: Thank you.
JASON LENGSTORF: Bye!