Créer un Clone de Spotify (Fullstack) avec Spring boot 3, Angular 17, Bootstrap 5, PostgreSQL (2024)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Today, I present you the most successful tutorial on my channel, build a clone ready for deployment in production of the streaming service the most famous music in the world, Spotify. To do this, on the front-end side, we will use Angular in its latest version with Standalone Components, Signal and the new Control Flow. Regarding the design, we will use Bootstrap 5.3 with the aim of having a responsive design, but also to go quickly for the implementation of our UI. On the back-end side, we will use Spring Boot and PostgreSQL, two very robust technologies which have proven themselves and which will enable to accommodate a very large number of users. We will also set up authentication via Google and password, creating accounts and forgetting passwords via Auth0 in order to secure our application. Regarding the functionalities of our application, our users will be able to add music, listen to it, search for music and manage favorites. We will start by setting up our front, that is to say the generation. The generation of our application Angular and its configuration. To do this, we will type ng-new spotify-clone-front. We will use the SCSS style and we will generate all our components in stand-alone mode. So no, no pre-rendering or SSR. In the meantime, I'm preparing to open the project with my IDE. So here my project is open in my idea we're going to wait a little longer, that's it finished npm has successfully completed its installation, we continue on the installation of bootstrap we are doing well be careful to position yourself in the file of our project and then we use the Angular cli to install our bootstrap package where it is ng add at sign ng drawn bootstrap slash ng bootstrap we validate here we answer the questions we answer so yes then we will customize bootstrap for that it matches as much as possible with the style of Spotify To do this we will create a folder in assets which will be called scss then we will create a scss style sheet which will be called underscore bootstrap pulled variables content of this stylesheet is available in the description you have a link that will help you bring it directly, I copy paste it and content of this file what we notice and the principle of this file is to overload the bootstrap variables variables that can be for example linked in this case to the size of the h1 tags but there are lots of things like example the color of the background the fact that we force dark mode font type etc etc one once this file is created you will have to import it we go to the css points style file which is the central style file and we will import our file just before the css of bootstrap thanks to the keywords import we type in 7 scss and bootstrap variables then we move on to the customization of our html index here we go force bootstrap to go into dark mode with the data bs theme equals dark and we will also import a font to once again stick to the style from spotify this font is the noto without us matter here now that's cool we're going to render in the app component points html and on will validate that our bootstrap and our config works well, we check and delete everything We add a dike with we are going to put inside a button with button class and primary button and We are going to make a hello bootstrap. Then, we will be able to run our server. Either you have IntelliJ or WebStorm, like me. In this case, you have just press play. Either you might be on VS Code or something. In these cases, you can open a terminal and run ng serve. Then you just have to go to localhost 4200. I'm going to use the IDE instead. Then we open our browser on localhost 4200. And we notice that the background is dark. And we have the green of Spotify with Spotify button style. We have validated that our bootstrap works and that our configuration is valid. We start again with our IDE. We will stop the server like this. We open our terminal and we will add our fontosome icon library thanks to Angular. We type ng add atrase for awesome slash angular-fontosome. There, we answer yes. We choose fontosome 6 and we takes the free icons. There it's done. We return to our IDE. We will create a shared folder and we create a TypeScript file which we will call font. Thierry Awesome Thierry Icons. Thierry Awesome Thierry Icons. The purpose of this file and this class, it will be to contain the icons that which we will use in our application. We don't want to import all the icons from this library to avoid having an application too big and obviously load the icons for nothing. We type export const font awesome icons, which is of type icon definition, it is an array. And it is in this table that we will define the icons that we want to put or not. We're going to do a little test, we're going to import a icon called fa-circle-play, like this. Then we go back, we're going to go rather in the app-component class to initialize this library. We will need the fa-icon-library service that we are going to inject, like this. We will also need to implement onInit on the side of this component, root component, where we will call a method that we will create in a moment. It's called font-init-font-awesome, which we created in this way, and where we will come call the service that we just injected before with the add-icon method, like this. And we're going to call the method, sorry, the variable, that is to say the table that we created just before. Then, we will carry out a small test to validate that this library works well. First, we will import the font-awesome-module. In this component. See using icons in the template. We will then be in the template. We will add fa-icon with the attribute icons, named CirclePlay, which is the icon that we imported just before. We run our server if it is not done yet. And we go to the side of our browser and wait and we see here that the icon is present ok perfect now we are going to design our layout we we come back to our idea and we surrender on the app component html if it is not already there case we do a check, erase everything and will start by adding a div with a class css containers fluid padding top 3 and viewport 8 200 another div with a raw class and 8 main raw that we will create in a little while more another div which will be dedicated to the column which will be on the left therefore t no tsm no dmd block col md3 col xl2 navigation pe-2 and And h-100 in this div will be there, we will create it right after there will be navigation and library like that you can visualize then we will create a another div which will be the main column with collar 12 collar sm 12 collar md 9 collar xl 10 panning start 0 and h8 100% then here we will place our and hours and our main div with the following classes so make bottom of padding 3 background dark over flow scroll and full and hand happy and in the latter we will place the router outlet ok we are good for our layout it gives us It remains to write the part and css first of all we will import the variable file like these will add the class is main euro which contains is with the variable main road is we also have the full class is main content with height who made him happy is now we are going take care of the two components navigation and bookstore which are in the left column as you can see on spotify here left column we open our terminal and we will use the angular cli thanks to the command nggc we are going to use an out or row correctly and we will call it navigation We open the HTML, we delete everything what is inside. We open a div with a class roundedTo, padding2, grid and background dark. In this div, we will put a tag ul with a nav and flex colon class. Then, a li tag which will contain nav item. Then, an a tag which will contain a link with a nav link class, flex align item center. The attribute, we are going to put an attribute router link because this link, it will allow us to return home. It's going to be equal slash to root. And we will also ensure that when we click and when we are on the root URL, home remains blank. For this, we put active like this. We're going to go to the line and we're going to also add router active link. This is so that the class is active only and only if we are on the root address. In this link there is an icon that we will bring right after which is called home and a div that will just contain home. We're going to have a second menu entry. Here, with a nav item class and a link in the same way as the one above. So a nav link. And flex align item center with a router link which this time will be search. And the same with class when she is active with active. An icon that will be called search. And a class with margin end 3 and a class navicode which must also be found here we close this tag and we also add the div which contains search inside finally we add the fontosome module and we add a small active class here with color white which is contained in our scss file like this now if we go back to our browser we need to go to the app component.html and add our app navigation component we also have to import our two little ones icons that are fa home and fa search with everything that normally we should actually have home and search in this way regarding concerns bookstore we return to our terminal and type nggc layout slash library on open this component we erase what is there inside we add a div with a class so margin top 2 rounded top 2 padding y 2 padding start 2 padding end 3 dark background here we add more a new div with flex justify Content, Between, AlignItemCenter and TextSecondary. Another small div with this time Deflex and AlignItemCenter and MarginCenter3. In it there is a FontSumIcone with a NavIcone class and a MarginEnd of 3. And our icon is called Book. Then there is a div that contains YourLibrary. We're just going to import right away the icon, that way we remember it. There it's done. And in Bookstore, we also go import the FontSumModule. Back in our HTML, we will add a link, an A tag, which will contain a link which will subsequently allow you to add music. And this link is symbolized by a Plus icon, which is a NavIcone class and is called Plus, which we will immediately import, FaPlus. So, let's see what it gives on the browser side. We always have to add this component with the import. That's it, so there we have YourLibrary is correct. We have our layout. Now we will move on to the generation of our backend. In the description, you will find a link which will take you back to the start.spring.io site. This is the link that I will show you right away. And this web application allows bootstrap your application directly, By having the possibility of specify the dependencies you want, the language, Spring version, etc. So I've already done that for you. You just have to click. You can see a little bit of the dependencies that I added. So, nothing crazy there. There is Web Security for API security, Load Client to manage authentication, Liquibase to manage database schemas, Postgre, the driver because we're going to need it, and Spring Data JPA for the persistence layer. So, we click on Generate. Next, we will open a File Explorer. We take the zip and copy it in our favorite folder. Once we've done that, we open it in our IDE. Once the project is open in your IDE, we will create an .env file which will contain our secrets, that is to say everything that goes be connected to the database, by example, or later, secrets of ODE. So, the environment variables that we are going to put in, it's going to be Postgre, username. In my case, it's Codecake. The Postgre, password, I don't have one. The Postgre URL is my localhost. And the name of my DB, Codecake, too. So, for my part, Postgre, it runs with Docker locally. But you can use any Postgre. This .env file, we will have to add to our configuration When we are going to run our server. So I'm going to modify my configuration, so in IntelliJ, I go to Edit Config. I do Modify Options, Variable Environment. And here, I'm going to add, so I'm going to remove it so you can see, but I will add here directly, my .env, I do OK, I do Apply and I do OK again. If you are with VS Code, there is also a possibility of adding environment variables. When you change the configuration of your launcher is a little different but you can do that too. This .env file we will add it to our gitignore to be sure that we will not commit it by mistake. So I'm going to initialize the git repository here we are going to add it, it is well ignored. Then we're going to go to resources we're going to rename the application file being reported in yml since we want to use this style. This file you will also have to recover because there is still quite a bit of options that are inside and we are not going to copy it by hand because it There is little added value in making you do that. I'm going to copy and paste it but you have the link in description. In broad terms we have a The level of logs, we have what is the active profile by default, the name of the application, the configuration of the data source with the configuration also from Spring Data JPA, a little more Spring config Data JPA with also the Hibernate config, we also have the config here from Liquibase, which we will talk just after, and a config here including We'll talk again when adding the music. We now move on to adding certain dependencies that we could not add in the Spring Boot Initializer, we go to the POM and we add the following dependency which is going to be the org.springframework.boot and which is called Spring Boot Starter Validation. This will allow us to validate DTOs as input to our web service. Then we will also need Mapstruct which is a DTO mapper. So it's... It's org.mapstruct and it's called Mapstruct and it is version 1.5.final. We have the same thing, but the processor which we will have to add to the compiler. This one is in scope provided. So. Next, we need the plugin, we need the compiler. So the maven-compiler-plugin. The version is 3.11.0 and we needs to manage configuration. So the source, java-version, the target, it's the same and we're going to have to add a annotation-processor which will be our Mapstruct. The artifact-id is Mapstruct-processor and the version is the same, 1.5.final. That's it, we update maven and we should be good. We now move on to the configuration of Liquibase which will go through the configuration of our diagram. We already have the thick changelog file, it's perfect, we are going to create an XML file that we will call master.xml and which will contain all Liquibase changelog files. Once again, since it doesn't have a big added value of getting hit that, you can retrieve it directly in the description via my GitHub repo. I'm going to copy and paste it too. Nothing special, we made some replacements of properties so that it is a little more compatible with Postgre and we will include all our changelog files for our DB. We will create this XML file right away. Like this, .xml, and this file will come to describe... Our schema and Liquibase will take care of transform this XML file into an instruction SQL to create our database. Along the way, he will also do checks on the fact that we have not modified this file XML and it will also look if it has any new XML files to execute when we will make updates to the database. Our database is architected as follows. We have a User table which contains our user's information. We have a Song table which will contain display information such as cover, the title of our song. We're going to have a Song Content table that will contain just the binary files, so the binary file of the music itself. And we're going to have a Favorite table Song that will contain the information which user appears on. For the rest, we have the creation of sequences for our Song and User tables. Now it's time to try to run our server. We press Play like this. We have an error and indeed, it We need to create the Spotify Cloud schema. I'm going to go to my PG Admin. I'm going to go to Codecake and I I'm going to create my Spotify Cloud schema. We save, we restart. So there, it seems to have gone well. We will nevertheless verify that our tables are well present. That's it, so we're good to go. Now that our database schema is done, we will be able to move on to the game writing our Hibernate entities, i.e. writing our functional area. The application, on the backend side, will be divided into two parts. The first will be the catalog, which we will call the context catalog. And the second will be the user context. The context catalog, it will contain everything what is related to songs and music. The user context, it will contain anything related to users. We start by creating the user context. We go to the Java folder, we create a new package, which will be called user context. In this package, we create another package which we will call domain, and we will create our first entity which we will call user. This class, we will note it, arrobas entity. We will also add arrobas table. The name of this table will be Spotify user. This entity will have an ID. This ID, we will note it, therefore at arrobas ID. We will also add the arrobas generated value, with a strategy of it is sequence type, with a generator which is called user sequence generator. Then we need to define this generator, its name. It's the same so user sequence generator name of the sequence in base it is user generator and the size of the rental that we put is 1 finally the name of the column is id then this entity it also has a last name with a name column last underscore name it also has a first name like this she also has an email has a subscription this object is not yet created we will do it just after it is an enum and finally he has an image url which is his avatar on will create subscription which is an enum and which has like value premium and free we return to user and we will generate the getters and setters of which we need then we are going to use a class abstract which will be used by the user and which will be used by the user and which will be used by all our entities this abstract class will allow us to carry out an audit to do this we will create a shared kernel package which will allow us to put things in common through our contexts in this package domain we create a class we will call abstract auditing entity this class will have two main fields which are going to be an instant which is going to be createDate which will be equal to instant.now and which will us allow you to keep track of when our entities were created and for the second field when they were modified. On this field we will add an annotation createdDate and at column with an attribute therefore name which is created underscore date and which is not updatable. Then we have a second field which is also a moment and which is called last modified date which is also equal to instant.now which this time has the notation last modified date and And the name of our column which will be LastModifiedDate. We can create our getters and our setters for these two fields. And we will also add annotations which will ensure that Hibernate and Spring Data JPA understand that it is necessary to the persistence of one of our entities, they come to supply these fields if necessary. These annotations are as follows. We have ArrobasMappedSuperClass and we have ArrobasEntityListener and you need it pass an AuditingEntityListener.class. Finally, last thing, it is a class which is generic and implements Serializable. And we need to have an abstract method of type T which is called GetID and which will be implemented by all classes that will extend. Okay, this last one. So obviously, we have to also add that it is abstract. We can now extend the audit class by our User class. It is abstract of type Long. Alright. Now we can move on to the Repository part. We will create a new package which is always going to be in UserContext. But this time we are going create the Repository package. We create a User Repository interface. This interface is in JPA Repository, User and Long. We import User, our domain, and we're good to go. Next, we will also create our output objects. Like this, it will already be done with our mapper. We will create a User Context Mapper package. This mapper is also an interface that will be called User Mapper, which has an annotation Mapper, that we take our Spring model in the purpose of being able to use Spring injection. The first mapper that we are going to put is ReadUserDto, which we'll create in a minute. The name of this mapper, we will call it ReadUserDtoToUser. It will take as parameter a user who is our entity. This is for Mapstruct. That way, we won't need... to map our objects by hand. Next, we will create our DTO, which we will call ReadUserDto. This class, sorry, it's a record. It has the following fields. FirstName, LastName, Email and EndName. Image url for the avatar we can return to our user mapper does the import and we are good for the moment we will miss our user service and at the moment we don't need very much now we can move on to the context catalog so we create the text catalog package we create the domain package and we create our different entities so the first is song we implement serializable we add the notation outside the base entity and non-table based notation with name of our table which is going to be song song it is going to have the same type of ID as user so we will copy it here to go a little faster we are going to change here it's song there too it's song and there too here is our song it has a uuid which is a public id, the column name public underscore id, it is not nullable, then it also has a title which is similarly non-nullable, she also has an author, she has a cover and he there is also the cover content type, story of simply know the extension of the image. We have all fields, so we can generate our getters like this And we can move on to the next entity. We will create song-content in the same way implement serializable we have the notation outside base entity, and with the name song-content, this time, there is no id generator, since we are going to use a foreign system key between the two, therefore, song-content, and so we will create the entity of the public column. content will use the ID of the song as if it was his primary team so it's in low ID and in the bottom column name song ID if we are going to have a one to one relationship between the two will tell hibernate that it must use the same IDs for these two tables one to one and in join column name it's song ID and column reference it is ID then we will have the content of the file itself of the sound which will be an array of bytes we will note with glob on will store them in the database and the name of the column is file it's the same it is not nullable finally last field we will have the file happy guy like that we will be able to manage the different types whether mp3 wave etc name and its files content type then we create our getters we can be able to manage the different types whether mp3 wave etc name and its files happy type then we can move on now to the creation of the last entity which is the entity to manage the favorites we therefore create the java class with favorite we reimplement that there is disable we add the Table entity notation name favorite favorite song we add a UUID which will be the song public ID which is its ID and here we are going to have what we call a primary key composite which will be composed of the of song public ID and user So that's also its ID and the name of the gala-user-email column. We generate the getters and setters and we are missing an ID annotation here class where we will specify our composite primary and which therefore must be created. We'll call it favorite-id.class. It doesn't exist, we create it. Yes, so it will implement Serializable. It has a UUID, a song-public-id, a string-user-email. It has an empty constructor, it also has a constructor with two fields and a getter setter finally we will also implement equals and hashcode for this class we now pass when creating our repositories we create a package repository we then create the interface song repository on extend jpa repository with song and long here we add no method we will do it gradually when we need it create methods then we will create our content repository like interface same on extend jpa repository on Of type its content and long and finally the favorite repository which will extend the same jpa repository that's it for our repositories next we create our output objects so we begin by creating an application package and we will create a package value object in these value object on there will be two called song author which are records vo yes adds which must be the field it must not be blank and we will call it value then we will have a song title vo which is also a record and which should not not be blank also that's what we're going to use a little later in our DTOs we will create a another package which will be called DTO and which will contain our input output objects first it's going to be we're going to create a save song dto save song dto which is also a record so we must will add our value object so the song title here we are going to move on to the line it will be simpler it must also be valid song author vo then we will have the cover which is a painting byte here we have the cover content Type which cannot be zero either and which is a string the cover content type and then we have the file which is also a byte array therefore which is the sound file and finally we have the same title that the cover we have the character string for the happy type of the sound file here we have the character string for the content type of the sound file here we have the file of the DTO page This one is much simpler, it's going to have a UUID which is the public ID and it will have a byte array of type glob which is called file and just the file content type. The object will be used to be able to return the content of music for our heads. Save this one, it will help us to persist. And now we are going to create the object that will give us allow you to just read the information. We'll call it readSongInfoTTO. This one on the other hand is a class. It will then have songTitleValueObject which is called title. She's going to have a songTor, the same. We're going to have a cover, which can't be zero. We're going to have a happy thong type, which cannot be null. So be careful, it's cover content type. Then we have... Boul�en, read, and we have UUID which is the public ID. We generate the getters and the setters. We are good for this DTO. We only have the favorite left song DTO and we will be good for this part. So the favorite song DTO. This, a record, is not zero. So we have a favorite Bouleen, a UUID, public ID, which is him. Must contain, then base, zero rating. So we are good for the DTO part. Now as for the part previous, we will create the mappers. In the application, we create a BackageMapper. With, as an interface type, we create a SongMapper. Notation, map. We are not going to add it because these are a little more complex. We will see them during the service. And we will create the SongContentMapper, which is also an interface. We'll just note it. Last thing regarding the persistence part, we will have to configure Spring to indicate to it where are our different repositories, domain, etc. To do this, we will create an Infrastructure Package. And in this Package, we will add another PackageConfig. In this PackageConfig, we will add a class that we will call DatabaseConfiguration and which will contain our annotations which will allow you to configure Spring. We add ArrobasConfiguration. We add EnableJPREpositories. This annotation will allow be able to configure in which Package are our repositories that we created. In my case, it's frCodeCake. SpotifyClone.UserContext.Repository. I also have another one in CatalogueContext. We also need to enable transactions and we also need to activate the Audit part. We also need to activate the Audit part. We spoke earlier. With all that, normally, we should be pretty good already. So, next step. Now let's move on to the part where we will configure our Auth0 tenant. I invite you to open a browser on the following URL, so manage.auth0.com. If you don't have an account, I invite you to create one. And so I, for the occasion, I created a new spotify clone tenant we will start with create a new application so by going to the applications menu we create application we select regular web application we go call it spotify clone we click on create you can go directly to settings then in allowed callback url we will put both following addresses therefore http localhost 8080 slash login slash hot 2 slash code slash octa we go also add the address of our front end which is on port 4200 now we will also add the logout url if it will be our two urls to disconnect we will also set the allowed web origin so that there are no worries will also put the allowed web origin so that it there is no problem in Corsica and it is everything normally for the configuration of our domain so we press save to save and now we are going to go to the branding part since we are going to personalize a little bit even a lot our login so that it looks like as much as possible on spotify so we will go in customize options so ok enlarge a little a little so that we can see better so in colors the primary button let's change it primary Button label it's the same thing the secondary button border it's not the same color the secondary label button it's the same the base focus color it must also be green the base over color it's good the link focus component it's not blue but it has to be green and From Spotify. The header must be white, since in fact, afterwards, we are going to change the background, and the background it will be black. This is why we reverse a a little bit of all the colors. The background widget, the border widget, it's good. The input label placeholder, it also changes a little bit. The input fill text, it turns white. The input border is the same. The input background turns black. Icons are the same thing. And just, success. We're going to save that already. Now we're going to go to font. In fact, we are going to add the noto font 100, just to be the same. At the level of our border, here, it's this one. It's a pixel. And the rest is good. Then, in widget, we will add the icon and we will put that of Spotify. We can even enlarge a little there and the patch background we are good look if we do try so there you see that we have a look close to spotify ok now let's move on to the part configuration of our back-end to do this we will go to applications again we click on spotify clone and we will copy our client id and our client secret so I return to my back-end project I click on my env point therefore my file and we will add two keys we will add hot0-client-id I will copy paste mine and And I'm going to do the same for the secret shopper 0 secret shopper now that I did that we go to the application point we will have to pass our values into Aspring Security and the octa package so I type octa o2 issuer therefore the issuer is the address of your holding for me is I can take here https colon slash then here is the client id if I pass the environment variable so for me it's hot 0 underscore client underscore id and the same for the hot secret client 0 client secret ok now that that's done we're going to be able to configure spring security we will go in the infrastructure point config package and we are going to create a java class called security configuration of this class we will add to it configuration notation and it will have a unique method which will be called configure which will return a security filter chain we will note it at the bottom of this pin since it is a good spring this method is called configure it takes http security parameters and they will be exception at the level of our parallel we will start by listing the urls that have the right to be called publicly and the requests that have the right to be who must be authenticated we will therefore use the object authorize authorize a At line .requestMatcher type HTTP method get and the address of the endpoint, it will be API songs since it will be the endpoint which will allow our users to be able to list the songs. So that's OK, it can be public. Then, the second method which can be public, it's search. We will allow our users to be able to search for the songs they want. That's it, so it's the same thing in my permittals. And for the rest, all the others requests, they must be authenticated. Next, we will configure the CSRF. So CSRF, we're going to do .tokenRepository. It's going to be cookie based. There you have it, with HTTP only. And we will add a tokenRequestHandler to it of type CSRF tokenAttributeHandler. There you go, all that is configuration. To configure the CSRF token, on the Angular side, it's also automatic. We configured it when the time came. For the rest, we will configure Spring. In OAuth2 mode, we will tell it OAuth2 login, we're going to put a withDefault on it, we're also going to call the OAuth2 server resource with a object so OAuth2 and OAuth2.jwt withDefault and finally OAuth2 client withDefault, we don't have All we have to do is return our http.build, that's it That's it for our security configuration. Now we are going to register our users in the database when they connect, for that we will have two different cases first is if we don't know them we simply saves them and the second case which will be a tad more complicated It will be necessary to update them if necessary. So to do this we will start by creating a method in our repository so we will go to user context, repository, and user repository. We're going to need an optional method so which returns us a user and that's fine be findOneByEmail, this will allow us to search our users by email. Once we have that we will create a new service class in an application package, user service, we will This service will have we we will need the user repository and we will need the maper it can be final and that's why we're going to need utility methods the first will be a method which will map a user with high formalism now we will be able to write a method which will map the content of the jwt token which comes from high zero use that we will be able to persist in our database this method is private it will return a user to us and it is called high map of attribute to user it takes as parameter a string map of objects and we're going to call it attribute we're going to import we're going to call it we're going to instantiate our user in doing a new and now we are going to extract the information one by one dot get sub we go make string username equal null if attribute point get preferred username is different from null then username equals string of attributes We are going to do a to lower case then we are going to do an if attribute.getGivenName and different from null user.setFirstName we will cast to string and we're going to say attribute.getGivenName there you go otherwise attribute.getName not equal to null then user.setFirstName always the same we cast to string attribute.getName here we go make an attribute.getFamilyName and different if it is different from zero then we make a user.setLastName of string attribute.getName then if attribute.getEmail is non-null we make a user.setEmail we need to cast it string let's do the same .getEmail here small Subtlety we need to make a sub.contain type and username is not null and username contains there if this is the case we make a user.setEmail equals username If it's still not in the case we make a user points this email equals sub last block we do an if tribute point get picture it's different no one finally user points 7 a.m. url is in thong and we make an expensive get All we have to do is return our user and we are good for this method which was quite daunting now we're going to need a method that will allow us to update a user if necessary then this method is private and we will call it date user will take as parameter a user we will fetch our user in the database data user to date optional we will call our user repository using user fields points get email if this user if we get to it find we will first extract it from this way user point get email we will first extract it like this user point get user everything is dated here and we are going to update the fields who interests us?bije mail user points and email that outside everything update posit image url north get mastered l user 17 las namen use Get Last Name killer says it all point on test the usources points busca ister once we have that we save we save an arrow and we are good there method according to We will have to arrive at extract via Spring Security the information which are in the JWT at the time of the request. To do this, we will return a readUserDto and the method, we will call it getAuthenticatedUserFromSecurityContext. The object is of type ODEuser, we'll call it main. We're going to cast him since it's not going well be the case in Spring Security. We will call the securityContextHolder, we are going to do a getContext, a getAuthentication and a getPrincipal. Then, our user will use the map method. We will give attributes since, like you can see it, the getAttribute, it's a map of string object and This is the parameter of this method. It will return us a user and then, we will be able to use our mapper since we are going to transform a readUserDto. Sorry, we are going to transform a user into readUserDto. Small font, here I put update instead of updated. Now let's move on to writing of the endpoint which will allow the front to retrieve information from users when they are connected. To do this, we will create a package in the userContext which we will call presentation. We are going to create a Java class that we are going to call off. OffController, or rather offResource. We will note it with restController and offResource. With request mapping the address will be slash and Pierre and we are going to need our user service that we are going to inject it and we are also going to need of an object called clients registration we're going to need this one for the lockout on this we will generate a constructor and we are going to make our first endpoint which will be that of retrieving information from the user who is logged in this method it will return a response entity of type read user dto it will be called get authenticated user and as parameters it will return a note she will take in parameters sorry another user which will be annotated at authentication main that will allow spring to us inject it when calling this endpoint endpoint it will be annotated at get mapping and the address of this endpoint will be slash get authenticated user simply the body of this method we will do an if obviously it the user must be different from null if it is equal equals zero we will return a new response entity with http status internal server error on will retrieve the information call it user from authentication and we will do a user service Point get authenticated from security context this way we recover the information that are present in spring security so there we returns response entity point ok point audi du user from Authentication and there we are good next method this is the method to be able to disconnect our user therefore this one will return a response entity we will call it logout and it will take parameters http servlet request we will call request on will note it post mapping and the address will be logout we will first call the registration with a get provider detail point get issuer uri we go create the variable then we will create another variable origin url which comes from the request http header point origin we will make a table of objects which will be called param which will be issuer uri registration point get client id and origin url then we will have to format a message message point format sorry message format we will import it's going to be 0 and this method has the deparameter here that's why there was a little problem so v2 slash logout client id equals return to equals 2 so that's it to disconnect from o2 then we do request point get session point invalidate and we return response entity du point ok point body map of logout url logout url yes because I have to create it here variable called logout, we're good for this method then small piece of code missing little typo here we need to inject a client and Ce registrations, here it must be equal to findRegistration, which must be equal to Octa. So. If we reduce here and try to run our server normally, everything must go correctly. There you go, it's perfect. Now, we come back to the front-end side and we will configure it so that it can communicate with our back-end. Then we will make him consume both endpoints, logout and getAuthenticatedUser, that we created just before. First, we will configure a proxy to avoid CORS issues. To do this, we will create a file which will be called proxy.conf.mjs at the root of our project. In the latter, we will put an export default, an array that will contain an object and which will have several fields, therefore the field context, where we will specify the addresses, which he must proxify, therefore o2 and slash login. The target, for us, will be our back-end, so http://localhost 8080 and secure-true. That's it for the conference. Now we will find ourselves in the angular json. We will look for the dev-server line. We will add option. Like this, option, it's an object with a build-target field which will be spotify-clone. Colon, build and the proxy-config attribute, proxy.conf.mjs, therefore the name of our file. There you go, our proxy is configured. I need to be able to run my server in the normal way. This is perfect for the moment, we will minimize it we can close our files we will head now in the app config in the app config we will add if I move a little bit to the line if too and I will add the http client like these with the configuration of the xsrf token if therefore the cookie name xsrf-token and the header name is x-xsrf-token like that it's done, we don't have to think about it anymore, we're fine allow to inject the http client now On will generate our two environment files for be able to manage our different backend addresses. To do this, we go to a terminal and we will type ng g generate environment. This will generate two files in a folder. We will add In this one, Point URL API, we're going to put slash slash localhost at point 4200. We do the same thing on the other side. There you go, our environment is configured. And we will now move on to creation part of our model. So, we will create a service directory. And in this service directory, we go recreate another directory with template. In this model, we will create a file TypeScript which will be called user.model. This file will contain a interface which will be called user. And which will contain a first name of type string, a last name of type string, an email of type string, a subscription which will be of type subscription, we will create just after, and an image URL of type string as well. We will create our enum. Subscription here it's premium and free so that's it for our model we are good we will be able to pass in the service part we will generate a service in the service folder which will be called off there you go we open our service in the latter we are going inject the http client like this we will also need rental is an angular service we will create a variable not connected which will serve us a little later before writing our two signal and our fetch method we will have need an interface that we will use between the service and components this interface we will create it in model and it will be called state the code for this model is in the description for I'm just going to copy and paste it, there's no of particular value to what we type to them is just an object with value error status fields and then a builder class which therefore allows of builder for errors for successes and for initialization once it's done Towards our service and we will be able to instantiate our first signal that will be called fetch user or dollar which will be private and which will be of type writeable signal of type state user http error response we will import all that so it's not popper jscore but it's our slash state pattern which is going to be equal to the line to http user signal state point builder error response for success email and six dots not connected small typo here fetch user so the second signal which will be our public signal it's going to use computed and it's just going to derive it from fetch user dollar then our fetch method it will return void and inside we will make our call http get or get which is going to be from type user the address it will be dollar environment dot api url slash api slash get authenticated pull user then we can do it directly subscribe inside this subscribe we go manage the next case and we will write in the signal by making a set of state point builder point for success user http error response for success On send the user inside and we do a build. Now for the error case. There, it will be Error. We will have to do this on several lines. So, we're going to put an if. The status of the error. Ah yes, the error status. We're going to type it here. It is equal to HTTP, Status Code Unauthorized. And the user is not authenticated. We'll create that in a moment. So, we're going to write to the signal. Almost the same as that. Except we're going to send email. We will send a ThisNotConnected. If the error is anything other than that, so we're just going to return an error. State Builder for Error. And we return the error. Now the IsAuthenticated method. It takes nothing as a parameter and it will send a boolean. We are going to make a if. This.FetchUser. So if there is... A value in the signal. We're going to go back. This.FetchUsers.Value.Email. And we're going to make sure that this email is different from NotConnected. If it returns something else, then it returns false. Now, the Login method. It will not take any parameters. It returns void. And we're going to do a... Location point href which will be equal to location point origin dollars with brackets this location point prepares external url and the url that going to be high slash permission slash octa that's all we have to do for the login then the logout is the same it sends void we will use the http client to do post the url it will be $environment.apiurl The body it is empty and we will have to specify a small option to be sure that we send correctly cookies even if it is not the same domain. We go to the line, we subscribe, we go put the next case and in the next case we will do a this.fetchusers.set I can directly copy that since in fact we are going to return A user simply not connected and then here we will make a location href equal to response point logout url and I forgot the response here which is of type href Issued in this case so we are good for our service little forget above at signal level wears out it is necessary to add a build point so that there are no more errors compilation now we can do a little checking and running our angular cli server to check that everything compiles correctly here so we're good now that we've finished our authentication service we will be able to move on to display part our task will consist of creating one and hours as is the case on spotify with before after and the login and sign up buttons this way we can manage the authentication displayed to our user if he is logged in or not to do this we go to our terminal we go add by composing in the yachts that we are going call and hours this component we will open it we will start with the ts part we will begin by implementing at the limits because we are going to need it we will also so as not to forget it import the fontos are modules because we are going need to display icons next side template we will also need the offer of Service we are going to inject it we are going to create an object connected user be able to know when the user is connected that we will initialize with this of service.notconnected and we will also inject the location service we will now instantiate an empty constructor we will write instead in this constructor we are going to do effect and in this indeed we are going to go and see if we receive a value of the fetch user signal if its status is equal equal to ok then there is a small error in the service here so in fetch user must go so on this line and I forgot the small ones parentheses that's it because otherwise we don't have access to the object and the signal now I can do my status so I have my if and in this if I'm going to make a this.connected user and I'm going to assign it since everything is ok to my of service dot fetch user with the parentheses and I will recover the value that's it we can go there seen that in any case it can't be undefined once I have that I will be able to write my login method which will send void which goes all simply called of service point login this method for callback It will just redirect my user towards the back end so that the back end redirects it to 0 Or authenticates it next in the case we will write logout if we simply do a this point service point logout and we will also write for the two small arrows so go backward is the same it's void and this time we just do this point location point back and for go forward same void and this point location point forward finally last little thing we're just going to raise the ng a little bit we init we will call off service point fetch to retrieve our user's information and we are good for our service for our sorry component we can now move on to the html part so we will open the header component point html we delete everything that is in it and we will put a nav tag which will have a attribute class so rounded top 2 navbar sticky top background dark z index 1 and padding 2 in this navbar we will have another div which will have a fluid container class another div which will have a deflex class justify content between align item center and a width of 100 still in this last one we will have another div Which will have a deflex align item center class in this div This time we will have a click we will call go backward because this div will be used for show the arrow and it will have an attribute class which will therefore contain margin end 2 Rounded 5, Border 1, BG Black, Router Icon, D Flex, Align Item Center, Justify Content Center, sorry. And in this div, we will have our Fa Icon which will be called Chevron Left with a size of 2. Then another div which is going to be this this time the Forward which will be called Go, the Go Forward method, with the same classes, apart from the Margin End. So here, we will simply copy. And then, the Icon ending which is called Chevron Right which has a size of 2. Then, here, we're going to open another div. Inside, there will be a If. And in this If, we will have an OffService.isRef. If this is the case, we will create a another div with class D Flex, Justify Content Center, Align Item Center. Inside this div, we will have a class OverAnimation which will contain an A tag, which will contain a click with just login. Quite simply, a Margin End 4 class. And this link will be signA. Then we will have another link with a attribute so class with btn light button and over animation we will add the click with the login method and the text of this link it will be login then at the else level we will put that this is if our user is logged in if it is connected we will have a btn-light button over animation with a margin end of 2 and click level this time we are going to call logout and here we are going to say logout below we are going to have a link with an if if the user has a avatar then we will display it in a tag img with src so we will make a connected user point image url we will put a small attribute alt so user avatar class there it will be we will have width 35 and height 35 also if he does not have an avatar then we will display an generic icon which will be called just user with an avatar-unknown class here we are going to add a little bit of css since there are classes which are not bootstrap classes so we have for example router-alte Or the size font is equal to 10 pixels we also have here the width which will be equal to 30 pixels light it's the same we are going to have padding top at 2 pixels the cursor point to get the glove we're going to have a class avatar with a border radius of 40 pixels and an unknown avatar with a border radius of 40 pixels a font size of 22 pixels a width of 45px, a height of 45px also, padding-top 4px, text-align-center, vertical-align-center, and background-color, we will put white, and color, these are variables, so we will have to that I add the import of our variables. For this, I have to do sets, slash css, and variables like that, like this. Ok, so now if I go to the side of my browser, I should refresh like this, but yes, nothing is displayed, this is normal, since I have to add my header here, so I say header in this way. So the icons, they are not displayed not very good because you have to that we import them, which makes sense. It was chevron-left, chevron-right, and user. Now we have our two cool buttons now here we don't have the over of the buttons and this one does not have the cursor pointing we will fix that right away because I forgot a css class we are going to do over animation over and we will add transform scale 1.09 there you go so with that we should have the effect here we have the effect with the sign up ok now what we can do to not bother too much will add here cursor pointer and normally we should have here the cursor with the problem the problem is resolved we will be able to check if the login works before that, make sure that at level of your server is running well your back end so if necessary you have to restart it now I'm going to click on login so it redirects me well I don't have an account so I'm going to click on sign up I will enter my information here I click on continue I accept here that here I am well connected so it worked correctly now if I log out there is a small problem then we have a small typo of the side of the back end so in log out there is client id here which must be client underscore id so we will restart our server we will try again and this time we are good so if I click again on login I don't need to log in I I'm going to try again to reconnect once there and then it works fine I log out another check that we will be able to do is check in the local database that our user is present I can go to my workstation is free and I can view data all rows so you can see that here I have my information Now let's move on to the part importing a song via a form. We will start by creating our model. So we go to Service, Model, and we will create Song.Model. We will start by creating an interface which will be called TitleValueObject and which will contain Value. It's going to be a thong. Then, we will have another interface which will be called AutorValueObject and which will also contain a Value. We are going to have yet another interface which will be called SongBase and which will contain all the basic fields of a song, at namely the PublicID, the title, the author. So, sorry, because the title, it's obviously a TitleValueObject, and that too is the author. Next, we will make our interface which will allow us to save a song and which will extend SongBase. This interface will have a file. Which will be of type File. It will have a ContentType file which will be of type String. A cover which will also be of type File. And a ContentType cover which is also of type String. That's it, we'll add more, but for the moment, we can stop here. We will now move on to our service. So we go to a terminal and we go create a service by doing nggsService. SlashSong. We open the service file its service points and we inject our client http we create our private signal like these error is what think why can't I import but because state the import is not good there we will go to the line and state point builder which will be of type save song http error response for unit mobile then we will have our ad signal which will be a had head conspiracy 10 points had with a dollar we can now write our service had which will return void and we will make a 10 points http point save song post the address that going to be environment point and stone and euro is slash and stone slash are we will have to send a form of achievement that we are going to create we are going to create it immediately above besides had it is necessary that they take a As parameter, a SafeSong. We will create our FormData. Since in fact, the data, we are going send them in good multipart. We create a new FormData. I'm just going to check that import is definitely the right one. I think so. So, we do an Append. We're going to do a first Cover part. We will put the file of cover of our song. Next, we will put the Song.File file. Here I can tell him that anyway, it will not be Undefined since the validation, we will have done it just before. Next, we will have to clone our object. We're going to make a StructuredClone of Song. Because in fact, I'm going to remove clone both files to be able to send the rest of the information in JSON. I can just do a FormData.Append with the ADTO and I will send the JSON as a string. So. I have my FormData. Now I can subscribe and take care of case when the server sends me back my SavedSong. So here, I'm just going to do a sys.add.set and we are going to make a state.builder of type SavedSong.http.errorResponse and we're going to send the SavedSong back. Yes, because, little typo, It's a great success. So. The error case will be, we're going to copy this line. We're going to say forError and we're going to put an error like this. We will also create a reset method in the goal of being able to reset our form and content of the addState signal. Builder when we want it safe song http response point for unit build here we are good for our service now before moving on to the actual writing of our form we will need a way to be able to notify our users throughout our application easy way so we will create what we call a toast service which will allow us to display notifications anywhere thanks to a simple Angular service then we go to our terminal we add a service which will to be called toast we add a table to it which will be called toast info which will be a table which will contain our toast notifications info it's an interface so we create everything from following the toast-info.model model we write export interface toast info it will contain two things the body its body and a class name now we can import it and we will create a show method which will trigger the display of this toast it will have two parameters which will be type type and body so type he will have two values at the time of display of this toast it will have two parameters which are going to be type type and body so type will have two values as possible success or danger and type it will allow us to decide on the class name so we will say that if type equals equal danger then class name is equal to bg danger and text light otherwise class name will be equal to bg success and text light whatever we going to use actually here it's The toast service from ng bootstrap so we what we do we write right here is a method that will allow us to make things a lot easier but otherwise we simply use the booster toast then we're going to call our we're going to add a pardon element in our table so it is a toast.push of body then we will form our object which will be toast info equals body and class name here it is and then we just have to do we're going to just do a this.toast.push of toast info all simply now that we have our service we will be able to go to our app component in firstly we will go to the ts and we will inject our toast service like this now we go back to the html and we will add a div here with an absolute position class z index 2 margin top 3 toast container we are going to make a fort this fort he iterated on the table which is located in toast service here it is and we're going to call our ngb toast which will have a class which will be equal at toast point class name we just want to close the tag to have auto completion here it is then we will have the self-help which will be equal with holes we are going to have delay which we are going to set at a second hidden we will call toast service point remove toast that we will have to create it and inside we're just going to add toast point body we're going to create this method immediately takes toast and inside we are going to make a zis.toast is equal to zis.toast.filter toast and different from toast that's it So now I should be able to test if my toast works by doing a HelloToast show. That's it, so let's try. There you go, it worked well. It's just not very well placed. We need to add at the level of SCSS the ToastContainer class which will contain bottom 13% and right 35%. With that, normally, he HelloToast should appear. Small font here too, it's a success. We will now be able to move on to the part creation of the form itself. Firstly, we will create our component. We're going to call AddSong. Then, we will open the .ts file. We're going to clear out a little bit. By the way, we will... Before attacking the code, we are going to create an interface. This interface will be useful to us validation for our form. We'll call it addSongForm.model.ts. We are going to do a type export. We'll call it createSongFormContent. This guy has a title which is FormControl type with a String type. Then we have the author who is the same, of type FormControl, of type String. On a Cover which is a FormControl of type File or Null. And File which is of type FormControl, the same, of type File or Null. Now we move on to creation of our form on the code side. We will first import ReactiveFormModule. We will also need NGBAlertModule and some icons. So, we will also import the sum base modulates. We are going to create an object which will be called songToCreate which will serve as our object which we will fill in as we go along the user will fill in the different fields. For now, it's going to be an empty object. We will use the FormBuilder. We can also put this one private. FormBuilder. We inject. We will also need the songService. We will also need the router to redirect our user once he has finished adding. His song. And finally, we're going to need... Toast service, we're going to need toast service, we will see immediately afterwards why there has a problem, and two last variables, we gonna need one, are we in mode creation, that is to say we wait for the return of server, and do we also need to know the flow status, which is of the flow status type, which we will create in a moment, and which will be equal at init, this is a type that we will create above, type flow status, which can be either of type init, either of type validation file error, or validation cover error, or success, or error. So actually it's because I forgot that, there you go, we're good. I can pass now when creating my form. public create form which is equal to form builder.non-nullable.group, we're going to create another group. This group will be of the create song type form content, and here I will be able to specify the name of my fields, form, and I will be able to instantiate them. Value is like that, the field is non-nullable, anyway, and the, ah yes I forgot, here it is non-nullable, and the validator, we have it here, and this field is required. Then we're just going to copy the others fields, since they are the same, that's it, anyway they are all nullable, if one fields is null, in all cases... It doesn't work and we block validation. Now we will move on to writing the method which will be triggered when the form is sent. This method we will call create, it will send void. We will start by putting the boolean isCreating is true. Then we will do some validations, we will look at songToCreate.file it is, if it is zero then we will display a general error message. We're going to do the same for the cover so songToCreate.cover equals equals zero and same we have a flow status which has the right value. Then we will come and create our two objects, our two value objects that are of type title value object therefore value and inside we will extract the title that is in the form, value.title. We do the same for the author value this.createform.value.author. We now fill in the fields title with the value objects that we just created above like this and then we call our service so the song service, we do an add and we send the songToCreate, that's it. Then we will need two methods to be able to manage when the user he will come and upload the file so song and cover file. To do this we will create a private method Which will be generic between the two methods and we will extract the file from Du champ upload we will call it extract file from target the argument is target which is of type event target or it can be null and this method it returns a file object or null we type const html input target equals target as html input element if this target it is equal to zero or if the html input target point file it is equal to null then we return null otherwise we return html input target point file and return the first file then we will take care of the cover so on upload cover which takes an event as an argument target or null we create the const cover which will be equal to This.extractFileFromTarget we pass it parameter the target object and we check that cover is very different from zero if that is the case we can put in our object so songToCreate cover equals Cover and we do the same for the content type to know the type of the file we do cover point type quite simply we will do the same for I'm going to copy paste we do the same for the file is an upload file that's file and here it's point file and there it's file content type now we are going to go back to the top of our file and we are going to implement our component destroy implement this on December a little bit here we are going to call six points are service points that's the point of that is that when the component is destroyed we will reset the signal in its borderline state it was the service we had written just before then we will create a constructor like these and we will add an effect in the latter so that we can react when the song has been created that we have feedback from the server the first thing we do we're going to put the is creating a false since in fact this ball 1 it will be used to display a small icon of loading to be able to signify our user that he has to wait then we are going to make a if song service point add signal if status it Is ok I refresh and My list, so this one we don't have it yet, so for the moment I I'm going to comment on it, we'll uncomment it later. Then I'm going to call my toast service with a show and so I'm going to mark song created with success and it's a toast of type success also we will call the router and we will redirect our user to home, like this if there was an error, so I will test song service signal, if the status it is equal to error so I will display a danger type toast this time with error when creating song please try again occurred it's danger type we're good for the code of our component now we move on to HTML now we are going to go to the file app.route.ts where we will add our component addSong in order to be able to display it then we add an object like this in the table route with a pass attribute the pass will be slash add pulled song and the component it will be addSongComponent if we execute then if it is already done just fine, otherwise you can run again your server we come back to our browser ah yes, so you just have to remove the slash there you go, and if I press I have addSongWorks very good, now we are going to open the HTML, we erase what we have here And we create our first div with our flex class flex column and align item center. We create another div with an attribute class, with a width of 75. We put a title with a class attribute margin bottom 3 and a start text. And we put the title add a song, without spaces here. Now we will be able to write our form. So form with id create form ng submit form. We will call the create method like this and form group form group our form is called create form inside this form we add a div with the class therefore form floating a margin bottom of 3 secondary text colors our input it will have therefore it will be of text type since it's the title it will have a form class name control which comes from bootstrap we will put a Haiti title will go to the line at the level of validation will make a class point is a no point is valid and this condition is created form lookout point title valid points that's it this will add the class is valid if this field is valid like that we will finally be able to stylize bootstrap not able to style and means to the user if it is good or not for the isin valid it's the same thing we make a watch with title and inside a point dirty dirty it is if the user touched the field then a cry in the form of a lookout point again Title and now we check if it is invalid, this is how we do it will also put a placeholder title on it and form control name which is title here is this input it has a label for title title and at the level of our validation we will put an if create form point get title point dirty and I'm going to copy it it will be faster point as error the error is therefore required then we display a div which has a class invalid feedback and therefore we must close this div and inside we will mark title is required here for the first input title on can check here, we have our title well our input which remains correctly for me is can see here icons which display well with validation now for the author field will copy paste what we have already done like this and we are going to rename author here author author like this author and author is required voil� I have my second field which is not validated very good because the form control name was not not the right one and that's it, now it's good we can go to the queue, then this one will be a little bit different, because he is type, so there he says it's gone, and here we just go to have, well not to have an invalid, we are going to have a placeholder, the form is done, we will have an accept which will have .wav.mp3, there we go accept both, so we have the valid class, and we must also call change, change, and we will call upload file with event.target, here we are, we are going to format a little bit, here it is file, and file, there you go, and the label, sorry, the label we will instead put mp3, validation here, the if, we don't need it, that's it, and we will take the block here, and here we will put id cover, cover, type file, here it is cover, besides there it's file, there it's cover, cover, here guys, we are going to put jpeg, jpeg like this, png, and .sg, so there we have that, we have good cover, the change is on upload cover, and no don't file, and we should be rather, not bad, we we have our fields here, very well, we have to here change the mp3 label for cover, we also have a style part, which we will have to add, Here, to correct a small defect, at the level upload fields, so we're going to put a little padding, we are going to put a padding of 9 pixels, 0, 0, and 10, like this, and an auto height, there you go, And once we added this style, we need to add it to both file type fields. There you go, here you see, we have fixed this place a little. Very good, we can now attack the part with the buttons and the alerting part. We will therefore add a div with a class of grids, a button with a button class, button primari, which is disable if the form is invalid or if we have a song being created. This button is of type submit. And if we are not creating, we're just going to put a div with add. And if a song is being created, so we're going to put a div with a class. Flex align item center justify content center with an icon that will be called circle notch and a margin end 2 class. And a little spin type animation with a small div in which we will mark adding in progress with three small dots. We will immediately add before forgetting the icon in fa. Circle notch, we don't forget that, in our icon file. Now we can validate in our browser. There we have it, so if I fill in my fields, Here I have my ad which works correctly which is deactivated now we will move on to the handling global errors to our form for example if something happened abnormal when we sent the information to server then we add an if like this and we will test the flow status variable so if flow status is equal to error or if flow status is equal to validation cover error or if it is equal to validation file error then we will display a ngb alert is not therefore dismissible which is danger type and which has a flex align class item center justify content center and margin-top 3 this ngb alert it has a fontosome icon with alert name and alert name it has a class margin end 2 an icon circle x mark with a size of x here is this icon we're going to add it right away, it's fa circle x mark like this then we will add a div which will contain the different errors so if the flow status it is equal to validation cover error in these cases something is wrong or something is wrong With the cover the same with the file with the mp3 file and if there is a general error so when create the song now we will test quickly for our form to send the data at the back end even if we don't yet have a head point then I'm going to connect like this and I go to Add Song, I I'm going to add some music, I'm going to open the inspector, we're going to go to Network. When I do Add, obviously I have a 404 because the endpoint does not exist not, but we can look at the payload. I have some binary here and I have my DTO with my JSON, with the right fields to every time in my title, author, etc. Now we are going to head from side of the backend to create our endpoint which will create our song for us. To do this, firstly, we will have to fill and add methods to our mapper. We go to the SongMapper and in this SongMapper, we are going to add a first method which will return us a song which we will call SaveSongDTOToSong and which will take a SaveSongDTO as input. There are two fields. We'll have to ignore it. The first is ID and the second is the public ID, since they don't are not present in the SaveSong DTO. So there is absolutely no point. Either way, it wouldn't be possible. Second method that we will need, we're going to need to have a method which returns us a readSongInfoDTO Which is called SongToReadSongInfoDTO. And who takes... Setting a simple one we just need one mapping here a single notation with a target which is favorite since this part is loaded by another table that's the same favorite part it's another table now we are going to need small methods that will map fields that are internal so for the value object exactly so it will be this method before sending a title value sound object called string any title value object string title and it will return us a new song title value object simply on will do the same for the authors because song author value object are authors value object are authors and finally the same but in the other meaning so this time it refers to and finally the same but in the other direction so this time it refers to a string and there it is song title value object to string and it takes as input a song title quite simply and we return title point value and we do the same for author copy this method song author value object song author value object that's it and that's it and we do the same for author copy this method song author value object that's just author That's just author that's it note my fear and ok now we will move on to service part so we create a new class java we will call song service in the package application we will rate it with arrobas Service like this this service we are going to need our maper its maper we are also going to need the repository therefore private final song positori on will also need the final song song content repository and we will also need the song content mapper I create my constructor for inject her four anvani mines a little bit the line and we just need to add a few methods in the song content mapper then the first method it will return a happy song dto and it will be called song content all its happy eto she takes parameters a song happy we will also add the notation by base mapping which has as source song point public haiti and the target public Haiti like these the second method it will be called it will return a sound happy and it will be called save song dto tout her she will take a safe song as a parameter like these for ap its content it's a table which only contains song files. We do this in order to be able to request them in a different way. That is to say, we have a table for information, a table for the content. This allows you to just ask for it when we need to play music. While on the other hand, we have the other need to be able to just display the songs. We return to our department. We will now create our method. It is a public method which will return a readSongInfoDTO called create and which takes a saveSongDTO as a parameter. The first thing we're going to do, we will call our songMapper and we will call the saveSongDTO method. Any song that takes saveSongDTO as a parameter. Like this, we transform directly our DTO as an entity. This entity, we will persist directly thanks to the songRepository. Here, we're going to make a song and we're going to create on the fly a variable in passing. This way, we will have the information as IDs and everything generated by Hibernate. Then we will do the same for the songContentMapper. We will map with the saveSongDTO to song method. We will extract the binary data and we're going to make a songContent. I need to create with the variable here, songContent and we're going to make a songContent.set .setSong songSaved. Like this. Now, at the level of our songContent, I will be able to call my repository and make a save of my entity. Finally, I can do, here, I will recreate my variable. No, I don't need it. I can just do the return directly with my songMapper.songToReadSongInfoDTO. And I return the songSaved. So much for our service. Now I can move on to the endpoint part and we will create a package in the catalog context which we will call presentation. And in this package, we will create a JavaSongResource class. This resource class, we will denote it with an atrobase restcontroller and at requestmapping, with as argument inside, slash API. We are going to need song in this department service, in this resource, sorry. We're going to need song service. We will also need our validator, like this. We will also need user service. And we're going to need our object mapper, which will allow us to deserialize our JSON. So we're going to make a new object mapper, like this. With this, I'm going to create a constructor. So I'm only going to need song, I'm going to need this. Alright. My method will be public. It will return a response entity of readSongInfoDTO. It's called add. We will note it. Arbase. Arbase. Post. Mapping. With as value, slash songs. And she's going to publish. ConsumeMediaType.multipart FormDataValue As an argument, we will have a multipart MultipartFileCover A multipart file that goes be called file And a character string which will represent Our DTO, our saveSongDTO in string We will just have to add small annotations So that's at arrobaseRequestPart So that Spring can recognize the different parts of the multipart form We will also have the requestPartNameFile We're going to pass this to the line Same for this part here Name and this part is called DTO And potentially it can be IOException In the body of our method The first method, this is the first thing method what we are going to do, we are going to Deserialize our jason in doing so we will make a read value save song and we want to serialize it in save song dto.class like this we will create a variable then this variable we will have to add the information to it so we will have to recreate the information so cover and file we are going to do it this way we are going save song point then little typo here save it song dto it's you have to call it title if we're going to have a mapping problem if we continue we will put save song author it's the same this one it it has to be called autor then we will have a cover point get byte cover point Point get content type exactly then we will have a file point get byte and file point get content type now that we have reconstituted our object we will validate its fields we will do as follows we will call our validator with the method validate and we will play our safe song dto we are going to create a local variable we are going to say that if there are violations we will remind them violation if this set is not empty then we will create a string with all the errors that that there is therefore in this time this object will do violation point stream point map of violation and violation point get property passes over violation point get message to get the info and then we will do a collection point joining like this we can pass it to the line and that also for more clarity then we will create a detail problem with which will be called validation issue and our problem detail we will generate it as follows for status and detail on will send http status point bad request and in we will mark validation errors or details On will pass the string that we built just above above in this way and we are going to return so a response in titi of validation issue point build that's how we managed our error cases as it is necessary finally the easiest passing case we are going to make a response in titi point hockey and inside we will call our service Point create with our object that we created above there you go we are good for our endpoint except that there is a few small typos so we will have to go to song.java here there is a public id which is like this so we will have to factor in a little bit public id like this if i try to recompile and in song there is also the fact that cover it must be a byte array it must also correct the setters and getters there you go in this way it's good two little things additional which will prevent us from adding songs the first one we will have to render in song.java and add the notation at arrobase lob .java and add the notation atrobase lob it works correctly then it we'll also have to go to song service to be able to activate transactions in the methods of our service once we did that we can reload our server we can go to the side of our front normally we press add and normally we must have the toast song created success what we can do to validate and be sure we can Validate yourself and be sure you can make yourself different thing to link Weiwei Poazar then we even have it fly ?? we can refresh here and so was going in post yard and??we can refresh here and so go into post yard and look at the level of our som confused our table song look at the level of our som confused our sound table Here we have a good line which has been added we can also go and take a look of eye in its content to look we have well here our files which are present now we will stay on the side of our back end and we will create the endpoint part to be able to recover all the songs in the aim behind to display them in our front we leave in our song service we are going to do a little bit of empty like this we create our method which is a public method and which will return a list of read song info dto so we import we call it get All, it takes nothing as a parameter. We're just going to add a little annotation to it, transactionAll arrobas, to tell him that This is a read-only transaction. We call our songRepository with the findAll method. We do a stream, we go to the line, we make a map, we use our songMapper, the songToReadSongInfo.tto method, and we then call toList, for just be able to have a list. We can make a return like this, and we have our service. Then, we go to the resource side, so from the endpoint, we write a new method which is public, and which will return a response entity of list, of songContent. We import list, and we call it getAll. It takes nothing as a parameter and we return simply response entity.ok song service getAll is read song info.dto We add it getMapping notation with getMapping address Which is going to be slash songs and our endpoint is good we check anyway that it compiles, our server is running correctly and we can now move on to the front part for our front we are going start by writing the service part so we have first need to modify a little bit our model so we're going to go to song-model we're going to add an interface called read-song and which extends song-bass this interface it has a cover which will be of type string we will have a cover content-type which is the same as string type a favorite boolean that will be a little bit for later and a champ-display-play it's the same, That 's it, we're going to talk about it again, that's it for our interface now we can go to the song service we are going to create our signal we are going to call get-all which is going to be a writeable signal which is going to be type state, type read-song stop type http error, we will now instantiate our signal like this, we will go to the line and we will give it a state.builder which is of type stopping readSong.httproresponse.forinit.build. There you go, we create a second signal, this time public, which will be instantiated using computed, and we will use the get, the private signal. There it is, like this. We can now write our service here, so getAll, it will return void, and we call our HTTP client by making a get type stop of readSong, like this. Get, we take a URL as a parameter, environment.api.url.api.song. Then, we will subscribe to this observable. Next, when things go well, he will send back a song table, and we just go, do a set on our cue private, and pass it the shutdown state readSong with the song object inside. So, just like that, .success, like this. If there is an error, in these cases, we will notify the signal by doing a set of the same thing, except that we will call this time forError. Like this, passing him, sorry, in Error parameter, that's it. We are good at our service. We continue with the creation of two components which will be the first, which we create with ng-gc, It 's going to be Home, and the second one, which is going to be Home. And the second one will be SongCard, that we will create inside Home. This will allow us to display the small cards of each song. So we create that. Next, we will go to app.config. And here, we are going to activate the animations, since we are going need it to animate the little Play button. Once we've done that, we surrender in the app.root and we will render routable our Home component. Since it's the Home component, it's okay be paf, empty, that is to say root. The component is called HomeComponent. A little comma here, like this. We should be able to have something thing, there you go, HomeWorks, very good. We will open the HomeComponent.ts file. We will import the fontosome module, the SongCard component. And that will be all for now. In terms of injections, we will have need the SongService and we will have also need the ToastService. We will also need a variable AllSongs which will contain all the songs. And this variable, we will use it to iterate over it and display the small maps. So it's an ArrayTorridSong which can obviously be undefined. For the moment, we will implement OnInit. Like that. Like this. And we're going to do a this.songService.getAll. We will also write a constructor with effect inside, where we will recover the signal that is in the songService. This one, by the way, I have a small font here. We will recover the object that is inside, which we will call allSongsResponse, a const. So if allSongsResponse contains OK. So, I just have to assign my list to values that are in this object. Otherwise, I still test the Error status. I'm going to call the toastService to show my user that there was a technical problem. An error occurred when fetching allSongs. And that's dangerous. Now we should be able to... We should make the request. So I can move on of my HTML, like this. I erase everything in my component, and I will create a first H1 tag, which will have a class, therefore a margin-bottom 3 and margin-top 3 attribute. I will mark, so, goodMorning, and I'm going to have a div with a flex and flex-vrape class. In this div, there will be... The forum, therefore, which will be songOfAllSongsTrackByPublic. And this one is going to be our app SongCard, which is going to have a class Card, SongCardBackground, MarginTop 0, MarginBottom 4, MarginStart 0, MarginEnd 4. There you go, it will have as input Song, I'm going to give him the song like this, and that's all for now. We're just going to close our tag like this, and we are good for our HomeComponent. We need to add a little bit of style, so we are going to go to the HomeComponent.scss, where we will first import our Bootstrap Variable File, like this, with Assets, slash, scss, slash, Bootstrap Variable. We are going to add a class Card, which will therefore have Flex. FlexBasis 14%, FlexGoro 0, MaxWidth 200px, and CursorPointer. We will also have a series of QueryMedia to manage and ensure let it be a little responsive. MaxWidth 1100px, for Card, we have a FlexBasis of 21%. Then, we will have a HerbazMedia. From MinWidth to 1400px, and the same, Card, the FlexBasis will be at 12%. Finally, last Query, we are going to have a MinWidth of 1850px, with a class Card that has a FlexBasis of 14%. Very good, we can move on to writing the SongCard part. So, we're going to clear a little bit here. We're going to open... The SongCard file. In terms of imports we will need of font awesome module and here we go need a little animation. The animation will be to be able to animate the little play as you can see here. We will try to keep the same behavior. So we will start with write a trigger like this. We're going to do a little import. The name is InOutAnimation and this trigger has a definition. So we have a transition. We're going to import that. The transition is The style will be transformed transform translate y by 10 pixels like this with an opacity argument of 0, we close the object and like this then we will make a animate which takes as parameter the timing which is 2 0.2 seconds of type is out we go have in the style that is going to be transformed and which will be of the same type as this one except that it will be 0 and here the opacity will change to 1 then we will have a second transition which is going to be the book release so I'm going simply copy the book and this time here it is 0 to c10 and here it is is in and the opacity it is reversed here we have our animation we will see how to use it right after level our component will be able to implement init also we will create our input in mode signal it will be input point required of type read song we're going to have to import all that a little and we will also have to import input like this with small display lines since it will we need to modify the state and for the moment It's not possible to do it with signals. We will make favorite false and display play false at the moment. In the ngOnInit we will make a this.song display equals this.song like this and we will create onOverPlay method to show icon animation which will take as a parameter a boolean like this I forgot to put two void points and one So that, we will have to assign so song.displayPlay equals displayIcon. And for now, that's all we need. We will now go to the HTML part. We therefore erase the content and we will create a first div. This first div, it will have a mouseOver which will call onOverPlay to true. We're going to have a second event that we're going to try to listen which is going to be called onMouseLive. onOverPlay and which will pass false. In this div, there will obviously be a another div which will have a cardBody class. In this div, we will have a tag image which will have a class attribute with cardImg-top and cardImg-bottom. Then, src which will be the cover. So like this, we're going to make a dataSongDisplay.coverContentType. plus base64 plus songDisplay.cover. This is because in fact, the image, it comes to us in base64. So, we display it as such. And we're going to put alt cover. Like this. Then, we will have an H5 tag which will contain classTitle and marginTop3. Inside, there will be the title of the song. So a title.value. Next, we will make a P tag. With an author attribute. And inside, we will mark the name of the author. .value. Like this. Finally, we are going to manage our animation with an if song.value. Display.displayplay a div with a play class, position absolute, d, flex, align, item center. We declare that it must be this div which is the animation, so in-out animation. In this div, we will place the play icon with a text class so that it is green and the name of the icon which will be circleplay. There it is, like this. I'm going to import this... Ah, it's already imported. So that's good. Now we need to do a little bit of CSS. I will import the assets file bootstrap variable, like this. We will have title and the author class which will have an ellipsis overflow text, whitespace novap and overflow hidden. We will also have a play class and font-en-sum icon with a font-size of 45 pixels and then a play class which will therefore contain bottom 105px, right 32px, border-radius 50px, background-color dark, border-style none, width 35px and height 35px. Normally, we should be able to go to our browser and we should have this display. So, there is still a small problem with hover level since it is not present. It's quite simply because here we have to adds a class to its card background with a cursor winter a background color a card inactive background and then we will manage the over like this like this background color card over background border radius card border radius that's how this we should have a display here's more close and we have the good animation now we are going to move on to the bookstore part so bookstore this is the part on the left where we are going to come list the songs to do this we will create a reusable component therefore with ngc in The shared folder which we will call smallsongcard. As its name suggests, it will just contain a small card with information, the small cover, the title and the author the map, the music, sorry. Once we've done that, we'll come back to it. We go to component.ts library. Here, we will correct its module authors and we will also import smallsongcard. Then, we implement onInit in this component. We inject our song service. We create a readSong array which is empty. In the ngOnInit, we will create a fetchSong method which will be loaded to call our service, so getAll. Next, we will create a constructor with effect. We will wait for the getAll service to return. We're going to do if this.songService. The get all signal if its status is equal to ok then we will assign the table that we created above the value found in the signal.value like this now we can pass to the html part and in this part we had already made the header we will add a div with des Flex, flex colon, rounded bottom 2, bookstore hate, overflow scroll, bg dark, and padding 2. In this div, we will have our fort, which will be song of songs, and track by song.publicid. In this fort, we are going to recreate a div with classes margin top 1 and song card and padding library 1. Then, we're going to add our app here. Small song card so it has an input song and that's all for now, so let's move on next in the small song card app we create our input input of type read song and then we can directly go to your template where you delete what is there we have already created a div with with a class flex and width 100 in this div we will put an image tag with a rounded to class width of 55 pixels and a height of 55 pixels a src which therefore has data plus content type with base 64 of our cover song.cover like this then we will put another div with a class flex d point flex flex column justify content center and margin start in this div we will put the first div which will contain the title with a margin bottom class 1 the title class and then song pointei title point value second div below this one will just contain author no need for margin bottom 1 don't need margin bottom 1 to a text class below queen dot bottom and we are going to have a motor directly like that. Here, we will have to add a little bit of CSS. We will first import our variables, like this. We will have library 8, which will contain 8 and library 8. Then, library songs, with an overflow, Y, scroll. A library songs card, there is a cursor, point, and a bookstore songs card, over, with a background, color, card, over, background, and border, radius, card, border, radius. Let's take a look at what That 's it, on the browser side. So, we have what we need, on the other hand, There is a small defect in the height. It's because this class. Is reused in several places, therefore you have to put it in the style.scss, color, it's secondary, and font size, font size small. There you go, we no longer have the effect that resembles Spotify. We will now move on to the player part and we start with the back-end we will go to the song content repository and we will add a method optional song content find one by song public id which takes a public uuid as parameter id we now move on to our service we provides a service in the song and we are going to add a public optional method which will send a song content dto and which will be called get one by public id and which takes a uuid as a parameter we call our repository with the method we have just just to create we pass the public as a parameter id we create a local variable of this optional song by public id and then we're going to return a song by public id we will map with our song mapper is the song content mapper song content song content dto we have our service we can so move on to creating our endpoint. The endpoint will therefore be public. It will return a response entity of song content DTO. It will be called getOneByPublicId. It will have as a parameter a request param which will be of type UUID and which will be called publicId. We will denote it with an at sign getMapping. And the address of this endpoint, It 's going to be songs.getContent. That's it, then we're going to call our song service with the method we just created. Then, we create the locale variable which is an optional. We're going to rename it, it's songContentByPublicId. And we will return a songContent.map response entity OK. Otherwise... We are going to do an else get response entity. And we're going to return a problemDetail. We're going to go to the line to let it be clearer. With an HTTP status .http. Bad request, with a detail from, we will mark UOID unknown. So, yes, it's because I forgot the build point. There you go, our endpoint is complete. Now we move on to implementation from our player on the front side. First, we will add a dependence that will allow us to play music in a browser. To do this, we do npm install hauler, a JavaScript library which allows you to play music. We also add the types to have autocompletion in JavaScript, hauler, and we make a .save dependency for them add in dev dependencies. Once everything is done, we can go to songmodel.ts where we go add a new interface. We will call this interface song-content. It will extend read-song and it will contain a file of type string and a file-content. type, string type also, tent-type. Then, we move on to our service. So we're going to create one new in our terminal. We do ng-gs and we will create service slash song-content. Then, we open our service. We inject the HTTP client. And we will create a first signal which will be called QToPlay, which is a WriteableSignal that goes contain a ReadSong stop and which will be equal to a signal with an empty array inside. We create the public signal using Computed. We make a zis.QToPlay. Then, we will create a second signal which will be called Play and which will be of type State, State of SongContent of HTTPErrorResponse. This one will be used to recover the content of music before listening to it. We do State.Builder.Content.HttpErrorResponse. For init.Build. Then, our public signal, the PlayNewSong, which we're going to call. It's Computed with Play inside. So, this signal will serve us, by done, to maintain a queue and each music will pass through this signal. This signal will contain the queue of Song and this signal will contain the current song. Now that we have this, we will be able to create our signal. Method that will create this queue. We are going to create a new view with first song parameter which is of type read song and in second parameter songs to play. It's a read song stop. It sends void. First we will extract the first song from our song to play board. So we're going to make a song to play.filter song and we will say song.publicid must be different from first song.publicid. Then we are going to do an if again, we are going to do un???? its to play so we make a song to play at point a shift of first song. What it's going to do is it's going to put the first song at the front of the table. Then we just have to do this.q to play .set and we send the table in the signal. Afterwards following method we will do the method for that every time that the unawakening either from the heart of wandering, changes music, we will go and retrieve its contents. We'll call it fetchNextSong, song-to-play, which is of type song-content, which returns void. We are going to create a query param, so new http param.setPublicId, and the value is song-to-play.publicId. Then we're going to make another get call, so environment.api.url, slash api slash songs slash get-content, and we add param query param. Then we can subscribe. Marking so we will manage the next case that will suit us send back a happy song and in this next one we will make a this point map read song to song content this method we are going to create it it will take into account song content and song to play parameters and then we will just have to send the song content in the signal thanks to the state builder so this one will be of type song content http error response for success and song content it is just necessary that here I specify in get song content and in case error I made a this point play point set and I'm going to do the same thing here what I I'm going to pass the error into parameters, now we're going to write the signal using the state builder so this one we are going to write our map method here it is our map method will just transfer the fields from one object to another therefore from its content rather from song to play to song then here it is read song and we're going to do song to play point cover song content point cover content type song to play point cover content type sound content point title equal to song to play point Title son content point autor is equal to song to play point autor his favorite content point is equal to song to play point favorite we are good for our service now we move on to creating our player we go into our terminal and create our component in layout slash player Once Once it's done, we go to app-component.html and we will add it directly. We add a footer tag and in this footer, we add app-player and we just add a width 100% class. That's it, now if we comes back, we have player-works. Alright. We now go to player-component.ts. In terms of imports, we will have the font-sum module. We will have the form module. We will have the small-song-card-component. And for now, that's it. We are going to inject the song-content-service. We're going to need a current-song variable which will be of type song-content or undefined. And for the moment, it will be undefined. We're going to need an instance of our player which will be current-all. We're going to import the import. We will do it manually. We're going to import all from aller. We will also need a boolean is-playing which will be false for the moment. We will need the current-volume which by default will be 0.5. We will also have a boolean is-muted. And then we're going to have two different tables. A first one which will be called next-queue. Who's going to be like... Of the stop-song-happy type and which will be an empty array. And another array, previous-queue, which is the same, a song-content stop and which is currently empty. These two tables will help us. To manage the before and after with the forward and backward buttons. Then, at the level of our constructor, we will have a first effect which will consume the queue. We're going to form a new line and who's going to listen a song content service.queue to play. We check if the new queue has a length greater than zero. And if that's the case, we're going to call an onNewQueue method. We're going to create right away. Like this, we will be able to create our queue in this method. In this method, we will record in this class the state of the new queue and we are going to call a method which will be called this.playNextSongInQueue since when we created a new queue, we wants to play the first song in this queue. We add void, void. This method will have an if this.nextQueue.length is greater than 0 then we pass the boolean this.playing to false, if this.currentSong. So we're going to queue previous, first we will put the song current, then we will take the song next, next queue, and therefore from the next array tail and we're going to send it to the happy song service so that it can be retrieved and listened to. That's it. This effect. Then we will have a second effect who will listen there when there is a new song to play. So we're going to do an if this.songContentService.playNewSong.statue="ok". There, we will directly call this.onNextSong. We create this method and this method, it will extract the song, the current signal song. And we will create a new instance of the player. So new all instance, gala new all. In terms of options, we will have SRC, which is an array. And there, we are going to give it data, we will give him the music data. And so it will be this.currentSong.fileContentType, base suite 64, this.currentSong.file, and that's it. Then we will have a volume. There, we just have to pass the variable to it. CurrentVolume, we will have a method onPlay, we will call our own method, we will create it right after, onPlay, we will have onPause, this.onPause, we will have onEnd, this.onEnd, and that's it for configuring this instance. Then, if an instance of all is already present, then we stop it, the news instance, we play, and we replace the current instance, the old one, by the new one. Next, we will create our onPlay method, which will set the boolean isPlaying to true, we will write onPause, which will set isPlaying to false, and onEnd, which will call playNextSong in queue, and which will set the isPlaying boolean to false. Next, we will also manage the buttons. OnForward, Backward, so we're going to create a method. OnForward, which will call playNextSong in queue, and onBackward, which will do a if if there is something in the queue. So, we're going to set isPlaying to false. We'll see if there's a song common, and if this is the case, we will put first in the next queue. And we're going to say... " Unstack the previous one". Song in queue previous.shift Now that we've extracted it, we will send it to the fetch next song service previous song Another method, we're going to have a break. If there is an instance of all, then we will simply call the pause method. And for play, if there is an instance of all, then we call play. Regarding the method mute, we will return void too. If he exists... An instance of all so we will call this point is muted we are going to invert it we are going to invert the boolean we will call mute on the instance of all and if muted is muted is equal to true then the current volume must be equal to zero otherwise the current volume must go to 0.5 and we must go this volume level at all current this current volume then last method we will have the all volume changes which will take a parameter a new volume of type number which sends void and which goes so divide current volume which will divide rather new volume by 100 because we will see that more late it is because of the range therefore of the input if we has an instance of all then the volume must be from this point current volume if the current volume is equal to zero then is muted is equal to true and the player instance of all must go to mute otherwise if one is already mutated then mute must be equal to false and we must remove the mute from the player that's it for the player's part, let's move on now in the template part we open the html we create a first div which will be which will contain therefore flex justify content between align item center player and I have a Then, inside from this div, we will have another div with a class song played des flex align item center. Here we will display the music current, so if current song. So, we're going to call our small song card. We will pass it as a parameter song and the current song. We'll see what it looks like here. So here, obviously, there is nothing displayed since we don't yet have a song loaded. We add another div here with the class of flex justify content center and align item center. We add an icon here which will have a control class. The name of the icon will be backward step and we will call the backward method when clicking. If the player is playing a music, then we display the pause icon which will have an MX4 play icon class. When clicked, we call pause and the name of the icon is circle pause. If this is not the case, we will copy this line. We call play and the name of the icon is circle play. Finally, we get this back. This one is forward and we click. It's we forward then we will have a div with a class of flex justify content between align item center and volume in this div we go have another div which will be called volume-icon with the click we will call we mute if is muted or the current volume is equal to zero then we will display a mute volume icon otherwise if the current volume is smaller than 0.3 then we will display volume low otherwise if current volume is greater than or equal to 1 then we will display volume high finally we will have here a range type input with a class form range margin start 2 which goes have an input of type range with a form class range margin start 2 which will have an input of type range margin start 2 which will have an input of type range margin start 2 which will have an input of type volume Change with $event inside now if we go on that then it we must actually have some css missing and it you also have to import the icons so we have fa backward step fa forward step then we have circle play circle play already present circle pause volume mute volume low volume high and I think that's enough, then we'll move on in css the css we will import the assets sheet like this we will have a player class with a margin left of 14px a margin right of 12px a margin bottom of 9px a height of footer height inside we will have a check with a font size of 8px and a margin bottom of 9px and a margin bottom of 8px and a margin bottom of 25px a secondary color we will then have control but the over version which will be white on will have a play icon class with a font size of 33px and a white color and a cursor pointer we will also have the play icon but the version over which will do a transform scale 1.09 we will have a play icon class with a range form with a width of 130px volume which will have a Width of 130px volume icon who is going to have a child fa icon with a width of 30px and a font size of 17px we also have a range shape with a width of 100px finally we will have a range shape with a song played with a width of 250px. There you go, now everything is a little more in place. We can see that there, it's pretty good. There you go, that too. So our player is OK. Now you will have to plug it in and test it. To do this, we will render in the home component. In the home component, you can get rid of onInit now. We can also get rid of song.getAll, since it is the bookstore that takes care of it. Now, regarding our player, we will have to inject the songContentService like this. And we will create an onPlaySong method which will take songToPlayFirst as a parameter, which will be of type readSong, which will therefore call songContentService.createNewQueue with... with a songToPlayFirst. And then we send all the song chart that we recovered. Then, on the template side. So, we're just going to clear the air a little bit here. On the template side, at the songCard level, we will add a songToPlay here which will be equal to onPlaySong$event. We will create this output in the songCard. And in the songCard, we will have a playSong method, which will call songToPlay.next.this.songDisplay. And so, this method, we will call it from the songCard side. Here, we're going to call it on click. Then, we will do the same thing on the bookstore side. So, first of all, we will go to the smallSongCard. And we're going to go to the smallSongCard. And we're going to add an output to it as we did for the SoundCard. We will call SongToPlay in the same way. This is a ReadSong type transmitter event. We will note it with the base Output. And we will add a Play method which will make a Next thanks to this.song. That's it, this way. Now we can return to Library. In Library, we will inject our SongContent, our SongContentService. We will create an OnPlaySong method which will take in parameter SongToPlayFirst which sends Void. And in there, we're going to call the SongContentService.createNewQueue, SongToPlayFirst. And we send the whole painting of Song. Then, we go to the template. And we're going to link. Both. So it's SongToPlay and this one is OnPlaySong. That's it, so now, if I click on example on this music, it doesn't work. So yes, we forgot to put here click Play. There you go, and there it works. So here we have a little problem of height, and this is due to the fact that the icons are not the correct size. So we're going to have to go to the style.scss, and add a small class that we have forgotten, which is called navicon, and which must be font-size 20px. Once we've done that, the height is correct. Then, the goal will be to prevent users who are not logged in be able to read music, and display them a pop-up inviting them to log in. To do this, we are going to go to the off-service, we will create a type, export type of pop-up state, is equal to open or close. Then, we will create a new signal, that's fine be trigger of pop-up dollar, which will be of type writable signal, with the type that we just... create, and its initial state will be closed since the pop-up will need to be closed. We create our public signal, state change, with computed, so this.trigger of pop-up, and then, we will create a service that we will call open or close of pop-up, which will take a state of type as a parameter off pop-up, and that will simply write in the signal, the new state. Now that we've done that, we're going to create the component where we will display our two buttons which will invite users to log in. To do this, open a terminal and we do ng-gc, we will create it in layout and we're going to call it off pop-up. Once created we open the ts part and we go inject the of service like this then we will just create a login method we will call the of service point login once that's done we will move on to the html part where we will add a div with a bg dark class of flex align items center flex columns padding 4 and of pop-up in this div we will have an h2 tag with a class margin bottom 4 we will write start listening now with a free account below we will have a another div with a class hate 75 a width of 100 a class default flex flex columns and justify content center we will have a first button who will call who will call the login sorry we click a btn class primary button one width of 100 and a margin bottom of 3 it is type button it will be called login we will do even for the second button which also works call login except that we will call it sign up on will display sign up then we will go to the scss file to add a small class which will be called of pop-up and which will contain a Hate by 250 pixels then we will go to the app component because it is this component which will manage the opening and closing of the pop-up to do this we will add the of service we will also inject the modal service which comes from bootstrap And we're going to need an instance of the pop-up, which is of the NGB modal type, and which can be zero, and which is zero elsewhere. Then, we're going to go into the constructor, we're going to add effecte, In effect we will call a method we will create just then so it will be open or close modal and we will pass the value to it which is found in the signal which contains the state of our pop up will also have to add an option who is going to be in the water signals rights here we go create our method it is the type of pop up state in our method we will also have the state so we're going to call it state Is equal to open then we open the pop-up this is the method we go there create just after otherwise if we have an instance of the pop-up and if the state is equal to close and if there is not and if there is a A modal which is open it's modal service so we close the modal then for the open of pop up method we will therefore instantiate a new pop up using to the service modal we will pass the component to it that we created previously with a few options authentication pull model and we will center it like this then we will have to listen to some events so we are going to do an off modal ref point dismiss when the user will click on the gold edge of the pop up it will close automatically we have to be aware of that so in next we will recall the of service point open or close and we will give him the order to close so that the state is synchronized with opening the pop up does the same thing here with closed and we are ok for the part opening closing now we will need a way to detect when the user is not connected and when he tries to play music so to do this we will create an interceptor which will come listen to http requests and see what the back-end responds to us and based on that we will react to Create an interceptor on type ngg interceptor and we're going to do it here we're going to do it here we're going to do it here we're going to call it so off interceptor and we will put it in the folder service like this when we open the interceptor we are going to put we are going to inject the Off service like this then we will give a blowjob on request we will call tap and in tap we will see if it there is an error the error will be of type http error response and if the error it has a status equal to 401 and if the error has a url and if this url it includes api slash authentic quest and id user and it is authenticated on the front side then we try to relog it automatically otherwise if the error at a url and that request point method is different from get and it does not terminate not include non and with pardon with api slash song what we want, she can list them or that always the same if it has an url and if this url does not end with api slash quest authentic and id and if the user is not connected then in these cases we open the pop up open or close open here is our interceptor now we will have to activate it that is to say register it here so at the level of provide Http client we are going to call with interceptors it is a table that we will have to import it and in this table we just want marked off interceptor Besides, it's not very well named because that it makes interceptor interceptor so we are going refactor it yes that's it and we also want to do a refactor of this file like this normally now if I return to the side of my browser that I click I have my pop up when i try to read against i think there is a small color problem is not a good barrel primarie but it is in the secondary barrel in off pop up component html here it is like this and so if I log in there I have the player which works small problem which I just realized account by recreating my database because that some music was poorly created in my case when you add a music to the list is not refreshed correctly after creation this is normal since if we go to add song component we had here commented on the get all I prompt to uncomment it and normally now if I'm going to recreate any one so little no matter, here it refreshes correctly We will now continue with the research part. So, we move on to our back-end. We're going to go to the song repository. We are going to add a method which will return a list of songs that will be called findTitleOrAuthorContaining and which will take as parameter a searchTerm string. This method will be noted with arrobase. Query, because we are going to have need to do a custom query. This query will contain a select, s from song, s, where lower de s.title, like, lower, concat, percentage, comma, two points, search, term, comma, percentage, more, to the line, or, lower, for the field. We will do the same for the author field, select, lower, from concat, we will copy that 's it, it will go faster, that's it, and it's search, term, otherwise it won't work. So here we are going to try to match the search term with either the title or the author once we did that we go to the song service we go create a new public list method that will return a list of read song info dto this alpha method was called search and it will take in parameter a search term we will call our song repository with the method we created just before we will pass the search term as a parameter we're going to make a stream map we're going to call the song maper with song to read we will transform the entities in dto and we will collect all that in a list like this we will create the locale no directly do return like this and we are good at the level of our service we move on to the part resources we go to song resources we create a new endpoint which will send an entity response playlist song info dto who will be called search and in parameters we will have a request params which will be of type string and which will be called term this method it will be annotated with a get mapping and its address it will be slash songs slash search inside Method we are going to return an entity point response ok and we will call song service point search by passing it the term parameter we are good we will test that our back end is working correctly here we have recharged everything seems to be good we will now head towards the front end to implement our research to do this we will first open the song service And we will add a search method which will take as a parameter a newSearchTerm which will be a string and which will return a state observable, stop, readSong and httpErrorResponse. We will create a queryParam variable which will be equal to newHttpParam.set Our param it will be called term and the variable is the parameter of the newSearchTerm method. Then we will call our httpClient, we are going to do a get, this get it will be of readSong stop type. The url will be environment.api url slash api slash songs slash search. We will pass the queryParam as an option. Then we will call the pipe method inside with a map operator which will map the songs, the table, in state.builder of type shutdown from readSong.httpErrorResponse.forSuccess. We're going to play him the songs and we are going to do a build point. If there is an error, we will use operator 4. And we're going to send an observable thanks to the off method. And we're going to do the same thing as here except that we will return forError like this. We will put a small comma and we will import off of RxJS. Once we have our service, we will create our new search component thanks to ng, thanks to the Angular CLI. We create it directly at the root. There it should appear here. We will go to app.root.ts. We will add a pass, search, with a component attribute. Search component, like this. We're going to go here, we're going to check that when we click, we have search work. Next, we go to the ts file. We will import the form module, the fontosum module and the small songcard component. We're going to need a search term of type string, initialized to empty. We're going to need the song service, which we will inject like this. We're going to need the song content service, which we will inject in the same way. We're going to need toast service. We are going to create a table of songs to welcome the search results, which will be of type stop read song, and which will be initialized. And we will also create a boolean which will give us allow the user to wait until the request comes back from the server. We will create a private method which will be called resetResultIfAntiTerm with newSearchTerm as parameter. We will use this method a little later. It will allow us to reset the empty array if the length of the search term is zero. So it's a little easier. If newSearchTerm.length is zero, then the resulting array of songs should be empty. We will also create an onPlay method. So he will... Because in fact, in research, the user can also click on music and music must be played. So, it is also necessary... That in the same way as what we did in the bookstore and in home, we can create a queue. So we do a create queue, first song, and we then send the table of song results. Then, we will do our method main, which will be the onSearch method. The onSearch method, it will take into account newSearchTerm parameter, which is of type string. We will assign this newSearchTerm to searchTerm, and then, we will transform this newSearchTerm into an observable, which we will import like this. We're going to do a blowjob. In this pipe, we are going to make a tap operator, which will send us the newSearchTerm this way. We're going to call the reset function, like this. We will call the filter operator, with newSearchTerm, like this. And we will filter the requests, if the newSearchTerm.length, it is not greater than 0. Little comma, we're also going to do a debounce, to avoid putting too much strain on the server, and to send him all requests each time time the user types a letter. We will set an interval of 300 milliseconds. We are going to make a tap, which will allow us, here, to be able to pass our search boolean with hole, To display the little spinner. We're going to make a switch map, then, where we will call our service, like this, so songService.search. We will pass it a newSearchTerm, and we will pass it a newSearchTerm, And finally, we can subscribe. In our subscription, we have just the case next to manage, since the observable us returns the state alone with error or not, so we will manage it in the onNext directly. And we will create an onNext method which takes the searchState as a parameter. Like this, we will put a small capital letter, like this, no period, no semicolon in this case. We do a createMethod, we create that. There, here, we import the correct state. Then, in onNext, we will pass the isSearching boolean is false. Then, we will see if the state, the status of the state is equal to OK. If this is the case, we will assign the table to the value that is in the state, on the board. Otherwise, if there is an error, we will display a toast with anErrorCuredWhile, searching of danger type. And that's it for the next part. Typescript from search now let's move on to the html part we remove what is in this file then we will open a div with a class absolute position input group flex no wrap search bar and zindex this div it will actually contain an The input where the user will be able to type their search so we are going to make a span class border 0 rounded start 5 input-group-text and padding end 1 in this span there will be an icon icon it's called search we're going to import it all from so as not to forget that's all actually then we have an input which contains a ngmodel the ngmodel the variable is search term and we will do an ngmodel change and we will call onsearch with the $event like this the name of the input is search the class the classes sorry border 0 rounded end 5 shape control margin end 2 search-input and padding start 2 this input it goes to be of type search we will put a placeholder only a small space what do you want to listen to and we're going to put an aria label search that's how this then we will have a div with a class padding top in this div we are going to have a first if if search is equal to true then I will have a div with a class padding top in this div we will have a first if if search is equal to true then I will have A god with a class flex justify content center margine top 5 and 100 then I will have another div with a class spinner big text primarie the smell and in this god will see a span with a visual class y hidden with in this political span then I see she if the stern sort we are going to paste just this the stern kind does not feel comfortable it is superior to Or equal to 1, and if the song table results, it is equal, we will have a div with a flex, a justify content center with a header of 100. There, like this, and inside, we go have another div with marked no results for this search, sad emoji. Finally, last case, we will have an at sign else, and in this one, we will have our for, with a song of songs result track by song. public id. In this for, we will have a div. Who is going to have a "Song Background" class, a " Width" of 100, a "Margin Bottom" of 2, a "Padding" of 2, a "Padding End" of 3, a "Flex" class and an "Align Item Center Then, in this "Div", we will have our " Small Song Card", like this, where we're going to pass it to him in parameter our "Song". Finally, we are going to do a small passage in the "SCSS" file. We will add a "Search Bar" class, where we will do a "Transform", "Translate", "100px " minus "72px", "Width" 350px, "Height" 50px. And we will also add a size for our "Phantosome Icon A font size of 21 pixels is therefore normally we should be able to return here we are fine our field and if we try to look we see that if I put letters as I go the search works correctly we have our results and gradually we have the music which appears small oversight here because that in fact the music doesn't play when you click so you must not forget to add the listener like this with a play event here it is and when we search we must see here the pop up showing that it triggers well and if I me log should be able to search and what music fires correctly like this then we will also take the opportunity to add a small loading icon in the bookstore and in the home to do this we are going to do a little bit of empty here then we're going to go to the bookstore component we are going to add a boolean is loading equals false in constructor in effect we will also add a boolean we will change it to false and in fetch song we will set it to true, we're going to comment it out for moment just to see clearly Let it appear well We now go to the HTML part and we will add an if isLoading to this div. Here, I will already take the opportunity to put the else like this and we will create a div with a class flex, flex colon rounded in volume 2, justify content center, align item center and hate 100. In this div, we will put the icon, the spinner grow, primary text, loader and role status. Then, a balispan, class, visual hidden, loading. That's it, now if we return from next to our bookstore, we see here the icon which is in loading mode. We will uncomment here and there now, there it is, very good. We will now go to home. Home, component and we're going to do the same thing. We create a boolean isLoading set to false. In the constructor, we will pass it to true and in the OK, rather in the effect, here we are going to set it to false. There it is, like this. Now, in the template, we will add an if isLoading, the else like this. Here, we will come and copy directly the spinner since it's the same. And no. Normally, we should have no effect, we just going to remove this one, there you go, so it's a a little bit low, we're going to remove the haste without it, that's it. We now move on to managing favorites so for this functionality we will add here as it is done in spotify kind of small tiles where we are going so it is no longer displayed but a kind of small tiles which will group together List of songs that the user has favorited we are also going to add hearts so at the level of player on the right in the search also when we will do a search here for that we will pass on the side of the tank we will first create small methods a utility method in the user service this method it will be public and it will return an optional read user dto it will be called get by email and will take into account parameter an email we call the user repository made a fine one by email then we create the variable and we will map this result with a simple user maper simplified very well then we're going to go to the other song repository we're going to need two methods one method first who will send an optional of songs we will this method we will call it find one by public heidi and learn in parameter a public helped the second it will return a list of songs and it will be called find all favorites by user e ? ?? and it will take a list of songs as a parameter 15uuu'ue v� d'� from the public and she will take in parameter An email this method we will note it in low query and because we are going to need to do a join and we will not be able to do just in naming with spring data jpa this query it goes do a select s from song s join with the table favorite f on s point public said equal f point song public said where of user email is equal to the email that you pass in Settings. Once our repository and methods are complete, we will import a StateBuilder. It is the same class that we find at the front, but on the Java side. I invite you to retrieve it from the description. For my part, I will import it into infrastructure by creating a new package since this state is to be shared. In this way, add and I also need status notification. Then I go to SongService and I create my method which will allow me to add and remove my favorites. This method will return a state of favoriteSongDTO and the error will be of type string. We'll call it addOrRemoveFromFavorite. It will take a favoriteSongDTO as a parameter and the email of the user who liked it. We will instantiate our builder like this. We're just going to change the type here which is going to be string favoriteSongDTO. Alright. We are going to call our songRepository in doing a findOneByPublicId and we will fetch this public ID in the DTO. We will now create our local variable. We will call songToLikeOptional. We'll look if we can't find it. So, we return an error directly in the builder. SongPublicIdDoesn'tExist.build Otherwise, we will extract. There, songToLike is optional. We're going to load the user. So there, the userService, it We'll have to inject it. So, private final userService. We will add it here as a constructor, as constructor parameter from songService, Let us have the injection. We return to our userService and we do a getByEmail or else throw. Since here, anyway, the user is logged in. If he has favorited, then we will add an entry into the table, favorite, so we creates the entity, and there is the new favorite, favorite.set, the public ID, we extract it from song to like, then we add the email, I can name this one a little better, I can put user or like song.email. And then I'm going to come and save basically my favorite object. I need the favorite repository, which I will also inject into the constructor. And if he wants to remove this song from his favorites, then we will create a favorite ID thanks to song to like and to the user, and we will come doing a delete using the foreign key is delete by ID, thanks to the composite primary key. Then, we will send back, a song dto, I have saw a song dto, false, with its public ID. Finally, we are going to return to the builder, we are going to return the builder, port success, and we will return the song dto, making a .build. We can continue with the service which will allow us to recover favorite songs for a user. This method will return a list, From ReadSongInfo.dto, we will call it FetchFavoriteSongs and it will take an email as a parameter. We're going to call the SongRepository, we're going to do a FindAllFavoriteByUserEmail, we pass the email as a parameter, we make a stream, then a map, we will call the SongMapper, we will call the SongToReadSongInfo.dto and then we're going to call List. That 's it, we'll return it directly and we have our service. We can now move towards the endpoint, therefore towards the SongResource, where we will create two new endpoints. The first endpoint we go to create, it will be the one that allows Add and remove favorites. So it will return a ResponseEntity from FavoriteSongs.dto. The method will be called AddOrRemoveFrom. We are going to need a bin which is valid, which is contained in the body and which is of type FavoriteSongs.dto. We first go, in the UserService, retrieve the user who is in session, like this, UserFromAuthentication. We will then call the service, AddOrRemove. We'll give him the DTO and the email. From the user we will create our variable if then we will look at the state level status equals status notification point error then we will create a problem style point strong status we will send http status point bad request and we will send the details of the error by doing like this we will then return the entity response with the detail problem alters and we will do a build point if everything went well then we send back a response entity with status code ok and with content of the state that I can name it a little bit best favorite song response we don't forget here annotated note endpoints with post mapping the address will be slash songs slash like on now moves to our second endpoint which will send a response list entities readsong info dto it will be called fetch favorite songs we are going to come here to read the information of the user connected as we did more high and we will come and make a response response entity point of songservice.fetchfavoritesong by passing the email as a parameter. Finally we annotate with getmapping and the address is songs slash like. Then we will rebuild our tank and we check that everything is going well. To give you more details on how it works on the database side at the level of the structure, if we take a Our pgadmin, you can see that we have a table favorite song. If we look, it has two columns which are userEmail and songPublicId. And in fact, this table, if there is an entry, we consider that the user has favorited it. And so, it is for this reason that in the songRepository, we make a join to come map a song with a line in the favorite table based on userEmail. Finally, now, on the side of our service, we will have to add a method which will come load the information of the songs that are favorites for users who are logged in. So, we're going to go to our songService and we are going to add a method which will be private, which will return a list of readSongInfoDTO. We're going to call fetchFavorite. statusForSongs, which will take as a parameter a list of readSongInfoDTO, we will call song. In this method, we will recover the information of the logged in user, we will call authenticatedUser. Then, we will retrieve the list T uuid t song by making a song.stream.map read song info dto get public id we will transform it into a list like this it's song public id then we're going call our favorite repository we are going to make a find all by user email and song public id in �a on we will have to create it and we will go to parameters authenticated user email and song public id on will go to the favorite repository this method it will return a list of favorites and so it's find all user email and song public id here it is like this then here we will create a local variable this is going to be the user favorite songs and we will return songs.stream.pick song and if user favorite songs contains songs get public id then this song is in favorites and we will we will return to list and So this guy here, we need to make a stream.map and make a favorite of getSongPublicId. This list must also be transformed. Like this, like that, there it works. Then we're going to have to go to our getAll and then in our search. So in our getAll, we will have to transform our service a little. Here, we will extract the variable, we're going to mark allSongs. We're going to return allSongs. On the other hand, we are going to do an if on the userService. If the user, he is authenticated, then we will return a fetchFavoriteSong with the following array. There it is, fetchFavoriteStatus. We will create the method isAuthenticated in the userService. And we're going to return securityContextHolder.getSong. getContext.getAuthentication.getPrincipal.equals.anonymousUser. There you have it, anonymousUser. Then we go to the search method and we're going to have to modify it too. So we're going to extract here variable, so searchSong, the list. And we're going to do the same thing. If the user is logged in, then we return the favorites information. We will now move on to the front-end side to implement our favorites functionality. We will first go to the song service. Here, we will implement the service call of our endpoint that we have just created. For this, we add a method addOrRemoveAsFavorite with a parameter boolean and a public ID of type string. This method returns void. We are going to call our HTTP client in making a readSong type post. The address is environment.api underscore url slash api slash song slash like. The bodysuit will be favorite and public ID. On this, we are going to do... We're going to subscribe. We will manage the next case which will be updatedSong. Here, I will have to create the two signals. So the private signal which goes be called addOrRemoveFavoriteSong which will be of type writableSignal. From readSong array state. It's not an array, it's just an readSong and HTTP error response which will be equal to signal state.builder of type readSong HTTP error response and for init.build. Another signal here that's going to be equal to computed 6.addOrRemove like this. We return to the side of our service. Here, we are going to do an addOrRemove at the signal level. We're going to do a set and we're going to do a state.builder of type readSong HTTP error response .forSuccess.build with the readSong object inside. We will also check if the updatedSong, the favorite field is true and if it is case, we will call the toastService.show and we will notify the user that the song has been successfully added to her favorites. It is a success. I will inject toastService like this. And if this boolean is false, then I will do a toService.show and in these cases, I will notify him that he has removed it. From favorite. No, it's still a success. This is not the case of error. In the event of an error, we will simply notify the signal by sending a state in error, by making forError like this and passing through putError. Then, we're going to use the toast again in mode danger and this time, we will mark as Error Error when adding song to favorite. So much for our service. Then we're going to need a second serve which will allow us to recover the favorites. To do this, it will send void and we will call the HTTP client. In get mode we will recover a read stop song the url is environment.api url slash api slash songs slash like we subscribe we manage it case next with the table of favorite song which returned to us by the API here I have to create my two signals my fetch favorite song I will add here it is stopped reading song the same thing here it's for init here it's this signal and it is this signal like this sig I will also add sig here now we can return to our service we are going to do a set in this signal which will be of state point type type builder stop read song http error point for success favorite song point build in this which concerns the case of error we will make a mistake and we will just return the builder with an error like this now we are going to build a component reusable which will make it possible to manage the favorites to do this we will generate a new component which will be called favorite song button he's going to be in shared now we're head into the shared favorite song button and let's find ourselves in shared now we're heading in shared On import font to sum module on will create an input which is required which is type read song we will have to inject off service And songService. We are going to add an onFavorite method, which will be called when the user will click on the heart icon. We will pass a readSong as a parameter, and in this case, we will call the songService, with the service we created, and we will reverse song.favorite, and we will also send the publicId, like this. Next, we need a constructor in which we will place our effect. We will come and collect the signal, therefore favoriteSongState. If this state is equal to OK, and the value... Is equal to true and the song its public id is equals the public id found in the song favorite then the favorite song point is equal at value point favorite in these cases we can assign the value we can now move on template we erase the content we put an if which will see if the user is logged in and if this is the case we will display the favorite button we will first listen to the click and we will call we favorite by passing in song parameters if the the music is in favorite so we will display a icon with a full heart we will call favorite text primari the icon here we will make a table with a face key and Art like this and then we are going to have an else except this time flagship so it's basically regular and this one I don't remember the exact name but it's so that you may have a full heart and an empty heart. Then we will extend it scss and we will just add the favorite class with a font size of 20 pixels. Our component is finished, we have nothing left that to place it we go to the search in the template and we will add our component here so favorite song button and we will pass it on in parameters the song like this and we will do the same thing for the player component where we go place it like this, here it is current song. Now we can return to our side front we have to log in there and if I search here, ah yes then small problem of icon we have to import so it's done art here and then there is a little import a little special to do here I do import art art regular from except that this one is the regular here and this one I have to It matters because they have the same names. We are obliged to put an alias at the level of our imports I must be able to come back here So look for a little css problem we're going to now create the map that will allow us to access the list of music that we have put as a favorite for this we will create a new component which will be called favorite song card we will create this component in home. Once in Home, we will open the TypeScript, we will just import RouterLink and we can then go directly to the HTML. This HTML will have a div with a class who's going to be called Card, we're going to have a SongCardBackground and a Playlist-Card. In this div, we will have another div with a CardBody class, with padding 1, a T-Flex class, a width of 100 and rounded 2. We will also have a RouterLink which will take us to the favorite URL. Then, we will have an image which will have a class rounded 2, a width of 55 pixels, a height of 55 pixels, a species. A SRC which will be equal to, then I have forgot to close the quotes here, an SRC which will be equal to Assets.img. Slash like.png we will put alt it will be like song like this, this image you will be able to find it in the repository, I will import it into a folder that I will create, which is going to be called img and I'm going to put it here, then we will have a div with a class d flex flex colon justify content center with a margin start of 2, then in this div, we will put another div with a margin bottom 1 and 1, a title class, this div will contain like song, then we're going to go to the scss and we have just a class to add which will be playlist card which will have a width of 200 pixels and cursor point very well, now we will place this map at home level, so we are going to render in the home component template on will add an h1 title which will have a class margin bottom 3 inside we are going to mark playlist and we will add our favorite app song card like this, if we go back now in our home, here we have our little one tile like song, then we move on to the creation of the component Which will contain the list of components, it will be called favorite We go to the app-route to add a URL to it. Her pass will be favorite, and therefore the component is favorite-component. We go to the favorite TypeScript file. We will import favorite-song-bouton-component and small-song-card. We will implement onInit, like this. We are going to create a favorite-song table, which will be of the read-song stop type, which will be empty for the moment. We are going to inject the song-service. Like this, we will also have need the song-content-service. In the ng-onInit, we will call the song-service.fetch-favorite. Here there is a small size for favorite-songs. We will then create our component. Builder with effect inside. We will extract the signal from song-service, which will allow us to control when the user removes songs from favorites. Like this, we will test the status, if it is equal to OK. And if that's the case, we're going to refresh the favorites list. Then we will have a second effect who will take care of retrieving the list. So we add our effect like this. We will recover in his service the FetchFavoriteSongSignal. We create our FavoriteSongState variable. If the status of this variable is OK, then we extract the value. Above, we are going to do a forEach. And in this forEach, we go skip all the songs with holes. Finally, we will assign... This board on the board that is in the State. Finally, if the user clicks on a music, we have to be able to play it. So we'll have to call service that is present in SongContent. Like this, SongContent.createNewCue FirstSong.This.FavoriteSongs On can now move on to the HTML side. We will put an H1 tag with a class MarginBottom3 which contains LikeSongs. A div with a PaddingTop class. A for which will iterate over the table. FavoriteSongs.TrackSong.PublicId In the for, we have a div with a SongCardBackground class. Width 100.MarginBottom2. Padding2.PaddingEnd3. Tflex. AlignItemCenter In there, we will have our SmallSongCard app which will have a class of Width 100 to which we will pass in Song parameter. Like this. When clicked, we will call OnPlay. We're going to play the song like this. And finally, we're going to do AppFavorite. It's the button. Like this. With Song. There we have our Favorite. So, we have a problem. We have a BadRequest problem. If we go to see in Network, it's because in fact, we don't send. There is a mistake. Here we send Favorite. We do not send the Boolean. So, indeed, there is a typography. Here you have to put Favorite and PublicId. Let's see if that fixes the problem. So. There, we are good. I can put it in Favorite. If I click here, it's also in Favorite. And I can also take it off. We can clearly see that... It works here small error so it works when same but it's less elegant we don't click but we listen to the song to play which can be found here now we can return to our side browser we return to like song and we can see that it works correctly that we can put for example here as a favorite we can do like song we can remove some and everything works correctly. can also remove the test toast that we had left until now in the app component in the onimit vng our spotify clone is now functional we can play music manage queue control volume search for music put it in favorites manage our favorites and add music thank you for having followed this tutorial to the end I hope that you liked it I'll tell you next time ciao
Info
Channel: Cyril from CodeCake
Views: 3,003
Rating: undefined out of 5
Keywords:
Id: FEQ9C9PfLLI
Channel Id: undefined
Length: 219min 32sec (13172 seconds)
Published: Sat Mar 16 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.