
Code A Linktree Clone With React, NextJS, TailwindCSS, And Supabase (Full Coding Tutorial)
Code A Linktree Clone With React, NextJS, TailwindCSS, And Supabase (Full Coding Tutorial)
Get access to high quality coding projects that actually impress recruiters: https://www.sweprojects.com/
Check out all the gear that I use! https://kit.co/dohyunkim/desk-essentials
Follow me on TikTok: @youravergetechbro
0:00 - 0:41 Intro
0:41 - 2:23 Set up React, TailwindCSS, NextJS
2:23 - 4:00 Set up Supabase
4:00 - 23:00 Set up Supabase Authentication
23:00 - 34:15 Write new links to Supabase DB
34:15 - 45:38 Read and style links from Supabase DB
45:38 - 1:08:23 Upload profile picture to Supabase Storage
1:08:23 - 1:15:28 Create unique page for creator username
Content
0 -> what's good everybody it's your average
1.56 -> Tech Pro and in today's video we're
3.36 -> going to be recreating link tree
5.22 -> completely from scratch using Tailwind
7.5 -> react next JS and everything is going to
10.139 -> be hosted on Super Bass and within Super
12.12 -> Bass we're going to be using their data
13.559 -> space and their storage as well as their
15.78 -> authentication features for those of you
17.58 -> that don't know linktree is a link
19.5 -> aggregator that is really popular
20.939 -> amongst content creators on tiktok and
22.98 -> Instagram it lets creators build their
24.779 -> own personal website because Tick Tock
26.58 -> and Instagram only allow them to have
27.96 -> one Link in their bio so linktree lets
30.42 -> you have multiple links in your website
32.34 -> this is a great full stack application
34.2 -> and it's going to be a pretty in-depth
35.82 -> and complicated one because we're going
37.2 -> to be using a lot of features from
38.94 -> within Super Bass so get ready to code
40.8 -> and let's get started let's get started
42.66 -> in creating our link tree clone we're
44.94 -> going to be creating this application
46.2 -> using next JS react Tailwind CSS for
49.739 -> styling and we're going to be using
51.12 -> Super Bass as our backend the very first
53.039 -> thing that we're going to do is set up
54.18 -> our next JS react and Tailwind
56.28 -> environment the way that we're going to
57.42 -> do that is by going over to the Tailwind
59.16 -> CSS document station and run this
61.379 -> Command right here this creates our
63.12 -> entire react and next.js project for us
65.76 -> so let's copy this command
68.58 -> paste it in here
72.659 -> we'll call link tree clone
77.82 -> proceed
80.28 -> and just wait for this to complete
81.54 -> downloading okay so it completed
83.34 -> downloading now let's open our editor
85.259 -> right here and then let's follow the
86.64 -> instructions on how to set up Tailwind
88.14 -> so now let's run these two commands
90.06 -> right here
93.299 -> foreign
97.38 -> okay
98.64 -> and then let's add this into our
100.68 -> module.exports
111.24 -> then we add this line of code into our
113.54 -> globals.css file
123.42 -> and then we can run npm run Dev and
126.54 -> let's get it up and running last but not
128.52 -> least to verify that Tailwind is working
129.959 -> properly we want to just copy this block
131.58 -> of text into our editor right here
133.319 -> replace all the code that was previously
135.42 -> in the home index.tsx file and then when
138.48 -> we save that let's go back to our local
140.099 -> host and we can say that it is properly
142.26 -> styled with the proper styling for those
144.48 -> of you that are new to Super Bass Super
146.28 -> Bass is a Firebase alternative that is
148.68 -> completely open source and it is what I
150.9 -> personally use for my projects nowadays
152.64 -> now to start off with Super Bass you
154.2 -> just create an account and then you
155.64 -> create a project once you create a
157.08 -> project you're going to be in this page
158.64 -> right here then go over to your
160.739 -> application and let's create a file
164.18 -> called.m.local and this is where we're
166.08 -> going to be storing all of our
166.98 -> environment variables we need to set up
168.84 -> our dot m dot local file because these
171 -> are the actual URLs and the keys that
172.92 -> we're going to be using to connect to
174.36 -> our Super Bass instance we're going to
176.4 -> need two environment variables your
178.019 -> Public Super Bass URL and your Public
180.12 -> Super base and on key to get your Super
182.459 -> Bass URL you want to go over to your
184.26 -> project so right here I'm in my link
185.76 -> tree clone project you want to go to the
187.5 -> settings tab where I'm at right now go
189.42 -> over to your API Tab and this is going
191.76 -> to be the URL so copy the URL
195 -> paste it into your.m.local and label the
197.879 -> variable name as next public Super Bass
200.519 -> URL and then next up for your next
202.68 -> public Super Bass or non-key where you
204.84 -> get this
206.04 -> is you go over to the same exact page
208.019 -> and you get over to your project API
210.18 -> keys this allows your local instance to
211.98 -> actually connect to your super based
213.48 -> instance I will say be careful because
214.98 -> this does use up your actual free quota
217.14 -> of your Super Bass instance but for any
219.3 -> small project like your personal project
220.799 -> you're probably not going to be running
222.299 -> up that many instances or having that
224.34 -> much action on them for you to even get
226.319 -> close to using up your entire free
227.819 -> instance you want to go over to the
229.26 -> super based JavaScript library
231.06 -> specifically I'm going to be using the
233.04 -> Super Bass JS version 2 Library so just
235.86 -> copy that now let's run the command and
237.659 -> install this package into our linktree
239.7 -> app alright so now let's get started
241.019 -> with our Super Bass authentication the
243.12 -> way that we're going to do that is we're
244.379 -> going to create a brand new file here
246.239 -> called
248 -> login.tsx the way that next JS works is
250.92 -> that this login.tsx file login is going
254.099 -> to be a brand new path on our website so
256.26 -> to log in you're going to go to
257.4 -> localhost 3000 slash login and whatever
260.34 -> component we create right here is then
262.26 -> going to get rendered on that login page
264.54 -> so let's create that component so let's
267.12 -> do import
268.5 -> use state from react and then we're
272.28 -> going to do
274.44 -> export default function login
278.28 -> now return just a little div just a test
282 -> that we can properly render this let's
284.699 -> navigate to this page
287.22 -> and let's go to login
289.259 -> perfect this is the login page that we
291.84 -> created right here within hello world so
294.78 -> now let's create that actual login form
296.52 -> well the way that we're going to do that
298.139 -> is we're going to create a few State
299.34 -> variables called
301.139 -> you email set email it goes use State
305.759 -> stream
307.8 -> or undefined and we'll set that there
311.58 -> and we'll do the same thing for password
313.24 -> [Applause]
318.3 -> so now so what is going on where did I
321.3 -> mess up oh here
325.5 -> oh still some syntax errors what's going
327.72 -> on oh
328.16 -> [Applause]
330.24 -> all right so now we just have our email
331.919 -> and our password field now let's
333.66 -> actually start rendering some of those
335.46 -> input forms from Tailwind luckily for me
337.56 -> I actually I actually purchased Tailwind
339.419 -> UI which is a bunch of beautiful
340.86 -> pre-made components and I personally
343.02 -> brought this with my very own money I
344.46 -> think it's a really great investment so
346.5 -> let's go input groups and let's find
349.139 -> yeah this is exactly what I'm looking
350.88 -> for and I'm writing in react so I'm just
353.22 -> going to copy the JavaScript right here
357.479 -> um let's see yep just gonna copy this
359.759 -> right here and then I will then paste
364.38 -> oh
367.44 -> here
369.18 -> let's go
370.44 -> delete this part perfect okay
374.699 -> um oh gotta delete that
379.38 -> okay oh and I actually forgot there is
381.24 -> this extra plug-in we need to add right
383.58 -> here so let's update our
386.9 -> tailwind.config.js file
392.639 -> perfect okay and I believe
396.24 -> we might need to refresh our Pig Yeah so
398.94 -> let's rerun it again we have to rerun
401.4 -> npn run that when we update our Tailwind
403.62 -> config file what is going on here I
406.5 -> cannot find module Tailwind CSS forms
411.68 -> CSS forms
415.139 -> let's try that
418.62 -> all right cool have to install it now we
421.02 -> should be good to go let's refresh the
422.699 -> page right here awesome look at that
424.74 -> kind of pretty form we're getting there
426.6 -> let's let's make this a little bit
427.8 -> prettier now so I am going to then I'm
431.819 -> actually going to get rid of dark mode
433.259 -> really quickly because I'm actually not
435.06 -> feeling this background white color
437.52 -> black
439.08 -> for scheme is white or
443.819 -> huh
445.86 -> light
447.5 -> uh much better I just think that was a
449.819 -> little bit better for now so let's
451.38 -> format this to center it in the center
453.24 -> and make it a little bit prettier
455.34 -> the way that I'm going to do that is I'm
457.68 -> going to add start styling with Tailwind
459.84 -> right here and once again what's really
461.4 -> special about Tailwind is the fact that
463.02 -> you can style all of your CSS using HTML
465.84 -> class names that you can just memorize I
467.639 -> have a whole video explaining Tailwind
469.08 -> right here if you want to check it out
471.06 -> so I'm gonna do let's see I probably
473.22 -> want to do a flex box
475.5 -> oops Flex with 50 with one half to do 50
481.08 -> width perfect uh actually I think I'm
484.319 -> gonna do Flex call justify Center and
487.68 -> then items Center
490.02 -> okay oh
492.06 -> we're getting there let's go with full
494.36 -> perfect and height full
497.819 -> okay you know what this is good enough
499.379 -> we have now centered
502.49 -> [Applause]
504.72 -> oh
506.22 -> all right so now we have centered our
508.74 -> email element right there accordingly
510.78 -> it'll be that we did that was by
512.52 -> applying a flex box with flex column
515.279 -> making the width full and justifying the
517.8 -> items in the center and aligning the
519.599 -> items by the center and that's how I got
521.7 -> this nice Center lining right here so
524.099 -> now let's also do the same exact form
526.02 -> but let's also do it for a password as
528.839 -> well so here
532.2 -> password update the html4 password
538.32 -> password
540.18 -> password and password
545.16 -> and then for the place folder we will
547.019 -> also do
548.519 -> password
552.839 -> so let's check again awesome we have our
555.779 -> email and our password and as you can
557.7 -> see the email and the password forms are
559.38 -> a little cramped together so I'm going
560.76 -> to add a little bit of padding to or a
563.399 -> little bit of margin into my password
565.38 -> and the way that I'm going to do that is
567.959 -> actually right here
569.94 -> since this is all within
573.839 -> on my label I'm going to add margin to
577.32 -> the top and I'm going to do a predefined
579.98 -> uh margin defined as mt4 this stands for
583.86 -> margin top Dash four I know some of this
586.56 -> class name stuff is kind of confusing to
588.3 -> go along with if you've never really
589.86 -> used a Tailwind before but I do think
592.62 -> learning talent that learning curve is
594.48 -> definitely worth it because I'm just
596.459 -> you're just you're you're going to be so
598.08 -> much faster writing your CSS and styling
600.18 -> your components compared to if you were
601.92 -> doing everything with normal CSS classes
603.779 -> so I know that this is a little hard to
605.64 -> follow but I just I'll try my best to
607.92 -> explain that as we go along so this
609.48 -> little class I'm passing in right here
610.98 -> I'm adding some extra margin and look at
613.44 -> that it's more evenly spaced it looks
615.3 -> great now we also just want to add a
617.279 -> button right here to sign up or log in
620.16 -> uh so let's go over to buttons on
623.519 -> Tailwind UI again perfect and I want to
626.339 -> get which one do I want to get um this
628.2 -> middle one's a good size so
630.779 -> I think that's this one perfect yep
633.24 -> let's get that once again
636.6 -> button text let's make sure it looks
638.76 -> good okay good good let's add a little
641.58 -> bit of margin to the top
644.48 -> mt4 nice
647.94 -> and then we'll do sign up
652.86 -> so from here let us then we can now
656.7 -> actually handle the signing up with into
658.92 -> our application
660.779 -> or yeah
662.22 -> or this is actually the login page so
663.959 -> we'll make this login
666.839 -> and I just realized that this is sign up
668.579 -> so we're actually going to rename our
670.019 -> login.csx file file to signup.tsx file
673.8 -> then all I have to do is localhost 3030
676.079 -> sign up and we're good to go right there
678.54 -> so to actually go through the
679.74 -> authentication process and sign up we
681.899 -> now need to actually connect to
683.76 -> superbeast and the way that we're going
685.68 -> to do that is we're going to create a
688.74 -> Super Bass client file and we can do
692.459 -> that by let's create a separate folder
694.68 -> right here called utils
698.279 -> and then make a new file we're going to
700.26 -> call it Super Bass client.ts
703.32 -> now we're going to actually connect to
704.88 -> Super Bass using the Super Bass
706.32 -> JavaScript library that we downloaded
708.3 -> earlier so to do that we're going to do
709.86 -> import create client
712.56 -> from
714.12 -> Super Bass JS and then we're going to do
717.24 -> Define the Super Bass URL which we
719.88 -> defined in our in our dot m dot local
722.279 -> file it's going to be proxy.m dot next
725.579 -> public Super Bass URL
729.36 -> and then in the case that this is null
731.22 -> we're going to just default it to just
732.899 -> an empty string and that's what this
734.399 -> double question mark syntax does it
736.68 -> allows you to say if the value on the
739.019 -> left is null give it this value on the
741.18 -> right as the default value likewise
743.399 -> we're also going to do the same for the
744.48 -> super based non-key which we also passed
746.579 -> in
748.019 -> so it's going to be next public Super
750.24 -> Bass a non key and once again give it a
754.44 -> default value of an empty string now
756.6 -> we're going to actually create our Super
758.1 -> Bass clients
760.98 -> and then pass in the Super Bass URL
763.2 -> Super Bass and non-key and then export
765.72 -> default to the base now we are actually
768.24 -> going to be connecting to the Super Bass
770.16 -> client
771.54 -> now they'll go over to
774.24 -> actually providing the ability to sign
777 -> up with Super Bass so we're going to go
779.7 -> back to our signup.tsx file we're going
782.519 -> to create our function cons oops spacing
785.7 -> function sign up with email we're going
789.899 -> to do that we're gonna do the try catch
791.82 -> statement
793.8 -> and once again try catches are important
796.019 -> so that in case we run into any errors
798 -> we throw the error and handle it
799.68 -> accordingly but since this is not really
801.42 -> a production grade app this is more of a
803.1 -> personal project that is not as big of
804.66 -> an issue so we're going to do try uh
808.26 -> we're gonna do
810.24 -> constant response is equal to await
812.279 -> Super Bass Dot from
816.12 -> oh not from dot auth dot sign up
820.019 -> and then we're going to pass in the
821.76 -> email
823.98 -> and then we're also going to pass in the
825.48 -> password
827.3 -> and that's oh and we also need we also
830.639 -> need to verify that so the reason and as
833.579 -> you can see we're getting an error right
834.779 -> here because email cannot be undefined
837.42 -> but right now our email and our password
839.339 -> could be either string or undefined so
842.04 -> I'm going to only execute the statement
844.5 -> if email and password are not undefined
849.72 -> and now our type checking is good to go
851.7 -> right there
853.079 -> and then actually this response object
856.139 -> has a this response object right here
858.6 -> has a data and an error field so I'm
861.779 -> going to say if response dot error then
864.959 -> we throw that error then we say the data
868.68 -> oops response we then throw the response
871.32 -> dot error
872.88 -> now we're going to say uh the data
875.519 -> actually has a user and an ID field
877.94 -> embedded into it so we're going to
879.959 -> Define this field called user ID is
882.3 -> equal to
884.18 -> data.user.id so now user ID is going to
887.339 -> hold that
889.26 -> response dot data my bad
892.079 -> so now uh the we are fully authenticated
894.66 -> now so we should be able to sign up
898.139 -> accordingly and what do we want to do
900.3 -> afterwards we'll just counter log the
902.1 -> user ID and let's see what comes up
904.86 -> there oh but now we also need to make
907.26 -> sure we handle our on change elements
909.779 -> properly so that you know when we're
911.339 -> actually right now as we type in here
914.339 -> we're not currently we are not storing
916.8 -> the value of this typed out result of
919.86 -> right now we're not storing the value of
921.779 -> this type and input
923.76 -> into our email or our password State now
927.839 -> that's really easy to do that easy way
929.639 -> to do that is we pass in and on change
932.16 -> Handler
934.32 -> and then we're going to do
938.279 -> sets email to e.target.value
943.019 -> and then likewise we're going to do the
944.699 -> same thing for password right here on
948.12 -> change
949.5 -> set password now we're actually setting
952.8 -> the email and the password as we're
954.36 -> typing into this input group into our
956.519 -> email and we're password state that we
958.38 -> defined in this
960.12 -> sign up component so now we can say we
964.38 -> can provide an on click Handler to sign
966.72 -> up
973.019 -> to call our sign up with email
977.76 -> actually we'll just do on click is equal
980.339 -> to
981.36 -> sign up with email and now when we click
984.959 -> on the sign up button it will
986.279 -> automatically sign us up now let's test
988.199 -> signing up the user if it successfully
989.579 -> signs up it should log that data into
991.44 -> the console right there so let's do
996.36 -> test email and then password will be our
998.759 -> password sign up and awesome we have our
1002.839 -> user ID so we successfully authenticated
1005 -> now we go over to our authentication
1007.279 -> instance now we go over to Super Bass
1009.259 -> and we see that
1012.32 -> if it'll load we're seeing that it's
1014.6 -> still waiting for verification so email
1016.279 -> verification is enabled by default so
1018.8 -> I'm going to go into my email account
1020.259 -> and I'm going to go verify and let's I
1023.72 -> just verified it now we are
1027.38 -> good to go we're fully verified and we
1029.48 -> have an active user great we now have
1032.059 -> authentication done so now our sign up
1034.4 -> is done let's also just create a quick
1036.919 -> little login page so I'm going to create
1039.799 -> a brand new file called login.tsx it has
1044.24 -> to be under the Pages directory because
1045.86 -> once again that is how nexjs handles all
1048.86 -> of the routing so I'm going to copy the
1051.02 -> same exact thing over here basically
1052.82 -> copy this whole sign up file paste it
1055.22 -> into my login file Because the skeleton
1057.08 -> is going to be pretty similar so here
1059.66 -> I'm just going to rename it login and
1062.96 -> rather than sign up with email I'm going
1064.82 -> to update to this to sign in with email
1067.78 -> and then we'll do I think the function
1070.88 -> is called sign in yeah sign in with
1073.16 -> password and we should be good to go
1076.34 -> all right so let's test that out really
1078.98 -> quickly
1080.48 -> log in
1082.22 -> oh sign up with you oh okay oops need to
1085.58 -> update this right here
1088.52 -> sign in with email there we go and let's
1092.9 -> sign in again it was my email address
1097.16 -> and then password
1099.88 -> and yep we are successfully
1102.2 -> authenticated and now we also want to
1103.7 -> update our login page such that once the
1105.559 -> user successfully logs in we want to
1107.36 -> redirect them to our root page our
1109.4 -> localhost 3000 slash page with no extra
1112.22 -> path to it and next.js makes it really
1114.62 -> easy to do that so I'm going to import
1116.419 -> use router from next slash router I'm
1120.02 -> going to use this hooks
1123.2 -> and then all we need to do is once we
1125.299 -> successfully authenticate which is on
1126.74 -> line 19 right here do router.push
1130.52 -> to forward slash now let's
1136.46 -> and then right here let's test it out
1138.08 -> log in again with our user that we've
1140.36 -> been having and then we Oh wrong path
1145.7 -> and good see we automatically redirect
1148.52 -> to the root path so now let's start
1150.38 -> updating this page right here the first
1152.12 -> thing that we need to do is check are we
1153.74 -> authenticated or not and super bass
1156.08 -> makes it really easy to do that I'm
1157.94 -> going to wrap everything in this used
1159.32 -> effect right here because I want this
1161.419 -> code to I want to check if the user is
1164.299 -> authenticated or not once the initial
1166.1 -> page is loaded and the way that you can
1168.2 -> do this is if you pass in this empty
1170.299 -> array in as a second argument in the use
1172.34 -> effect function it only calls this use
1174.74 -> effects once right when the component is
1177.38 -> actually loaded the initial time now the
1179.419 -> way that we're going to do this is we're
1180.559 -> going to be creating a function called
1182.24 -> get user
1183.62 -> going to be an async function and what
1186.2 -> it's going to do is user is going to
1188.539 -> equal to the return response of the
1191.9 -> Super Bass dot auth dot get
1195.44 -> user
1197.6 -> and then let's log the response of that
1202.58 -> and then we also actually have to call
1204.74 -> the function right here and the reason
1206.6 -> why I am defining a separate function
1208.88 -> for this right here is because usefx
1211.22 -> actually can't have async calls directly
1213.98 -> inside of it and right here as you can
1215.72 -> see this has an async call in it so the
1218.419 -> workaround to enable this async call is
1220.52 -> by actually creating an async function
1223.039 -> within the use effect and then calling
1224.84 -> that async function within the use
1226.52 -> effect I'm not sure if that's the best
1228.02 -> way to do it if anyone has any opinions
1229.64 -> on this please leave a comment down
1230.96 -> below I'm always open to learn so yeah
1233 -> let me know I'm not an expert
1234.919 -> so let's call this again and as you can
1238.16 -> see on the page load of localhost 3000
1240.98 -> we are we are successfully getting that
1243.02 -> user return and the user ID is right
1246.679 -> there beautiful so we can see right here
1250.1 -> that we successfully authenticated so
1252.679 -> let's just create a simple state within
1255.5 -> react to save are we authenticated or
1257.48 -> not
1259.76 -> here well let's see let's see what the
1262.76 -> response looks like if nobody is if
1265.64 -> we're not authenticated at all so
1269.179 -> let's see the user object is no okay so
1273.2 -> really simple then
1275.539 -> we can then say if
1279.2 -> we'll create our is authenticated state
1284.39 -> [Applause]
1287.66 -> it's a Boolean and we're going to
1289.82 -> initialize it to false and then if user
1293.72 -> so basically this is checking if user is
1295.7 -> not null or it's not undefined it's not
1297.799 -> falsy
1299.48 -> then set is authenticated to true and
1304.1 -> just for good measure once we start
1305.96 -> saving things into our database once we
1308.12 -> save the actual links within link tree
1310.159 -> we're probably going to want to save the
1312.2 -> user ID as well so that's going to be
1314 -> data user ID so let's set that as well
1319.46 -> um we'll say const user ID is equal to
1322.4 -> data what is again user
1329.419 -> ID
1330.799 -> and then oh sorry it's not together it's
1333.44 -> user there you go oh user of data
1336.98 -> i d what is going on here there's going
1339.74 -> to be
1340.539 -> user.data dot user dot ID
1345.98 -> all right perfect and now let's create
1347.659 -> our state as well user ID set user ID so
1351.559 -> you go to
1353.78 -> I think this yeah it's going to be a
1355.88 -> string
1360.14 -> then we will set
1363.1 -> the user ID accordingly so now we have
1367.159 -> just now we have successfully set the
1369.2 -> authenticated State and the user ID
1371.24 -> State now the next thing that we're
1372.799 -> going to do is we're actually going to
1374.36 -> create a way to for a user for a logged
1377.539 -> in user to create new links so we're
1380.299 -> going to go over into our database right
1382.94 -> here so let's go over to our table
1386.539 -> editor now let's create a new table
1388.36 -> we'll call this
1391.6 -> uh links
1394.039 -> and we're going to add a few columns
1395.78 -> we're going to call we're going to add a
1397.64 -> title
1398.9 -> uh and we're going to add a we're going
1401.96 -> to make this varchar and it'll be null
1405.08 -> initially and then we're going to keep
1406.34 -> this as the URL so once again this will
1409.76 -> be the VAR Char again so what the title
1413 -> is going to be what shows up on the
1414.559 -> button on that link tree component and
1417.02 -> then the URL is going to be where the
1419.419 -> user actually gets redirected to when
1421.52 -> they click on that button now we'll both
1423.62 -> initialize that to null and then we will
1425.96 -> also add in a user ID field which will
1429.26 -> be a string so that we can have so that
1432.62 -> we can tie each link to a particular
1434.9 -> user and that should be it right there
1437.9 -> so let's just create that table super
1440.24 -> simple
1441.38 -> I love Super Bass for this so we have
1443.419 -> successfully created our table right
1445.159 -> here now let's create a way to actually
1447.32 -> create that brand new Link in this table
1451.28 -> so we're going back to our vs code right
1454.28 -> here now let's let's let's judge this
1457.46 -> level let's make this a little bit
1458.9 -> prettier so we're going to create a
1461.24 -> button
1463.159 -> oh okay there we go and once again I am
1466.64 -> not going to create my own button I'm
1468.2 -> just going to go over the Tailwind UI
1469.76 -> and copy this exact button that I use
1471.799 -> for the sign up and login page
1474.2 -> so just paste that again
1477.98 -> right here and then let's refresh that
1480.799 -> go back to our local instance yep we got
1483.919 -> the button text to show let's put this
1485.6 -> in the center just like we did with the
1487.94 -> login and sign up page so let's just
1490.34 -> copy this div
1493.22 -> then paste it
1498.1 -> what's going on oh
1502.76 -> okay now we are finally in the center
1505.28 -> and you know let's add a little margin
1507.919 -> on it as well this class right here mt4
1511.22 -> this is creating a margin tunnel on top
1513.98 -> and four is a set margin Gap that is
1517.7 -> predefined within Tailwind so perfect
1520.28 -> now we have a button showing right there
1523.159 -> let's update the text of this button to
1525.5 -> say add new link
1528.08 -> now let's actually create that add new
1530.36 -> link function to write data into Super
1532.58 -> Bass is also super straightforward so
1534.559 -> right here I'm going to create my
1535.58 -> function add new link
1539.36 -> it's going to be an async function
1541.52 -> and what it's going to do is to and the
1544.46 -> way that you write data is going to be
1546.38 -> like this so
1548.48 -> awaits
1549.98 -> superbase Dot from so you list the table
1552.74 -> and we just created that table right
1555.799 -> here let's look at it again it is the
1557.779 -> links table so just pass in the name of
1560.539 -> that table so from
1563.779 -> links dot inserts and we're going to
1567.5 -> insert the data that we want it that we
1569.6 -> want and let's go back we're gonna pass
1572.059 -> in title URL and user ID the ID field is
1575.659 -> automatically populated for you and if
1578.12 -> you look at our table again you see that
1580.7 -> we provide a default value for the
1583.1 -> created at field as well so we don't
1584.96 -> even have to add we don't so we don't
1586.76 -> have to add that manually it's created
1588.679 -> for us automatically
1590.299 -> likewise if you wanted to you could add
1592.279 -> a default value for the title the URL
1594.919 -> and the user ID but in this case we're
1597.08 -> not going to do that because we're going
1598.159 -> to set those within our actual client of
1600.26 -> the app
1601.58 -> so from here let's uh create that Json
1604.159 -> it's going to be title
1607.159 -> URL and user I
1610.34 -> D yep so title so right so right here we
1614.9 -> can see that we're going to need a title
1616.46 -> and a URL field as well so let's pass in
1619.34 -> title set title let's create a state
1625.7 -> string or undefined Oh cannot spell
1629.86 -> [Applause]
1632.299 -> and then do the same thing for URL oops
1635.539 -> it's use State okay
1638.679 -> so let's pass in the title here
1646.1 -> URL and we have our user ID perfect well
1650.779 -> we also want to make sure that title URL
1653.419 -> and user ID are not null as well so we
1656.299 -> can also add that
1657.86 -> saying if title and URL and user ID
1663.2 -> because once again when you just pass in
1665.24 -> the variable saying if this variable it
1667.52 -> checks if it's truthy or falsy and null
1670.64 -> and undefined are both falsy however be
1673.58 -> careful because if the value is 0 or an
1675.86 -> empty string those are also falsy so
1678.08 -> sometimes those values aren't going to
1679.4 -> be passed in so you want to understand
1680.9 -> that Nuance before using this type of if
1683.48 -> statement so that's it that is how you
1686.179 -> insert that data and then we can then
1688.1 -> say if
1690.799 -> error we want to throw the error
1694.279 -> and we'll then console if not then we'll
1697.039 -> just call to the log the data and see
1699.32 -> what it looks like and to make sure it's
1701.48 -> properly uh caught and to make sure that
1704 -> we properly handle that Throne error we
1705.919 -> want to wrap this in a try
1709.82 -> and attach
1714.62 -> oh man we'll just log the error for now
1718.34 -> and okay we're looking good now we'll
1722 -> actually just pass in the on click on
1725.24 -> this button to actually call the add new
1727.52 -> link function and we also only want to
1729.919 -> show that add new link function if the
1732.2 -> user is authenticated right so it is
1735.32 -> authenticated
1737.36 -> and
1739.64 -> once again this is a little shorthand
1741.62 -> Pro tip if you want to just return if
1744.02 -> you want to conditionally return a
1745.82 -> particular part of the component just
1748.34 -> wrap it in the curly braces and passing
1750.679 -> the very the Boolean and do and then it
1753.86 -> will this function will only return if
1756.32 -> this evaluates to true because this
1758.539 -> component is always going to evaluate
1759.799 -> the true because it is not falseed so
1761.72 -> yeah that is how that's how it all works
1764 -> Pro tip in terms of writing out some
1765.86 -> good and beautiful JavaScript so we have
1770.12 -> the button set up now we just need to
1771.799 -> create the forms for a user an
1773.659 -> authenticated user to add a title and a
1777.38 -> URL
1778.64 -> so let's add that in here I'm just going
1780.98 -> to go over to our sign up page and copy
1783.74 -> over this exact component as you can see
1787.1 -> a lot of components are being reused
1789.02 -> there's no need to reinvent the wheel so
1791.179 -> I'm just going to paste this here the
1793.22 -> area that we're seeing right here is
1794.539 -> that jsx Expressions must have one
1796.46 -> parent element but as you can see there
1798.26 -> are there's no one parent element
1799.82 -> there's two elements so one way we can
1801.98 -> work around that is by wrapping it in
1803.84 -> these empty braces these are react
1806.36 -> fragments that allows you to wrap your
1809 -> components wrap multiple children
1810.5 -> component under one parent component
1812.539 -> without creating an extra node in your
1814.399 -> Dom as if you that would that would
1816.62 -> normally happen if you just wrapped
1817.94 -> everything within a div so
1820.64 -> uh this is going to be type as text the
1824.059 -> name is going to be title ID is title
1829.58 -> the placeholder will be in my awesome
1831.62 -> link
1832.82 -> and we're going to set the on change to
1835.46 -> set title
1836.96 -> now let's copy and paste this same exact
1839.539 -> thing and replicate it for the actual
1844.64 -> URL oh the type is going to stay the
1846.86 -> same as text name will be URL ID will be
1850.1 -> URL
1851.38 -> and for fun I will make the default
1855.32 -> placeholder URL my YouTube channel hello
1858.02 -> yeah make sure to subscribe also by the
1860.659 -> by the way if you're finding this useful
1861.98 -> and informative and you want to see more
1863.24 -> of this type of content definitely make
1864.62 -> sure to subscribe lots more videos like
1866.299 -> this coming soon
1868.58 -> so there we go and we're going to change
1871.1 -> the on change to set URL instead
1874.94 -> so let's see what this looks like on our
1878.059 -> local instance all right looking good we
1880.399 -> can fix the spacing a little bit we'll
1882.26 -> add a little bit more margins between
1884.059 -> all of these as well as we've as we have
1886.399 -> been doing up until now let's increase
1888.38 -> the margin from one to four
1891.5 -> once again these are predefined margins
1893.779 -> that uh Tailwind handles for us
1897.44 -> let's see awesome this looks really good
1899.72 -> you know let's add a little title up
1901.52 -> here to clearly indicate that this is a
1903.62 -> link and this is a URL so luckily we
1906.2 -> have that already right here
1910.64 -> um here we go
1914.059 -> perfect uh and you know we'll just
1916.34 -> change this to a div we don't need a
1918.32 -> label
1920.36 -> there we go
1923.179 -> and this one will be title
1927.5 -> and then let's copy and paste this down
1930.26 -> here
1931.34 -> this one will be URL
1934.52 -> nice we are looking good so let's test
1937.22 -> this out let's do test title and then
1940.52 -> we'll set the placeholder link once
1942.44 -> again to my YouTube channel
1944.539 -> and let's do add new link and let's see
1946.88 -> what ends up logging into the console
1948.62 -> log let's clear this all out first now
1951.559 -> let's do add link
1953.299 -> all right we have an error code
1955.159 -> new row violates row level security
1957.98 -> policy for table links okay so this is a
1961.039 -> good topic to talk about
1962.779 -> so when you create a new table within
1964.64 -> Super Bass they have something called
1966.02 -> row level security that makes that
1968.299 -> basically allows only certain types of
1970.1 -> users or you can create certain sets of
1971.659 -> conditions to let people write into a
1974.12 -> database read from a database update the
1976.279 -> database Etc it's very useful but this
1978.62 -> is a little out of scope for this video
1980.059 -> so I'm going to disable row level
1982.399 -> security
1984.74 -> now we can try that again and it should
1987.14 -> work and would be first a page
1990.919 -> test title
1992.72 -> YouTube channel paste it in add new link
1995.12 -> okay data was null and that means we
1998.419 -> successfully wrote because we didn't get
1999.98 -> an error but a way to make this a little
2002.74 -> bit cleaner just so we can verify what
2004.659 -> data was actually written into it we can
2007.559 -> uh when you insert a when you insert
2010.48 -> into a Super Bass you it automatically
2013 -> does not actually select that data as
2015.039 -> well so what you can do is pass in a
2017.5 -> select function at the very end so then
2019.779 -> this actually returns data to show you
2022.059 -> what new piece of data you just inserted
2024.64 -> into your table so now if you were to
2026.559 -> run this again it'll count to log the
2028.179 -> data it'll actually show the data and
2030.64 -> then rather than just console logging
2032.62 -> null like it did right there so let's
2034.899 -> try it again let's do test title number
2036.94 -> two
2037.899 -> let's clear this out add a new link
2041.26 -> and we've successfully inserted data and
2043.72 -> it shows the title URL and the user ID
2047.08 -> great now we have successfully inserted
2049.78 -> data now we can create code to read the
2051.82 -> data and render our really beautiful
2053.619 -> link tree page to start reading and
2056.139 -> formatting our data let's go over to our
2058.96 -> code editor right here and we will
2061.599 -> create another use effect and with this
2064.54 -> next use effect that we're going to do
2065.8 -> we're gonna this is going to be the part
2067.599 -> of the code where we actually fetch all
2069.58 -> of the existing links for that
2071.26 -> particular user
2073.3 -> so we're going to do use effects
2076.06 -> we're going to call this once again
2077.619 -> we're going to create a separate async
2079.24 -> function because you can't actually do
2080.619 -> async calls directly within use effect
2082.599 -> so we'll call this get links
2089.44 -> links is equal to a weight superbase Dot
2093.28 -> from
2095.379 -> links
2097.72 -> uh um Dot
2101.02 -> fetch
2104.64 -> dot equals
2107.88 -> user ID is user ID
2119.5 -> forgot to add the parentheses right
2121 -> there silly it's not fetch it's select
2123.16 -> my bad
2124.56 -> uh perfect and then we'll once again do
2127.96 -> data and error
2130.359 -> oops
2132.339 -> if error row error
2135.579 -> so let's wrap this in a try catch again
2138.339 -> to properly handle that error that is
2140.859 -> potentially thrown
2143.859 -> catch error
2147.64 -> and then we'll just counter log that
2149.38 -> error for now
2152.32 -> if not we'll have the data and let's
2155.44 -> just console log the data that we get to
2157.78 -> make sure that we are
2160.119 -> properly getting the data
2162.76 -> and we'll do we only want to call get
2165.64 -> links if user ID is not null so we'll do
2169.42 -> if user ID
2172 -> we'll call get links
2174.46 -> and we also only want to call this one
2176.8 -> time so we're going to pass this empty
2178.42 -> bracket at the end
2183.04 -> and let's refresh the page and see what
2185.92 -> ends up printing
2190 -> oh I guess we're not authenticated at
2191.98 -> all no we are okay why is this this
2195.76 -> should be executing then oh sorry this
2198.22 -> one we can't we actually do have to pass
2199.78 -> in a variable in here we have the
2201.16 -> passing user ID and for those of you
2203.26 -> that are new to react what this bracket
2205 -> does is if you start passing in values
2207.579 -> into this bracket it basically says if
2210.22 -> any value if any variable change that is
2212.859 -> passed into this array changes value
2215.16 -> then you trigger this effect so what
2218.5 -> basically what we're saying here is if
2220.48 -> user if the value of user ID changes
2222.64 -> anytime it changes recall this entire
2225.28 -> effect and this is useful because then
2227.44 -> it checks then it'll rerun this check
2229.48 -> every single time and in the case that
2231.4 -> user ID is not null it will then execute
2234.04 -> the get links function so as you can see
2236.56 -> let's try that again
2238.839 -> okay we are getting an error code
2240.82 -> relation public dot links does not exist
2244.54 -> what is going on there oh okay it's a
2247.119 -> lowercase L not a cap I just got signed
2249.7 -> out weird all right so I realized the
2251.98 -> error that I just ran into was the fact
2253.66 -> that links are links table right here
2255.7 -> it's a lowercase L not a capital l so
2258.82 -> let's update that
2260.98 -> make it lowercase L instead
2264.76 -> go back to our localhost refresh perfect
2267.88 -> you just saw it right there yep we are
2269.92 -> properly getting all the data this is a
2271.96 -> great step now let's actually render and
2274.06 -> style this data into some pretty links
2276.16 -> to make it more link tree yes we need to
2278.38 -> set all of our links as well because
2280.119 -> it's one thing to just read the data
2281.68 -> like we're printing it out but we need
2282.82 -> to store it within our component too so
2284.5 -> we'll create a data or lynx
2289.599 -> set links equals use state
2293.5 -> and we'll create a new link type right
2295.839 -> here and it's going to be an array of
2297.52 -> links and then links is going to be
2302.92 -> type with title
2305.2 -> string
2306.76 -> URL screen and that's all we really need
2310.599 -> so that should be okay
2313.06 -> um oh and then
2314.82 -> within the select statement for the get
2316.96 -> links function we don't want to get we
2318.94 -> don't need to get all the fields so we
2320.38 -> can actually just pass in which text
2321.94 -> field we want to get so in this case
2323.44 -> we'll just do title and URL then we can
2327.52 -> do set Links of data and we should be
2331.66 -> good to go there
2335.74 -> perfect and we can check our react uh
2339.22 -> debug tool let's go over to our app go
2342.339 -> to home and this state right here is the
2345.28 -> one that holds all of our links and yep
2347.2 -> successfully we are getting out of our
2349.18 -> links with the correct title and the
2351.28 -> urls
2352.599 -> so let's start making these links really
2355.119 -> pretty so let's do that
2358 -> we are going to then map through iterate
2361.359 -> all the links links.map
2366.22 -> and what are we gonna do okay we are
2368.8 -> going to just
2370.54 -> let's just make this really ugly for now
2373.42 -> and
2378.4 -> for now we'll make this we'll just
2382.359 -> console we'll just create a really Bare
2385.359 -> Bones div that shows the title
2389.56 -> and what is going on here
2392.079 -> why is this oh missing key prop for the
2394.119 -> element iterator right so then we'll do
2396.4 -> index number because once again in react
2399.7 -> you want when you're iterating and
2401.38 -> trying to render an array of components
2403.119 -> you need to pass in a key so key is
2405.64 -> equal to index
2407.02 -> perfect so we will see if this is
2410.14 -> properly rendering yeah we are getting
2412.66 -> the correct titles a test title and test
2414.7 -> title number two so we're gonna have to
2416.92 -> do a few things now we're gonna need to
2418.96 -> do an on click function such that once
2421 -> the user actually clicks on this link it
2422.74 -> redirects them to the link that is
2424.24 -> underlying and this is really simple you
2426.46 -> can just do pass into on click
2431.2 -> and let's do e dot prevent default to
2433.839 -> prevent that page load again and then we
2435.88 -> can just do
2437.7 -> window.location.href is equal to link
2440.38 -> dot URL and that's all you need to do so
2443.2 -> that when a user clicks on the link they
2445.24 -> will then get redirected to that page
2446.92 -> and we can test that out right here I'm
2448.599 -> just going to test on this title
2450.64 -> and I am successfully getting redirected
2452.98 -> to my YouTube page which you should
2455.079 -> totally check out and subscribe which if
2456.94 -> you have not already
2458.619 -> Shameless plug
2460.66 -> so let's go back
2463.9 -> and
2466.18 -> let's start smiling this a little bit so
2468.76 -> let's start passing in some Tailwind
2471.4 -> class names so the first thing that I
2473.5 -> want to do I want to add a little bit of
2475.599 -> Shadows so let's go to Tailwind let's do
2479.859 -> box Shadows
2481.96 -> yeah and they have some really great uh
2484.96 -> predefined Shadows that look perfect for
2486.7 -> you because I know when I would do this
2488.38 -> manually it would look uh horrible so
2491.02 -> I'm gonna do let's do Shadow XL that one
2493.42 -> looks pretty good so we'll just pass in
2495.28 -> Shadow XL
2497.92 -> let's see what it looks like there all
2499.78 -> right looking better already but
2501.16 -> obviously super small so let's increase
2503.92 -> the width so let's go over to width uh
2506.98 -> there are some predefined widths here
2508.599 -> Let's do let's do with w20 let's see
2511.9 -> what that looks like
2519.22 -> okay
2520.42 -> still pretty small uh let's make that
2522.46 -> even bigger uh let's see let's go
2526.079 -> w96 really big one let's go 96.
2530.619 -> let's see what that looks like all right
2532.599 -> we're getting better now so let's
2534.76 -> increase some padding because I also
2536.92 -> just for testing purposes I'm gonna pass
2539.02 -> in a background color just so we can
2540.64 -> actually see how big or how small the
2542.32 -> component is because it's kind of hard
2543.46 -> to see white on white and the way that
2545.32 -> you do that is do something like BG
2547.18 -> background uh Indigo 500
2551.98 -> okay
2553.06 -> so these are our two links let's add a
2555.22 -> little margin on them as well because
2556.96 -> it's hard to tell okay so that is what
2559.78 -> our links currently look like I think I
2561.88 -> want to add some padding and round the
2563.859 -> corners a little bit so let's add that
2566.38 -> padding we'll do key four
2569.14 -> which is once again a predefined padding
2571.359 -> that you Tailwind has created for us and
2574.54 -> you can see it's right here
2576.66 -> P4 where are you it's going to be
2579.78 -> something like this uh it's gonna be 16
2582.76 -> pixels of padding so once again these
2584.319 -> are all predefined Tailwind CSS classes
2586.66 -> I know the learning curve is not it's a
2588.52 -> little high but once you memorize your
2589.9 -> classes it's wicked fast so now we got
2592.66 -> the padding good now we want to add a
2595.06 -> little bit of the rounded Corners as
2596.56 -> well so let's look at the border radius
2598.42 -> class on Tailwind
2600.88 -> uh perfect once again a lot of
2602.74 -> predefined ones I'm going to do let's do
2605.8 -> round and large this the corners are
2607.839 -> pretty satisfying to look at so we'll
2609.7 -> just pass in the rounded large Plus
2614.44 -> look at that looking better already oh
2616.78 -> actually yeah
2618.76 -> so from here let's also Center the text
2621.04 -> as well so on Tailwind let's see uh text
2624.579 -> Center text aligning is just text the
2628.48 -> center so I'm going to pass in the text
2631.359 -> center field
2634.119 -> look at that we are looking good
2635.8 -> although the black text is a little hard
2637.66 -> to read so let's text it let's change
2639.099 -> the text color to white
2640.78 -> let's look up text color and it's just
2643.119 -> text white is the CSS is the class name
2646.9 -> pass that in
2649.119 -> perfect this is looking really really
2651.339 -> good now we are so we're almost done
2653.38 -> we've been able to create our links
2655.119 -> we've been able to render them one more
2656.98 -> thing that we need to do though is right
2658.359 -> now when we add a new link we don't
2660.099 -> automatically render it we need to
2661.72 -> refresh the page to show that new link
2663.4 -> so let's add that functionality as well
2665.319 -> so when we're adding the new link we can
2668.02 -> then just we already get the data right
2670.48 -> here we're printing the data now all we
2672.28 -> need to do is update or set our new
2675.16 -> links to include that brand new return
2677.92 -> data so we'll do set links and we
2680.92 -> probably want to render this new data at
2683.079 -> the very top of the list rather than the
2684.579 -> very bottom so we'll do the Sprint
2686.319 -> JavaScript spread operator on data
2689.98 -> and then length what this does is this
2692.8 -> deconstructs the entire array and this
2695.68 -> basically and then this basically just
2698.14 -> combines all the elements within the
2700.119 -> data array that we just returned from
2702.339 -> inserting that brand new link and all of
2704.56 -> the existing links and combines it into
2706.42 -> one gigantic array oh okay the issue is
2708.88 -> that links might be undefined so we'll
2711.16 -> just verify if links
2715.119 -> there we go so let's add a new link test
2718 -> title number three on this case I will
2721.3 -> add my Instagram page
2724.48 -> let's do test Instagram
2728.02 -> add the link
2729.46 -> look at that and it's right there as
2731.68 -> well the next part that we want to do
2733.24 -> here is we want to add a way to upload
2735.52 -> an image to the super based storage and
2737.5 -> render it right there uploading images
2739.18 -> is its own beast and its own and you're
2741.28 -> not going to create it from scratch and
2742.66 -> I'm actually just going to use this
2743.98 -> pre-created package react images
2745.78 -> uploading so let's go and install this
2749.44 -> with npm so let's do npm install react
2752.98 -> images uploading let's do that right
2755.5 -> here
2763.48 -> okay we are good to go let's start
2765.76 -> importing this into our component right
2767.8 -> here and there's gonna be a lot of
2769.48 -> boilerplate code so don't be too afraid
2771.88 -> honestly I don't really know exactly
2773.56 -> every detail of what's going on I just
2775.24 -> followed the tutorial on the GitHub
2776.859 -> repository their example so it's pretty
2779.2 -> straightforward the way that we're gonna
2780.52 -> do this is we're going to be importing
2782.079 -> the image list
2784.18 -> type
2785.98 -> from react images uploading
2789.4 -> and then from here we're going to create
2791.619 -> a state called images set images equals
2796.24 -> to use State uh image list type
2800.92 -> and then we'll set it as an empty array
2802.54 -> for now so now let's go over to actually
2805.599 -> creating that upload image portal and
2808.599 -> once again this is just a lot of
2810.28 -> boilerplate code so do not be too afraid
2813.579 -> of what you're gonna see right here uh
2816.22 -> because once again I don't really know
2817.48 -> all the details of what's going on on
2819.28 -> this either it's just example
2820.96 -> boilerplate code that the example had on
2823.48 -> their GitHub so let's once again pass it
2826.06 -> into this is authenticated portion
2828.819 -> uh we'll add this underneath the button
2833.5 -> okay oh no
2836.02 -> um um let's see I'm just copying this
2839.079 -> from a previous project that I had
2840.88 -> created before
2843.339 -> okay
2845.5 -> now this is all the boilerplate code I
2847.96 -> know it looks like a lot and it kind of
2849.88 -> is a lot but it's not that bad and let's
2853.42 -> actually wrap this up just so we can
2855.099 -> clearly see the sections that we're
2857.079 -> working with so we'll do we'll wrap the
2860.26 -> new link creation stuff and we'll add a
2863.02 -> header saying new links
2866.8 -> creation
2869.319 -> and then we're going to do the same
2870.52 -> thing for the image uploading we'll wrap
2873.16 -> this
2874.9 -> in another div as well and call this
2877.18 -> section
2879.4 -> uh
2880.9 -> image uploading
2886.78 -> and then we also need to import the
2888.46 -> image uploading component so we're going
2891.4 -> to import it here image uploading
2895.48 -> all right we should be good to go and
2898.3 -> now we also need to pass in and on
2900.22 -> change function and what on change
2902.26 -> function does it's basically when a new
2904.06 -> image is uploaded how exactly do we
2905.859 -> handle it and the way that we're going
2906.94 -> to handle it is we're going to set the
2909.04 -> state of our images right here and what
2911.56 -> that's going to look like is pretty
2912.7 -> simple we're going to call it on change
2914.38 -> it's equal to it takes an image list
2917.26 -> type as an argument and then we're going
2920.14 -> to set the images states with that brand
2923.079 -> new image list that is passed in all
2925.24 -> right so we're getting some type errors
2926.74 -> here but implicitly binding element
2929.16 -> binding element on image upload
2931.119 -> implicitly has an
2933.22 -> any type
2935.02 -> that's fine we'll just ignore this type
2936.76 -> error for now it's not that uh important
2939.16 -> let's go back to our Local Host
2941.74 -> perfect you see right here the image
2943.72 -> uploading full uh dialog is right here
2946.66 -> so I'm just going to upload some random
2949.78 -> let's say
2952.96 -> we'll say uh this there's like an old
2955.599 -> YouTube thumbnail that I used oh we're
2957.28 -> actually so we successfully uploaded the
2959.38 -> image but one part I forgot to do is
2961.18 -> show the preview of that image so let's
2963.819 -> add that function really quickly and the
2965.74 -> way that we're going to do that is we're
2966.76 -> going to use next js's image class that
2969.339 -> they already have for you to find for us
2972.339 -> so we just imported it right there we're
2975.099 -> gonna go down and we want to show our
2977.619 -> image preview probably Above This
2980.02 -> removal images button or where we just
2982.06 -> uploaded our brand new image so we are
2985 -> gonna go here
2986.74 -> and then we are going to create we're
2989.26 -> going to then render our brand new image
2990.88 -> that we just uploaded to use the next JS
2992.8 -> image component we need to pass in a few
2994.48 -> props the first one is going to be the
2996.099 -> actual source of the image that we're
2997.42 -> rendering in this case it's going to be
2998.92 -> the images array we're going to get the
3000.66 -> very first element in there because
3001.74 -> there's only one element in there and
3003.54 -> then we're going to get the data URL
3006.359 -> and then for next year is we also have
3007.98 -> to add in the height the width and the
3009.78 -> alt text as well so in this we'll just
3011.94 -> make a height 100 we'll also make the
3014.04 -> width 100 and we'll just call the alt
3016.5 -> text uh profile picture
3019.859 -> oh what is going on here why is this
3022.38 -> string is undefined is not unassignable
3025.26 -> oh okay uh for this one we'll just call
3028.44 -> it
3029.4 -> I think this will work
3031.74 -> okay that's better
3034.26 -> uh great now we're showing the image
3036.78 -> preview of our profile picture and now
3039.3 -> we can actually upload it as well into
3041.52 -> Super Bass and the way that we can do
3043.859 -> that is we will also create a new button
3045.66 -> to actually upload that image
3050.579 -> so we will then add that in right here
3056.22 -> and we'll call this
3058.319 -> upload profile picture
3061.559 -> and create a new function called upload
3063.42 -> profile picture
3065.7 -> and let's define that function up here
3073.26 -> it's gonna be an async function so to
3075.9 -> upload our profile picture we first need
3077.819 -> to only upload the picture if our image
3080.16 -> is length if our image is State actually
3082.44 -> has an element in there so we're going
3083.88 -> to do if images dot length is greater
3086.339 -> than zero
3087.839 -> then we want to get the very first image
3089.88 -> and the only image that we have uploaded
3091.68 -> right there and we're going to store it
3093.359 -> in our variable called image
3095.819 -> then we want to check if image.file and
3098.76 -> if the user ID is defined because we're
3100.92 -> going to be uploading this profile
3102.42 -> picture and keying it by that specific
3104.579 -> user ID
3106.68 -> so then we so now we'll actually call
3109.079 -> Super Bass to upload the image ourselves
3111 -> cons data error is equal to await Super
3115.079 -> Bass dot storage
3117.72 -> Dot from public
3120.839 -> and then we're going to upload it and we
3122.579 -> want to make a unique folder based on
3125.04 -> that user ID as well just so we can
3126.839 -> organize our uploads a little bit so
3128.88 -> we're going to go from public upload and
3131.88 -> then the path that we're going to do is
3133.44 -> we're going to pass in
3135.48 -> the user ID
3138.72 -> slash the image dot file dot name
3145.2 -> and we'll also set upsert to true
3148.38 -> and the reason why we want to set upsert
3150.119 -> the true oh we need to wrap that in
3151.8 -> Brackets the reason why we want to set
3153.78 -> up search to True is because if we don't
3155.52 -> and we have a duplicate I mean and then
3158.52 -> if we upload a file that has a duplicate
3160.26 -> name it actually causes a collision and
3162.059 -> we actually don't end up uploading it
3163.5 -> uploading the new file with a new name
3165.3 -> in this case we will override the
3167.4 -> previously existing file with a new file
3169.319 -> with that same name oh I forgot to add
3171.66 -> in an extra argument the second argument
3173.4 -> is actually supposed to be the actual
3174.72 -> file itself and then the third order and
3177.42 -> then the third argument is that Json
3179.16 -> with upsert set to true
3181.44 -> now once again like we've been doing
3183.42 -> this entire time let's wrap this in a
3185.4 -> try catch statement because we're about
3187.14 -> to throw an error if something goes
3189 -> wrong oh sorry about that
3190.98 -> yeah try catch
3193.26 -> error
3194.819 -> console
3196.559 -> log error
3202.44 -> so then
3203.579 -> if error throw error
3207.24 -> now let's now the data is going to have
3209.76 -> that successful response of the
3212.52 -> successfully uploaded image but now we
3214.319 -> also want to get our public URL for that
3217.2 -> data that we just uploaded because if
3218.88 -> you want to then store that profile
3220.5 -> picture image into our table and now we
3223.98 -> want to store that profile picture into
3226.319 -> a table can with that contains that user
3228.9 -> information we're going to do this like
3231.3 -> this we're gonna we're then going to
3232.559 -> execute this line of code right here
3233.88 -> where we're actually creating the
3236.42 -> superbase.storage.frompublic and we're
3238.38 -> actually trying to get the public URL
3239.88 -> for the data that we just uploaded
3242.7 -> because data is corresponding to the
3245.16 -> successful data that we just got
3246.54 -> returned from uploading that image to
3248.4 -> Super Bass so this is guess this just
3250.38 -> retrieves us that public URL that's all
3252.599 -> it does and then now we want to actually
3254.099 -> create a separate users table such that
3256.619 -> let me such that a whole is not only
3258.66 -> that user's ID but also stores that
3261 -> profile picture URL in it so we haven't
3263.579 -> made that table yet but I'm about to so
3265.319 -> the way that we can do this is constant
3267.18 -> updated
3268.859 -> cons dot update user response is equal
3273.3 -> to superbase Dot from the table is going
3277.2 -> to be called users and then we're going
3279 -> to update the profile picture URL
3281.579 -> property which we haven't created yet
3283.079 -> again but we will right after I do this
3285 -> code and then we're going to set it to
3286.74 -> the public URL and we want to update it
3289.559 -> where the user's ID
3292.619 -> is equal to the user ID that we're
3295.02 -> currently working with
3297.54 -> so once again if update user
3301.559 -> response.error throw error
3304.92 -> and then oh gotta do a weight my bad
3308.28 -> yeah so if the error is there then we
3310.859 -> throw the error if not then we are good
3314.579 -> to go and the profile picture has
3316.079 -> successfully been uploaded so now let's
3318.3 -> go over back to Super Bass and create
3321 -> our users table so we will call this
3323.579 -> users
3325.319 -> and then this is the ID and we're going
3328.619 -> to make the ID a foreign key so we're
3331.2 -> going to set the table reference to auth
3333.66 -> or is it users oopsies auth users where
3339.119 -> are you where are you auth users so the
3342.3 -> column and then we want to select the ID
3345.359 -> of the users table so this users table
3348.18 -> right here is actually referring to a
3350.52 -> pre-create a pre-defined table within
3352.44 -> Super Bass that holds all the
3354.24 -> authentication info for that user
3356.579 -> however for us if we want to store a
3358.98 -> profile picture and tie it to a a user
3362.64 -> ID we actually can't modify this
3364.619 -> existing table we have to create a
3366.24 -> separate users table which is what we're
3367.92 -> doing right now so we're going to do
3369.78 -> that right here
3371.88 -> and then we're also going to create a
3374.52 -> profile picture URL View
3378.599 -> set this to a VAR Char because this is
3381.42 -> just going to be the this is going to be
3383.339 -> the string to the public URL that
3385.02 -> actually holds that profile picture
3387.24 -> image
3389.4 -> great now we have that table and let's
3391.5 -> disable row level security once again
3395.88 -> got it now we need to do two things we
3399.54 -> actually need to make it such that when
3402.42 -> we
3403.38 -> site when we sign up for a brand new
3405.839 -> user we also want to create a new entry
3408.9 -> in that user table we just created so
3411.78 -> let's create going back to our sign up
3413.52 -> page
3414.359 -> let's go and
3416.7 -> let's create a function called create
3418.68 -> user
3419.4 -> [Applause]
3420.599 -> create our try catch as always
3427.68 -> and then we'll just log the error if we
3429.78 -> see one
3431.579 -> and then from here we're going to do
3435.2 -> uh pass in the user ID that's a
3439.26 -> parameter and then we're going to be
3440.94 -> calling this after we successfully sign
3442.68 -> up for the user so we're going to do
3444.319 -> await create user
3447.3 -> user ID
3449.64 -> from here we'll do
3452.099 -> data error is equal to actually we don't
3455.099 -> need data for this one we'll just do
3456.3 -> error is equal to await Super Bass Dot
3459.839 -> from
3460.859 -> users dot insert
3464 -> user ID set it to user ID and then for
3468.839 -> we are not going to set the profile
3470.46 -> picture URL because we're not going to
3472.92 -> set the profile picture URL field
3474.599 -> because when we go over to our table
3476.22 -> schema again
3478.859 -> we can see that we provide the default
3480.72 -> value to be null and that's how we want
3482.76 -> it to be we just want to set the user ID
3484.8 -> and that's it
3486.839 -> so this should be good to go yep
3492.24 -> um oh and then we had to do a little
3494.52 -> type checking if user ID
3499.2 -> perfect so that should be good to go if
3502.74 -> oh right here if error row error
3507.3 -> so now let's go back and I'm going to go
3509.64 -> to my database and I'm actually just
3511.14 -> going to delete this user because so we
3513.359 -> can test the sign up process again
3516.24 -> believe the user let's go to the table
3518.339 -> let's go to Links let's delete all these
3521.099 -> links as well
3525.24 -> now we will go back and sign up for a
3528.299 -> new user so let's go
3530.64 -> sign up
3532.38 -> do the same one same email address
3538.619 -> so we should have signed up now I'm
3541.319 -> going over to my email I'm not going to
3542.579 -> show you my email screen
3544.079 -> um because I want some privacy but I
3545.88 -> successfully got my confirmation email
3547.319 -> and press confirm nice so I just
3550.079 -> confirmed my email let's go over to our
3552.359 -> login let's sign in again yeah this is
3554.76 -> the login page I need to change the sign
3556.2 -> up button text and then when you log in
3558 -> oh we are getting an error right here
3560.22 -> saying cannot read properties of
3561.839 -> undefined reading data URL so basically
3565.68 -> right here the issue that's happening is
3568.619 -> that we are trying to render the
3572.52 -> what line is that uh 158 yeah the issue
3576.18 -> that we're running into here is we're
3577.319 -> trying to render the data URL of an
3579.059 -> image but then the thing is no images
3580.619 -> have been set yet so that is why this is
3582.96 -> throwing an error so let's wrap this in
3585.24 -> a with a little bit of validation to
3586.98 -> prevent that let's do
3589.26 -> if images dot length is greater than
3592.68 -> zero
3595.98 -> perfect
3597.92 -> so awesome we are successfully
3600.42 -> authenticated let's check our database
3602.16 -> again
3604.079 -> um
3604.859 -> and we successfully hopefully
3608.46 -> we did not create our users successfully
3611.4 -> dang so I found out the reason why that
3613.619 -> new user entry was not created was
3615.42 -> because I actually created the wrong
3616.74 -> field so previously this was user ID but
3620.7 -> if we actually look at our schema for
3623.4 -> the table in the users table
3628.14 -> it's actually ID so not user ID so just
3631.319 -> switching this from user ID to ID let's
3633.839 -> say such makes it such that you
3635.339 -> successfully create your brand new user
3637.619 -> in your users table as well so now when
3639.599 -> we actually go through this profile
3641.099 -> picture uploading process it should be
3643.26 -> able to update the actual entry in the
3645.299 -> user's table
3646.74 -> so let's try that out so I'm going to do
3651.9 -> go back to our Local Host instance we'll
3654.599 -> go to just the index the root directory
3657.359 -> because I'm signed in I'll upload a new
3660.119 -> image let's just you know let's make my
3662.22 -> photo the LinkedIn photo and the upload
3664.44 -> profile picture
3666.14 -> and oh we got a error the error that
3669.96 -> we're seeing right here is actually on
3671.52 -> the storage tab of
3674.339 -> uh Super Bass so let's create a new
3677.819 -> bucket called public
3680.099 -> making a public bucket before we can
3682.2 -> actually start uploading your images
3683.4 -> into Super Bass you actually need to
3685.44 -> create your bucket which I created right
3687.18 -> here just called it public and you
3688.98 -> actually have to add a policy as well
3690.96 -> and specifically right here other
3692.46 -> policies under storage dot objects
3695.28 -> you want to create a new policy now
3697.26 -> normally you want this to be pretty
3698.819 -> strict you don't want anybody to be able
3700.559 -> to upload images but for a personal
3702.96 -> product like this one for a quick little
3704.52 -> tutorial
3705.78 -> you can just do allow all operations and
3708.78 -> then for the using expression we'll just
3710.64 -> do true with check expression we'll also
3713.28 -> do true
3714.54 -> and what this is doing is you're
3716.22 -> basically allowing any user to perform
3718.079 -> any type of operation on your storage
3720 -> bucket upload delete update all of that
3727.619 -> now once again this is not something you
3729.18 -> want to do in an actual production grade
3730.44 -> application because then you make your
3732.54 -> application really vulnerable for anyone
3734.76 -> to modify your data when you don't
3736.319 -> really want that
3738.359 -> so from here let's test this out let's
3740.28 -> do upload profile picture
3742.16 -> and no error messages we are good to go
3745.38 -> let's check our storage bucket again to
3747.48 -> make sure that it properly uploaded yep
3750.299 -> storage objects right there click on
3752.76 -> that look at that the uploaded out
3754.98 -> profile picture into our storage bucket
3757.319 -> now let's actually check our user table
3759.42 -> to make sure that the profile picture
3762 -> has properly been updated and as you can
3763.92 -> see right here it has we have uploaded
3766.5 -> the profile picture URL with that brand
3768.78 -> new image the LinkedIn logo that we just
3771.119 -> uploaded so now we go back to our Local
3773.94 -> Host we are now able to upload a profile
3777.54 -> picture and we're now also able to
3779.04 -> create a new link so now all that we
3781.98 -> need to do left is show that profile
3784.44 -> picture at the very top of the page and
3786.54 -> that's super easy because once again
3787.98 -> we're just going to be using the next JS
3791.04 -> pre-created image class
3794.22 -> so let's go back to our index.tsx file
3796.7 -> and actually right here let's let's
3798.78 -> create a couple links because this is
3800.16 -> looking a little empty YouTube channel
3803.579 -> and pass in my YouTube channel URL
3807.54 -> and then we'll also make my Tick Tock
3809.819 -> URL right here
3812.94 -> make sure to follow me if you want so
3814.619 -> just copying the URL to my tick tock
3817.2 -> so
3818.64 -> create another one tick tock
3822 -> Channel
3824.22 -> and then we'll also create an Instagram
3825.98 -> which is once again right there your
3829.079 -> average Tech Pro across all the
3830.46 -> platforms
3831.9 -> let's do Instagram page and then add
3835.859 -> that as well perfect so now all we need
3837.839 -> to do is show the brand new uploader
3839.64 -> profile picture up there
3841.559 -> and the way that we can do that is we
3843.119 -> are going to have to create another use
3844.98 -> effect because previously when I use
3847.14 -> effect we got all of our links but then
3849.059 -> we just created a new users table so now
3851.22 -> we need to do the same exact thing for
3852.78 -> links with our user so we can get the
3854.94 -> profile picture URL so I'm going to do
3857.46 -> use effect again
3860.339 -> we're going to cons get user
3864.119 -> async
3867 -> and then try attach again you know the
3870.119 -> drill at this point come on now
3872.819 -> console log error if we run into one
3877.02 -> then we will do
3881.28 -> the weight Super Bass Dot from user is a
3885.54 -> user or users is it with an s yes it is
3888.54 -> with an s
3889.619 -> so users
3891.96 -> dot select
3893.7 -> and then we'll just get the profile
3895.319 -> picture URL that's all we really want
3897.839 -> where it is equal such that the ID is
3901.26 -> equal to the user ID that we have
3905.28 -> and we'll also only call this if ID is
3908.339 -> not falsy
3910.44 -> oh notice
3912.059 -> user ID is not falsy
3917.28 -> and then if we have an error we throw
3923.4 -> the error if not we'll get the uh
3929.52 -> profile picture URL
3931.859 -> data of
3934.619 -> profile picture URL and then we'll do
3938.16 -> set profile picture URL to that new Leaf
3942.599 -> fetched profile picture URL right there
3945 -> now we need to set that profile picture
3947.04 -> URL state
3948.72 -> your url set profile
3952.98 -> equals use States
3955.02 -> string
3956.599 -> or undefined
3960 -> awesome
3962.28 -> now where were we
3964.68 -> um use effect use effect right here get
3968.28 -> user we set the profile picture URL I
3970.44 -> think it is returned
3972.42 -> now if you want to do if
3975.119 -> user ID
3977.099 -> call get user and once again just like
3979.619 -> the get links effect we're going to pass
3981.48 -> in the user ID into the dependency array
3986.52 -> and we'll actually then we should be
3987.839 -> able to remove this check right here
3989.28 -> because we're already checking for user
3991.26 -> ID before we actually call the function
3994.2 -> now that we have that what we need to do
3997.079 -> is at the very top right here just
3999 -> render that good old
4001.28 -> uh profile picture that we just uploaded
4003.079 -> so we're going to see the source and
4004.46 -> this one is going to be
4006.799 -> um
4007.46 -> the profile picture URL and actually
4010.28 -> before we even do that
4013.099 -> we want to wrap this so that we only
4014.9 -> render the image if profile picture URL
4017.18 -> is not null or undefined so we'll do
4019.7 -> profile picture URL
4023.299 -> and that
4026.18 -> and then we also need a pattern to alt
4028.039 -> it's a profile picture
4030.859 -> and a height
4032.299 -> past 100 and
4035 -> with will also make that 100 and we'll
4038.18 -> make the Border radius
4040.4 -> let's check the Tailwind documentation I
4043.88 -> want to make it a complete circle
4046.28 -> it's going to be rounded full that's the
4049.099 -> class name to make it a complete circle
4052.22 -> so let's do that
4055.16 -> go to our local instance aha we have an
4057.74 -> error oh so this is a common error with
4059.78 -> next.js whenever you're trying to render
4062.359 -> an image using the nexgis component you
4064.7 -> actually need to Output the URL of the
4068.119 -> image of the host name into an allow
4070.4 -> list so right here we're going to copy
4072.98 -> this right here because this is a host
4075.02 -> name that needs to be added into the
4076.52 -> allow list
4077.839 -> go over to our
4080.02 -> next.config.js and then we need to pass
4082.16 -> in in images
4084.14 -> field and then do domains
4087.02 -> and then add that domain into the allow
4089.299 -> list and then when you do this you
4090.74 -> actually need to recreate your app again
4092.42 -> so we're going to cancel it call npm run
4095.48 -> Dev once again
4098.179 -> now let's refresh the page and look at
4102.199 -> that we still need to find a way to
4103.94 -> render all of the uh actual data that we
4107.42 -> just created of the user we just created
4109.699 -> as well so this is going to be a little
4113.299 -> so a way to do this is we're going to
4115.46 -> set a username actually kind of like how
4118.04 -> linktree is gonna it's always like link
4119.66 -> tree slash your average Tech bro we're
4122.299 -> going to do something similar here and
4123.799 -> the way that we're going to do that is
4125 -> we're going to create a new folder here
4126.92 -> called
4129.02 -> oh call creator
4132.679 -> slug
4134.179 -> and that index file that we just created
4136.1 -> we want to move it into Creator slug
4138.739 -> instead the reason why we're doing this
4141.259 -> is yeah we want to update all the
4142.819 -> Imports the reason why we're doing this
4144.44 -> is because we want to render each
4145.819 -> profile as localhost 3000 slash a
4150.02 -> specific username now in this case we
4151.699 -> need to update our sign up page to also
4154.4 -> include a username as well so we'll just
4157.52 -> copy all this
4161.06 -> set username
4163.819 -> then we'll create a new form here
4167.9 -> oh we'll copy this over 70.
4177.14 -> username
4182.239 -> username
4184.219 -> type is text
4186.259 -> username username and username we'll
4191.06 -> make the placeholder your average Tech
4193.4 -> Pro and we'll just set username there
4197.66 -> and then we're going to be calling this
4200.06 -> username when we're creating our user
4202.219 -> entry so we're going to pass in username
4205.28 -> there's username
4207.199 -> now we also want to update
4209.84 -> our users table
4212.42 -> to include that
4216.56 -> username
4218.54 -> text field as well once again we'll set
4220.64 -> it to a far char
4222.26 -> there we go good to go
4224.739 -> so to test this again we are just I'm
4228.02 -> just going to delete this user right
4230.36 -> here
4232.52 -> and while we're at it you know we can
4234.44 -> clear out the uh the links table too
4237.739 -> delete those rows as well
4241.04 -> so now let's go back
4243.26 -> and go sign up
4247.04 -> and then we'll sign up
4251.6 -> as we have been up until this point
4254.96 -> and this one will be your average Tech
4257.239 -> Pro
4258.739 -> sign up again
4261.199 -> then I just got the email to confirm my
4264.38 -> sign up and then if I
4269.3 -> log in
4271.34 -> well in this case for my login page
4274.36 -> previously we were redirecting to the
4276.62 -> dashboard but we no longer have a page
4278.06 -> at that path anymore so we'll just hard
4279.92 -> code this to be your average Tech bro
4283.4 -> and then I'll go log in
4287.96 -> and we'll just log into my account
4292.4 -> and then password
4297.44 -> okay
4298.54 -> uh Super Bass clients we have an issue
4302.239 -> there
4303.44 -> but that is a pretty simple one we just
4305.659 -> have to update the path
4308.78 -> there we go okay so now we are able to
4311.6 -> create our links again
4313.82 -> one or a YouTube channel
4321.38 -> and we'll do Instagram
4322.98 -> [Music]
4327.199 -> and then we'll do our Tick Tock again
4330.44 -> Tick Tock add that link will also then
4334.699 -> upload a new profile picture rather than
4337.04 -> the LinkedIn logo we'll make it one that
4338.96 -> actually is I guess
4341.54 -> ah yeah we'll just do the LinkedIn logo
4343.52 -> again I don't have a better picture on
4344.78 -> me right now we'll upload that picture
4348.38 -> uh and then right now we have to update
4351.92 -> our index.tsx file one more time the
4354.92 -> last update such that uh when we set the
4358.219 -> user ID uh we actually want to do get
4363.5 -> user and rather than matching it by the
4365.6 -> ID we actually want to match it by the
4367.58 -> username now so
4369.8 -> and the username is going to be this
4372.56 -> little path right here your average Tech
4375.02 -> Pro and the way that we can get that is
4376.64 -> we'll just be using this uh router again
4379.58 -> from next.js so we'll do import use
4383.84 -> router
4385.76 -> from next
4387.8 -> router
4390.199 -> router
4392.36 -> and then we'll do const U
4396.199 -> H cons router it's equal to use router
4400.28 -> and then to get the actual uh slug right
4403.64 -> here you see that we created this page
4406.219 -> called Creator slug so this is going to
4409.4 -> be a part of so then this field right
4412.28 -> here your average Tech bro this is going
4414.679 -> to be the query the parameter name for
4417.32 -> this is going to be Creator slug
4420.98 -> so up here we'll do uh const Creator
4426.44 -> slug is equal to router.query
4430.52 -> now we have the creators log this is
4432.26 -> going to be your average Tech Pro so
4434.179 -> from here we're going to be match equal
4435.62 -> matching it from username
4437.9 -> is going to be equal to Creator slug
4442.94 -> and that should be and then we'll be
4445.52 -> setting the I know now we actually do
4447.62 -> need to get the user ID and then set the
4450.62 -> user ID
4452 -> to
4454.8 -> [Applause]
4458.06 -> user ID right there
4460.219 -> oh one last thing is we actually just
4462.08 -> need a previously when we were getting
4463.76 -> the user we were only getting the user
4465.5 -> if the user ID was defined but in this
4467.659 -> case we are not going to be doing that
4469.219 -> we're only going to be getting it if the
4471.32 -> username is defined instead
4474.44 -> [Applause]
4476.06 -> or sorry if the Creator slug is defined
4483.56 -> and then there we go this is our link
4485.54 -> tree clone so this is the
4487.1 -> non-authenticated version as you can see
4488.78 -> it's on the incognito window it gets all
4490.82 -> of our URLs and it renders our profile
4492.92 -> picture right there
4494.96 -> and then this is and then this is the
4497 -> authenticated version where if you are
4498.56 -> actually logged in as a user you can
4501.08 -> then create your new links you can
4502.58 -> upload your new profile pictures there
4504.739 -> we definitely went over a lot of
4506.36 -> information we created a full stack
4508.28 -> application using Super Bass not only
4510.44 -> their database but their authentication
4512.3 -> as well as their storage and we did this
4514.88 -> all using tailwind and react and next.js
4517.76 -> so a lot of information here hope you
4519.92 -> enjoyed the video and once again if you
4521.42 -> found it useful informative and helpful
4523.159 -> make sure to subscribe to the channel
4524.78 -> and give this video a thumbs up if you
4526.4 -> can thanks again for everything and I'll
4528.02 -> see you in the next one
Source: https://www.youtube.com/watch?v=Pbr7M4c9O3Q