diff --git a/src/music-catalogue-ui/components/albumList.js b/src/music-catalogue-ui/components/albumList.js new file mode 100644 index 0000000..f82f107 --- /dev/null +++ b/src/music-catalogue-ui/components/albumList.js @@ -0,0 +1,36 @@ +import useAlbums from "@/hooks/useAlbums"; +import statuses from "@/helpers/status"; +import AlbumRow from "./albumRow"; +import StatusIndicator from "./statusIndicator"; + +const AlbumList = ({ artist, navigate }) => { + const { albums, setAlbums, currentStatus } = useAlbums(artist.id); + + if (currentStatus !== statuses.loaded) + return ; + + return ( + <> +
+
Albums
+
+ + + + + + + + + + + {albums.map((a) => ( + + ))} + +
Album TitleArtistGenreReleased
+ + ); +}; + +export default AlbumList; diff --git a/src/music-catalogue-ui/components/albumRow.js b/src/music-catalogue-ui/components/albumRow.js new file mode 100644 index 0000000..3427205 --- /dev/null +++ b/src/music-catalogue-ui/components/albumRow.js @@ -0,0 +1,14 @@ +import pages from "@/helpers/navigation"; + +const AlbumRow = ({ artist, album, navigate }) => { + return ( + navigate(pages.tracks, album)}> + {artist.name} + {album.title} + {album.genre} + {album.released} + + ); +}; + +export default AlbumRow; diff --git a/src/music-catalogue-ui/components/app.js b/src/music-catalogue-ui/components/app.js index 79e0286..f9153f4 100644 --- a/src/music-catalogue-ui/components/app.js +++ b/src/music-catalogue-ui/components/app.js @@ -3,35 +3,30 @@ import Banner from "./banner"; import pages from "@/helpers/navigation"; import ComponentPicker from "./componentPicker"; -// Create a navigation context for the current page to display -const navigationContext = React.createContext(pages.artists); - const App = () => { - // Function used to change the current page. navTo is the page to navigate - // to, a member of the pages object. param is a parameter passed to the - // navigation function e.g. an artist id or an album id - const navigate = useCallback( - (navTo, param) => setCurrentPage({ current: navTo, param, navigate }), - [] - ); - - // Store the current page and the navigation function in state - const [page, setCurrentPage] = useState({ - current: pages.artists, - navigate, + const [context, setContext] = useState({ + page: pages.artists, + artist: null, + album: null, }); - // Note that the application provides a navigation context to all children - // below it in the hierarchy + const navigate = useCallback((page, artist, album) => { + console.log(page); + console.log(artist); + console.log(album); + setContext({ + page: page, + artist: artist, + album: album, + }); + }, []); + return ( - - -
Personal Music Catalogue
-
- -
+ <> + + + ); }; -export { navigationContext }; export default App; diff --git a/src/music-catalogue-ui/components/artistList.js b/src/music-catalogue-ui/components/artistList.js index 9186959..114ca2f 100644 --- a/src/music-catalogue-ui/components/artistList.js +++ b/src/music-catalogue-ui/components/artistList.js @@ -3,10 +3,9 @@ import statuses from "@/helpers/status"; import ArtistRow from "./artistRow"; import StatusIndicator from "./statusIndicator"; -const ArtistList = ({ onSelected }) => { +const ArtistList = ({ navigate }) => { const { artists, setArtists, currentStatus } = useArtists(); - // Early return = conditional rendering if (currentStatus !== statuses.loaded) return ; @@ -23,7 +22,7 @@ const ArtistList = ({ onSelected }) => { {artists.map((a) => ( - + ))} diff --git a/src/music-catalogue-ui/components/artistRow.js b/src/music-catalogue-ui/components/artistRow.js index 2547718..a884c5c 100644 --- a/src/music-catalogue-ui/components/artistRow.js +++ b/src/music-catalogue-ui/components/artistRow.js @@ -1,12 +1,8 @@ -import { useContext } from "react"; -import { navigationContext } from "./app"; import pages from "@/helpers/navigation"; -const ArtistRow = ({ artist }) => { - const { navigate } = useContext(navigationContext); - +const ArtistRow = ({ artist, navigate }) => { return ( - navigate(pages.albums, artist)}> + navigate(pages.albums, artist, null)}> {artist.name} ); diff --git a/src/music-catalogue-ui/components/banner.js b/src/music-catalogue-ui/components/banner.js index 238e240..041ab79 100644 --- a/src/music-catalogue-ui/components/banner.js +++ b/src/music-catalogue-ui/components/banner.js @@ -1,14 +1,7 @@ -import { useContext } from "react"; import styles from "./banner.module.css"; -import { navigationContext } from "./app"; import pages from "@/helpers/navigation"; -const Banner = ({ children }) => { - // The navigation context supplies a function that can be used to navigate - // between pages. For the banner, clicking on the logo always navigates - // back to the artists page - const { navigate } = useContext(navigationContext); - +const Banner = ({ navigate }) => { return (
@@ -16,11 +9,13 @@ const Banner = ({ children }) => { src="./logo.png" alt="Music Catalogue" className={styles.logo} - onClick={() => navigate(pages.artists)} + onClick={() => navigate(pages.artists, null, null)} />
-
{children}
+
+
Personal Music Catalogue
+
); diff --git a/src/music-catalogue-ui/components/componentPicker.js b/src/music-catalogue-ui/components/componentPicker.js index b1f2f9d..2624d04 100644 --- a/src/music-catalogue-ui/components/componentPicker.js +++ b/src/music-catalogue-ui/components/componentPicker.js @@ -1,12 +1,13 @@ import pages from "../helpers/navigation"; import ArtistList from "./artistList"; +import AlbumList from "./albumList"; -const ComponentPicker = ({ currentPage }) => { - switch (currentPage) { +const ComponentPicker = ({ context, navigate }) => { + switch (context.page) { case pages.artists: - return ; + return ; case pages.albums: - return ; + return ; case pages.tracks: return ; default: diff --git a/src/music-catalogue-ui/helpers/api.js b/src/music-catalogue-ui/helpers/api.js index 84064fd..534392c 100644 --- a/src/music-catalogue-ui/helpers/api.js +++ b/src/music-catalogue-ui/helpers/api.js @@ -23,7 +23,7 @@ const apiAuthenticate = async (username, password) => { return token.replace(/"/g, ""); }; -const apiFetchAllArtists = async (token) => { +const apiFetchAllArtists = async () => { // TODO: This call can be removed once user login has been implemented var token = await apiAuthenticate(username, password); @@ -44,4 +44,25 @@ const apiFetchAllArtists = async (token) => { return artists; }; -export { apiAuthenticate, apiFetchAllArtists }; +const apiFetchAlbumsByArtist = async (artistId) => { + // TODO: This call can be removed once user login has been implemented + var token = await apiAuthenticate(username, password); + + // Construct the request headers + const headers = { + Authorization: `Bearer ${token}`, + }; + + // Call the API to get a list of all artists + const url = baseUrl + `/albums/artist/${artistId}`; + const response = await fetch(url, { + method: "GET", + headers: headers, + }); + + // Get the response content as JSON and return it + const albums = await response.json(); + return albums; +}; + +export { apiAuthenticate, apiFetchAllArtists, apiFetchAlbumsByArtist }; diff --git a/src/music-catalogue-ui/helpers/constants.js b/src/music-catalogue-ui/helpers/constants.js index 80c4b3a..24c72c9 100644 --- a/src/music-catalogue-ui/helpers/constants.js +++ b/src/music-catalogue-ui/helpers/constants.js @@ -1,6 +1,6 @@ // This file is temporary, pending implementation of user login -const username = "a-user"; -const password = "the-password"; -const baseUrl = "http://host:port"; +const username = "dave"; +const password = "password"; +const baseUrl = "http://localhost:8098"; export { baseUrl, username, password }; diff --git a/src/music-catalogue-ui/hooks/useAlbums.js b/src/music-catalogue-ui/hooks/useAlbums.js new file mode 100644 index 0000000..f10cb6e --- /dev/null +++ b/src/music-catalogue-ui/hooks/useAlbums.js @@ -0,0 +1,34 @@ +import statuses from "@/helpers/status"; +import { useState, useEffect } from "react"; +import { apiFetchAlbumsByArtist } from "@/helpers/api"; + +const useAlbums = (artistId) => { + // Current list of albums and the method to change it + const [albums, setAlbums] = useState([]); + + // Current status indicator and the method to change it, defaults to "loading" + const [currentStatus, setCurrentStatus] = useState(statuses.isLoading); + + useEffect(() => { + const fetchAlbums = async (artistId) => { + // Set the "loading" indicator + setCurrentStatus(statuses.isLoading); + + try { + // Get a list of albums via the service, store it in state and clear the + // loading status + var fetchedAlbums = await apiFetchAlbumsByArtist(artistId); + setAlbums(fetchedAlbums); + setCurrentStatus(statuses.loaded); + } catch { + setCurrentStatus(statuses.hasErrored); + } + }; + + fetchAlbums(artistId); + }, []); + + return { albums, setAlbums, currentStatus }; +}; + +export default useAlbums;