1 00:00:02,100 --> 00:00:03,360 So we achieved a lot, 2 00:00:03,390 --> 00:00:05,460 we're able to write data, fetch data, 3 00:00:05,460 --> 00:00:08,190 use our token to scope data or to scope 4 00:00:08,190 --> 00:00:10,950 products and orders to our currently logged in user, 5 00:00:10,950 --> 00:00:12,610 we are able to login and so on 6 00:00:12,620 --> 00:00:14,280 and that's all nice. 7 00:00:14,280 --> 00:00:20,130 One thing we're not doing right now is we're not persisting our sessions 8 00:00:20,130 --> 00:00:23,970 so to say. If I reload, I always have to login again and 9 00:00:24,030 --> 00:00:25,230 that's not really what I want, 10 00:00:25,230 --> 00:00:31,170 it would be nice if we could automatically login if we do have a valid token 11 00:00:31,170 --> 00:00:38,310 and for this we need to do two things - we need to store the token somewhere on the device, not in Redux 12 00:00:38,310 --> 00:00:43,110 because Redux is in memory and this will be lost whenever the app restarts. 13 00:00:43,110 --> 00:00:45,920 So it needs to be somewhere on the hard drive of the device 14 00:00:45,930 --> 00:00:53,480 so to say and in addition, we need to check that storage whenever our app starts and automatically set 15 00:00:53,480 --> 00:00:58,080 the token in Redux and redirect the user if we do find a valid token there. 16 00:00:59,160 --> 00:01:05,590 Now let's start with storing because that's a logical first step. To store, we need to import 17 00:01:05,590 --> 00:01:10,090 something here in the, not in the products action creator 18 00:01:10,090 --> 00:01:20,810 but actually here in the auth action creator, there you need to import async storage from React Native. 19 00:01:21,930 --> 00:01:23,280 With that imported, 20 00:01:23,460 --> 00:01:32,130 we can use that to then save data to the device, async storage is a React Native API that in the end 21 00:01:32,130 --> 00:01:38,370 uses a key value storage on the device which is available both on iOS and Android where we can store 22 00:01:38,370 --> 00:01:45,150 data that persists across app relaunches, so which we can use to save data that is not lost when the 23 00:01:45,150 --> 00:01:47,460 app launches or restarts. 24 00:01:47,460 --> 00:01:52,910 So I'll add a new function, save data to storage or anything like that 25 00:01:53,040 --> 00:02:00,100 and there I expect to get my token and my userId, the two pieces of data I'm interested in here and 26 00:02:00,140 --> 00:02:08,970 we can now use the async storage to then set an item which is how you save data and that item which 27 00:02:08,970 --> 00:02:11,560 you set needs to be a string 28 00:02:11,580 --> 00:02:14,840 but first of all you need to define a key and I'll name it user data 29 00:02:14,850 --> 00:02:19,260 but that's up to you, you will need that key later to retrieve your data 30 00:02:19,330 --> 00:02:21,170 and the second value now is a string, 31 00:02:21,180 --> 00:02:26,310 so the string which you want to save in there and you must save a string essentially. 32 00:02:26,440 --> 00:02:27,340 Now that's no problem, 33 00:02:27,340 --> 00:02:33,990 we can use json.stringify to convert a Javascript object to a string and the object which I want 34 00:02:33,990 --> 00:02:40,200 to convert is an object that holds my token which I get as an argument here and it holds the userId which 35 00:02:40,200 --> 00:02:41,740 I get as an argument. 36 00:02:41,790 --> 00:02:48,980 So in the end, we then save this object as a string to the device. 37 00:02:48,990 --> 00:02:54,590 Now we just need to call save data to storage whenever we're done logging in or signing up, 38 00:02:54,590 --> 00:03:01,080 so here maybe after dispatching login, we can call save data to storage and forward resData.idToken 39 00:03:01,140 --> 00:03:08,100 and resData.localId, so the same things we use in Redux and we definitely want to 40 00:03:08,100 --> 00:03:11,610 use them there because Redux is important for the running application 41 00:03:11,850 --> 00:03:18,090 but I also save it to the device for when we want to load it when the app restarts. 42 00:03:18,140 --> 00:03:22,810 Now however that alone won't do the trick and we of course also need to do it when signing up 43 00:03:22,820 --> 00:03:28,340 but that alone won't do the trick, we'll need one extra piece of information here before we continue. 44 00:03:28,430 --> 00:03:35,780 Do you know which? We need to know how long the token is valid because you must not forget that eventually 45 00:03:36,230 --> 00:03:42,920 it will expire which is why for logging in and signing up, you get that expiresIn key in the response 46 00:03:43,310 --> 00:03:50,510 which tells you in seconds how long it takes until your token is invalid and Firebase won't accept 47 00:03:50,510 --> 00:03:51,510 it anymore. 48 00:03:51,650 --> 00:03:57,590 We need to store that information as well because if we later come back, if we restart the app after 49 00:03:57,590 --> 00:04:01,390 three hours, our token is probably invalid, 50 00:04:01,460 --> 00:04:04,370 so we need to know when the token will invalidate 51 00:04:04,370 --> 00:04:09,800 so that we when we check that, when the app restarts, when we check whether we do have a token, we know 52 00:04:09,800 --> 00:04:16,460 whether that token we might find is actually still relevant or whether it's invalid already and we need 53 00:04:16,460 --> 00:04:20,120 a new token anyways in which case we shouldn't auto login the user. 54 00:04:20,480 --> 00:04:23,360 So I want to store the expiration date and 55 00:04:23,360 --> 00:04:31,180 I'll create expiration date here as a constant, a new date object which in the end should take the 56 00:04:31,180 --> 00:04:35,020 current date and add the expiry time. 57 00:04:37,810 --> 00:04:45,790 Now the new date takes a date object but with get time, we get the current timestamp in milliseconds since 58 00:04:45,790 --> 00:04:49,590 the beginning of time so to say, which is in the here 1970 59 00:04:49,780 --> 00:04:51,200 in Javascript. 60 00:04:51,570 --> 00:04:57,630 So that's an amount in milliseconds and to that, we can add resData.expiresIn. 61 00:04:58,210 --> 00:05:01,720 This is an amount of seconds, 62 00:05:01,720 --> 00:05:06,910 so we need to convert this to milliseconds and it's also a string, so we need to convert it to a number. 63 00:05:06,910 --> 00:05:16,490 So here I'll add a plus here or simply parseInt alternatively to convert this to an integer and then I 64 00:05:16,490 --> 00:05:23,550 multiply this with 1000 to convert it from seconds to milliseconds because get time also gives us milliseconds. 65 00:05:23,570 --> 00:05:29,690 Now what this gives me is a new timestamp in the future which is the current time plus the expiry time 66 00:05:29,690 --> 00:05:35,990 and I wrap this into another date object to convert it back from a huge millisecond number to a concrete 67 00:05:36,110 --> 00:05:44,520 timestamp date object. This expiration date is now what we also need to pass to save data to storage, 68 00:05:44,530 --> 00:05:48,880 so here I expect to get the expiration date as well 69 00:05:48,880 --> 00:05:51,070 and we need to enclose this here. 70 00:05:51,400 --> 00:05:55,810 So here I can name this expiry date maybe to mix things up, 71 00:05:55,840 --> 00:06:05,830 this is my expiration date and there I want to call this nice little toISOString method which 72 00:06:05,830 --> 00:06:11,500 we can call on date objects because this will convert it to a string in a standardized format which 73 00:06:11,500 --> 00:06:14,240 I want to save. 74 00:06:14,400 --> 00:06:18,150 Now this is what I want to do for signing up as well, 75 00:06:18,150 --> 00:06:20,190 so this can be copied over there 76 00:06:20,520 --> 00:06:22,120 and this is what I want to do there as well, 77 00:06:22,170 --> 00:06:26,220 create the expiration date and save that to storage. 78 00:06:26,220 --> 00:06:29,220 Now we're saving that to storage which is nice 79 00:06:29,670 --> 00:06:35,670 but now we also need to check this when we're logging in. A nice way of doing that is to create a new 80 00:06:35,670 --> 00:06:41,040 screen and I'll actually create this outside of my existing folders in the screens folder but neither 81 00:06:41,040 --> 00:06:47,570 in shop nor in user and I'll name this startup screen and you can name it however you want. 82 00:06:47,610 --> 00:06:54,240 The idea is that I show this screen whilst my app is booting up and I'm figuring out whether the user 83 00:06:54,240 --> 00:06:55,380 is authenticated or not 84 00:06:55,380 --> 00:06:56,770 and this will be super fast, 85 00:06:56,790 --> 00:07:02,350 chances are we won't even see that screen when the app starts. Now in here, 86 00:07:02,370 --> 00:07:06,670 we create a regular React component by importing react from react 87 00:07:06,910 --> 00:07:11,120 and in there, I import stuff from React Native, to be precise 88 00:07:11,130 --> 00:07:17,310 I need a view, the activity indicator to show a loading spinner whilst the app is booting up and also 89 00:07:17,310 --> 00:07:23,280 stylesheet and also async storage because here I also want to access async storage to find out 90 00:07:23,280 --> 00:07:26,450 whether we do have a valid token or not. 91 00:07:26,460 --> 00:07:33,660 So here, I then have my startup screen component which is a regular React component and the stylesheet 92 00:07:33,780 --> 00:07:44,570 object here with Stylesheet.create and in the end, we can export this startup screen like this. Now in the 93 00:07:44,580 --> 00:07:56,450 component, I'll simply present the activity indicator here with a size of large and a color of Colors.primary 94 00:07:56,450 --> 00:08:05,100 and for that you need to import that colors constant of course and also assign a little 95 00:08:05,100 --> 00:08:13,520 style here to the view and I'll just take styles.screen here which we can now add to the stylesheet 96 00:08:13,520 --> 00:08:16,530 and that should be very simple, 97 00:08:16,690 --> 00:08:23,690 flex one, justify content center and align item center for some vertically and horizontally centered 98 00:08:23,720 --> 00:08:29,870 content. Now of course the real important stuff happens above the jsx code, 99 00:08:29,870 --> 00:08:33,520 this is where we now need to check the async storage for a valid token 100 00:08:33,620 --> 00:08:39,820 and I can do this with the help of use effect which allows me to run some logic here 101 00:08:39,820 --> 00:08:42,230 when this component mounted 102 00:08:42,250 --> 00:08:51,190 and indeed here, I won't need any dependencies because here inside of use effect, I now want to add a 103 00:08:51,190 --> 00:08:58,060 new function which I'll named try sign up or try login is more fitting and I use the separate function 104 00:08:58,090 --> 00:09:04,390 because here I want to use async await which I can do by creating that inner function which I can now 105 00:09:04,390 --> 00:09:05,310 call here, 106 00:09:05,410 --> 00:09:10,840 try login because again, async here on this function which you pass to use effect would not really be 107 00:09:10,840 --> 00:09:14,020 allowed and here in try login, 108 00:09:14,170 --> 00:09:21,460 the goal now is to check the async storage for a valid token. So I get my user data here by accessing 109 00:09:21,520 --> 00:09:23,030 AsyncStorage.getItem 110 00:09:23,240 --> 00:09:28,750 and then use the key you used for storing. 111 00:09:28,960 --> 00:09:35,950 So in my case that's user data but you must use whichever key you used in your auth.js file in the 112 00:09:35,950 --> 00:09:43,160 actions folder for storing your data with async storage. So I retrieve this and now the important thing 113 00:09:43,160 --> 00:09:49,970 to know here is that get item, like all async storage methods, actually is asynchronous which means like 114 00:09:50,030 --> 00:09:55,520 the name suggests I guess, which means that you get a promise here, so we can await this and this will 115 00:09:55,520 --> 00:09:58,560 give us back our user data. 116 00:09:58,570 --> 00:10:05,200 Now this is then the data as a string, to convert it to a piece of data we can work with, 117 00:10:05,230 --> 00:10:14,410 I'll have my transformed data with JSON.parse user data and this parses a string in JSON format and 118 00:10:14,410 --> 00:10:24,220 converts it to a Javascript object or array. Now before even trying that, I'll check if user data is not 119 00:10:24,220 --> 00:10:24,850 true-ish, 120 00:10:24,850 --> 00:10:31,030 so if it's not set, if we can't find any data for that key because then I know we're certainly not logged 121 00:10:31,030 --> 00:10:31,720 in. 122 00:10:31,780 --> 00:10:36,370 So here I want to return and not continue and do one important thing, 123 00:10:36,370 --> 00:10:43,810 I'll use props navigation and navigate to the auth screen because this startup screen has to be added 124 00:10:43,810 --> 00:10:44,770 to the navigator, 125 00:10:45,190 --> 00:10:52,960 so here we can import it, import the startup screen from the screens folder of course, startup screen 126 00:10:54,070 --> 00:10:57,590 and we add it to the main in navigator here, 127 00:10:57,760 --> 00:11:07,190 so where we also have auth and shop and there at the very top, I add startup and point at my startup screen. 128 00:11:07,350 --> 00:11:11,490 This means that this is the first thing I load here. 129 00:11:11,490 --> 00:11:17,010 Now on this startup screen, I therefore go to the auth screen if we can't find a token, so thanks to 130 00:11:17,010 --> 00:11:22,230 the switch navigator, I leave that screen and go to the auth screen or to the auth stack because I don't 131 00:11:22,230 --> 00:11:28,810 find a token. So if we make it past this if check, we at least have some data but the token still might be 132 00:11:28,810 --> 00:11:29,950 invalid. 133 00:11:30,370 --> 00:11:39,980 So we will of course have the token, the userId and the expiry date which we can extract from the 134 00:11:40,520 --> 00:11:42,770 transformed data 135 00:11:43,600 --> 00:11:46,860 after this line, here with this object 136 00:11:46,870 --> 00:11:54,580 destructuring syntax and these are the three pieces of data with the three exact same names as I store 137 00:11:54,580 --> 00:11:56,400 it here in the auth.js file. So 138 00:11:56,410 --> 00:12:00,700 here we have an object with a token, userId and expiry date field, 139 00:12:00,700 --> 00:12:03,000 that's what we store in async storage, 140 00:12:03,010 --> 00:12:08,580 that's what I can retrieve here. 141 00:12:08,610 --> 00:12:11,360 Now we need to check whether the token is still valid 142 00:12:11,460 --> 00:12:20,670 and for this, I can recreate my expiration date by using new date and passing expiry date which is actually 143 00:12:20,670 --> 00:12:23,220 a string in ISO format to it 144 00:12:23,220 --> 00:12:27,270 and now we can add an if check and see if expiration date, 145 00:12:27,270 --> 00:12:33,660 so the date when the token becomes invalid, if it's smaller or equal than new date, which is the current 146 00:12:33,660 --> 00:12:39,570 timestamp which means expiration date is in the past and if that's in the past, then our token is invalid. 147 00:12:40,320 --> 00:12:45,300 It's also invalid if we can't find a token or 148 00:12:45,360 --> 00:12:51,900 also if we can't find a userId, if any of the three conditions is true, we have an invalid token or 149 00:12:51,900 --> 00:12:54,550 an invalid userId or anything like that. 150 00:12:54,570 --> 00:12:58,700 So in that case, I also want to return 151 00:12:59,340 --> 00:13:05,640 and of course navigate away to the auth screen. If we make it past this if check, 152 00:13:05,700 --> 00:13:10,110 we do have a token, we do have a userId and the token is still valid. 153 00:13:10,110 --> 00:13:17,720 So if you make it here, I will call props.navigation.navigate shop to go to the shop screen and also 154 00:13:17,720 --> 00:13:21,020 important, I need to log the user in and for that, 155 00:13:21,030 --> 00:13:24,230 we need a new action because I don't want to send a request 156 00:13:24,240 --> 00:13:29,900 now, I just want to change some data in Redux. So I'll add a new action creator here, 157 00:13:31,550 --> 00:13:44,200 export const authenticate maybe which takes my userId and my token and which then is used to change 158 00:13:44,200 --> 00:13:45,250 my data in Redux 159 00:13:45,250 --> 00:13:54,390 and for that, I will now add a new action identifier, authenticate with a string of authenticate here 160 00:13:55,290 --> 00:14:02,190 and use this here as a type authenticate, attach my userId which is my userId here and my token 161 00:14:02,190 --> 00:14:08,360 which is the token I get as an argument and now this authenticate action is what I want to dispatch 162 00:14:08,370 --> 00:14:09,600 here on the startup screen. 163 00:14:10,730 --> 00:14:15,950 But before I do that by the way, we can also go back to the action creator and at the end of sign up, 164 00:14:15,950 --> 00:14:22,520 instead of dispatching my own custom sign up action here, we can also dispatch authenticate and execute 165 00:14:22,550 --> 00:14:23,390 this here, 166 00:14:23,510 --> 00:14:33,930 this action creator and simply forward resData.localId as a userId and resData.idToken 167 00:14:33,950 --> 00:14:40,040 as a token and do the very same at the end of login. 168 00:14:40,040 --> 00:14:45,400 So now I did indeed combine these two flows because in the end the same thing happens, 169 00:14:45,560 --> 00:14:51,350 so now in the reducer, in the auth reducer, of course instead of checking login and sign up, we can simply 170 00:14:51,350 --> 00:14:57,780 import authenticate and use this identifier here, get rid of sign up and I'll just comment it out to 171 00:14:57,800 --> 00:15:00,830 still live it here for reference because the same thing happens. 172 00:15:00,860 --> 00:15:05,980 So now combining it makes sense because we also need it for this automatic login. 173 00:15:06,020 --> 00:15:11,090 So here I then also dispatch it here for the manual login and the manual sign up 174 00:15:11,090 --> 00:15:17,150 and of course now with that, if you want to, you can also get rid of login and sign up here, of these 175 00:15:17,150 --> 00:15:20,200 two identifiers because we don't use them anymore. 176 00:15:20,390 --> 00:15:24,380 But with that, back to the start up screen, here we can now also dispatch this 177 00:15:24,600 --> 00:15:34,480 and for that you just need to import use dispatch from React Redux and import the action, so 178 00:15:34,490 --> 00:15:41,650 import everything as auth actions maybe from the store folder, from the actions folder and from the auth 179 00:15:41,650 --> 00:15:43,190 file there 180 00:15:43,270 --> 00:15:49,590 and then here at the beginning, get access to the dispatch function by calling use dispatch 181 00:15:49,800 --> 00:15:55,230 and with that available, here we can add dispatch as a dependency but this will never change, 182 00:15:55,240 --> 00:16:01,330 so the effect will never rerun and here after navigating to the shop, I now also want to dispatch auth 183 00:16:01,450 --> 00:16:10,510 actions authenticate and forward the extracted userId, so this field here and the extracted token, 184 00:16:10,510 --> 00:16:19,150 so this field which I got from my async storage and that should now automatically log me in. That was a lot 185 00:16:19,150 --> 00:16:24,310 of work, let's see whether that works. If I reload this, I end up here on the login screen because right 186 00:16:24,340 --> 00:16:27,010 now I have no valid tokens stored in async storage. 187 00:16:27,880 --> 00:16:37,510 So now if I try logging in here, I am forwarded, if I now reload, I again end up here 188 00:16:37,510 --> 00:16:43,560 and that's great because now this means that this seems to work and that token is stored. 189 00:16:43,730 --> 00:16:48,230 Now of course it would be nice if we could also logout though because right now, there's no way of doing 190 00:16:48,230 --> 00:16:49,070 that.