تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

مرور تنظیم وضعیت پادکست با Alpine.js0:00

So now that we have the dashboard in a pretty good looking place where any live listening parties can be shown to the users to be able to click in to listen to them, as well as being able to create any new listening parties, why don't, before we add any more features to this page, we take a look at how we can get every single User who is in a listening party room, that /parties/show that we have for each listening party, how can they be listening to the same media URL at all in one time? I think we can accomplish this with JavaScript first and we might switch this over to server side but I think JavaScript is going to be the best way to do this and we can do it and start this off using Alpine. But before we get started with that, I don't know if you're like me but if you are building

Fixing Dashboard Empty State0:46

start this off using Alpine. But before we get started with that, I don't know if you're like me but if you are building out an application from start to finish, usually I have my mind, hey I want to get moving on this next feature or this next part of the application but I see little things like this that I can tweak and fix right now and I don't really like the look of how the empty state looks for this page. Let's fix that really quick before moving on. So in the if listeningParties is empty, again this is just with Blade because if someone loads the page, we'll know if there are listeningParties in this Livewire component or not. So if there are not any listeningParties like is the current state, why don't we have

loads the page, we'll know if there are listening parties in this Livewire component or not. So if there are not any listening parties like is the current state, why don't we have this div flex, oops, within the class we could say it's flex and items-center and justify-center and add some padding maybe like a padding of, we'll start off with like 2 and we'll add the serif font and maybe text-sm, that should be a good start. Let's see. Okay, that's looking pretty good. We probably want a little bit more padding. Maybe a padding of 6, yeah, I'm liking that. That's looking pretty good.

Filtering Incomplete Parties1:54

Maybe a padding of six, yeah, I'm liking that. That's looking pretty good. Another thing that I just caught is when we have this listening parties that we're showing, we're actually showing all the listening parties but we don't necessarily have all that information until our background job of processing the podcast has run. And the last thing that happens in that processPodcast job is updating the end time of the listening party. So that's probably a good thing to check and say, okay, I want only to show the listening parties that have an end time set because there's a bunch of things that we're setting in this job that we're showing on that dashboard page but we might not have set them just yet.

parties that have an end time set because there's a bunch of things that we're setting in this job that we're showing on that dashboard page but we might not have set them just yet because when the listening party has been created, some of this stuff has not yet been set. And maybe in the next couple of episodes, we might go back to this and have some placeholder that we'll put in if some of this can't be set. Maybe it doesn't exist in the podcast RSS feed for whatever reason. But for now, we'll just check to make sure that it is complete. In this case, an end time has been set and if the end time did not get set for whatever reason, maybe something failed up here, then we're not necessarily showing unfinished listening

In this case, an end time has been set and if the end time did not get set for whatever reason, maybe something failed up here, then we're not necessarily showing unfinished listening parties to the user. So we can say this listening party where active is true, but then before we start to order it, we can say where not null, and then we can set that to be the end time. So now we're ordering by the start time with the episode and podcast, but we're saying where the listening parties are not null for this end time field. Okay, that all looks good. Now let's head to the actual show.blade.php. Again that's the Livewire pages parties.

Planning Synced Playback3:44

Now let's head to the actual show.blade.php. Again that's the Livewire pages parties. This is what we're showing when we go to the vote route of parties slash in the listening party that model that we're passing in. Let's revamp this. Let's get something going. So when I click on an upcoming listening party or one that is live, I am listening to that podcast. I'm hearing the podcast on the page in sync with other users. So why don't we create a new podcast so we can manually change this to live and we'll

I'm hearing the podcast on the page in sync with other users. So why don't we create a new Podcast so we can manually change this to live and we'll set the partyStartTime. So that way we have some real data to go with. If we have a Lyricasts party, let's say, and the Lyricasts RSS feed, and we'll set the start time maybe today. So we have that automatically set. And now since we're only showing the listening parties on the dashboard that have an endTime, it looks like this job is already successfully ran. So now when this does start, in this case in 22 minutes, and we can change this time.

time, it looks like this job is already successfully ran. So now when this does start, in this case in 22 minutes, and we can change this time manually, but in 22 minutes, when this starts, I want to be able to click into it and start listening wherever that podcast is playing. So maybe it's a 20 minute podcast and I start 10 minutes after it started, I want to be playing the podcast at 10 minutes. How can we do this? Let's just think about it within our code first, before we get into any fancy, maybe like streaming it from the server and having the server play it, it can probably be an easier way.

like streaming it from the server and having the server play it, it can probably be an easier way. And again, this might be something that we revamp in the future, but I think this way might work good for us because if we take a look at the listening party and specifically the schema that we've created, we have a startTime, we have an endTime. We've already calculated how long a podcast is and this startTime and endTime are on the server. So because we have this information, what we could do is when this page loads, we can check has the startTime happened? In this case, as the startTime, is it now or in the past?

check has the start time happened? In this case, as the start time, is it now or in the past? If it is, where's the end time? What's the difference between the start time and the end time? Once we know that information, we can say, okay, I want to play that media URL based off of that calculation of start time versus end time. So in the show page, why don't we get started? The first thing I want to do is also include the episode and podcast with this information that we're bringing in. That way we have it available in the page and we're going to start with this mostly

Handling Party Creation State6:27

that we're bringing in. That way we have it available in the page and we're going to start with this mostly unstyled and let's just get the information on the page first. And then in the next episode, we can style this up and make it look a little bit prettier before the listening party is even created. When we redirect users, we don't have the information that we want to show. So first we'll just say maybe like this listening party is being created. So when a user creates a new listening party, we don't want to immediately put them into a waiting room. We might not have information that we need.

a waiting room. We might not have information that we need. Maybe we'll just show them, hey, this is, you know, we're creating a new listening party room for you right now. So maybe we'll say if listeningParty, maybe if the endTime has not yet set and we can say is equal to no, then we can maybe show a, like a div and we'll just do something quick here. Like, yeah, we'll just give whatever copilot is setting up and we'll say something like creating a new listening party for you, or maybe like we'll use the name. So creating your, and then we can span class of font-bold, and we can say here, listening

Building Alpine Audio Sync7:27

creating a new listening party for you, or maybe like we'll use the name. So creating your, and then we can span class of, of font bold, and we can say here, listening party name. There we go. And else in this case, we'll say if the listeningParty and time is null, we'll show them this. So while this is already being created, while that job is being run, essentially else, this is where we can start to scaffold out the information that we have. And we'll just go ahead and say, and if, okay, so now let's set up an Alpine.js component. We can do that with x-data because here is where we are going to check and make those calculations on the client.

We can do that with X data because here is where we are going to check and make those calculations on the client. When the page loads is the startTime already passed? And if it has, let's play the podcast at the time, the lapseTime of the podcast that it should be playing. So maybe we can start off with audio, we can say audio, no, we can say isLoading equals true because then we can set it to false after the podcast initializes. Then we can say the currentTime, because this is going to be the variable that we're going to need to say when in the podcast should this be playing in this case, the currentTime relative to the client, after we make the calculations between the startTime and

going to need to say when in the podcast, should this be playing in this case, the current time relative to the client, after we make the calculations between the start time and the time elapsed within the podcast. And this can, in this case, the end time that we are already set within this ListeningParty model. And then we can have a start timestamp that way we can have this use in the calculation. We can set that to listeningParty.startTime, and then use that as a timestamp. Let's go ahead and just close this out for now. We've set the variables. Now let's go ahead and make some functions.

We've set the variables. Now let's go ahead and make some functions. The first one, why don't we have a, like a, a checkAndPlayAudio function. We can set the elapsedTime and that's looks about right. So we're wanting to do is set the elapsedTime to the max of the date now floor. And what this is doing is the Unix timestamp in milliseconds, but then we're converting it to seconds. So elapsedTime is going to be that minus the, this startTimestamp. So then we want to say if elapsedTime is greater than or equal to zero, then here's where we can perhaps like how many seconds are left in this audio.

So then we want to say if elapsedTime is greater than or equal to zero, then here's where we can perhaps like how many seconds are left in this audio. So that way we can know where to set the play head. For example, when we're scrubbing this audio, we don't want the user to be able to set it automatically or start and play it automatically. It just plays once the page is loaded. So we can say if elapsedTime is greater than or equal to zero, why don't we then set the audio and the audioPlayer has a currentTime parameter or option. And we can set that then to the elapsedTime and we can then go ahead and play the audio. And here's where we can set up some error handling and this looks all good.

And we can set that then to the elapsed time and we can then go ahead and play the audio. And here's where we can set up some error handling and this looks all good. If there is some type of error, we can display that or at least just show in the console. If we need that, that's helpful. Then we can say else, in this case, if elapsedTime is not greater than or equal to zero, why don't we check and consistently check? In this case, we can set the timeout to say every second. Let's double check to see if this should be playing. All right, let's put a comma there so we can add another function. Let's go ahead and actually above this, maybe we will initialize the audioPlayer.

All right, let's put a comma there so we can add another function. Let's go ahead and actually above this, maybe we will initialize the audioPlayer. So what we can do here is initialize audioPlayer. And what we'll go ahead and do is say this.audio and we can reference the component in our Blade. Maybe we can put it down here. We can just say this.refs.audioPlayer, it's probably the best way to do it and that's what we can set it in our template. And then we can say this.audio, we can add an event listener here to say that we want to play or run the check and play audio if the audio has loaded.

And then we can say this.audio, we can add an event listener here to say that we want to play or run the checkAndPlayAudio if the audio has loaded. In this case, this loaded metadata, which is an event listener for the audio component. If it's loaded, then we can run that checkAndPlayAudio. And we can actually set this here so that way we can set loading as false and then run checkAndPlayAudio because we want to set this to false because we're currently playing the audio in this case. And there we go. We'll set that there and then we can say, we can add another event listener like what Copilot is sharing here where we can say this.audioEventListener.timeUpdate.

We'll set that there and then we can say, we can add another event listener like what Copilot is sharing here where we can say this.audioEventListener.timeUpdate. In this case, when something changes in the audio while it's playing, it gives a event listener called the timeUpdate. We can set the current time and that's the value that we have here to the audio's current time. So we're already playing it at a specific time. That's what we're doing here in check and playAudio. But now we can kind of visualize and show, hey, here's where it is being played. Okay.

But now we can kind of visualize and show, hey, here's where it is being played. Okay. I want a comma here before to separate each function. And then why don't we add another one? We can say we'll format the time that way we can make sure it's in a displayable format. We can format it to or pass in the seconds variable. And why don't we do, this is very similar to what we did in the dashboard. We're going to say Math.floor(seconds / 60), remaining seconds. The remaining seconds is how much is left in the audio. We can probably use this in our template to show the current time of the podcast itself.

The remaining seconds is how much is left in the audio. We can probably use this in our template to show the current time of the podcast itself. Then let's go ahead and display this. We can say, okay, the audio x-ref, and this is referencing that audio player that we did up here. And we can pass in the source, the media URL or preloading and setting that to audio or to auto, I should say. Let's go ahead and set up the listening party or just kind of like display some information. So the episode podcast title, and again, we can also do this now that we set that up. So the listening party podcast title, the episode title, we can have the current time,

So the episode podcast title, and again, we can also do this now that we set that up. So the listening party podcast title, the episode title, we can have the current time, which is where we're using that formatTime function. So anything that we have in this Alpine.js data block is automatically available to the underlying children elements, formatTime. And then this is the start time that the podcast should start. And then we can have some loading if the media has not loaded in the audio player. So we have the dashboard, it's set to live. Let's click in and play. And it looks like if we were to take a look at our database, the reason why it's not actually

Noticing Live Status Bug14:36

Let's click in and play. And it looks like if we were to take a look at our database, the reason why it's not actually playing is because the start time was set to 2300 hours and the end time would be 2312, which is in the past. So because we haven't actually set up a scheduler yet to clear the live status that we see here, why don't we do that now that we're thinking about it?

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟