diff --git a/webdev-prosjekt1/package-lock.json b/webdev-prosjekt1/package-lock.json index d64e3da..4045023 100644 --- a/webdev-prosjekt1/package-lock.json +++ b/webdev-prosjekt1/package-lock.json @@ -8,6 +8,7 @@ "name": "webdev-prosjekt1", "version": "0.0.0", "dependencies": { + "@phosphor-icons/react": "^2.1.10", "@tanstack/react-query": "^5.102.8", "react": "^19.2.8", "react-dom": "^19.2.8" @@ -525,6 +526,19 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@phosphor-icons/react": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz", + "integrity": "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">= 16.8", + "react-dom": ">= 16.8" + } + }, "node_modules/@rolldown/binding-android-arm-eabi": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz", diff --git a/webdev-prosjekt1/package.json b/webdev-prosjekt1/package.json index 76c7624..dce5521 100644 --- a/webdev-prosjekt1/package.json +++ b/webdev-prosjekt1/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@phosphor-icons/react": "^2.1.10", "@tanstack/react-query": "^5.102.8", "react": "^19.2.8", "react-dom": "^19.2.8" diff --git a/webdev-prosjekt1/src/App.tsx b/webdev-prosjekt1/src/App.tsx index 510a04d..e9b8bc7 100644 --- a/webdev-prosjekt1/src/App.tsx +++ b/webdev-prosjekt1/src/App.tsx @@ -7,6 +7,7 @@ import { Searchbar } from './components/Searchbar'; import { TrackDetailPage } from './components/TrackDetailPage'; import { AlbumDetailPage } from './components/AlbumDetailPage'; import { ArtistDetailPage } from './components/ArtistDetailPage'; +import { FavoritesPage } from './components/FavoritesPage'; import { Header } from './components/Header'; import { TrackCard } from './components/TrackCard'; import { AlbumCard } from './components/AlbumCard'; @@ -38,6 +39,7 @@ function App() { const isSongDetailRoute = currentPath === '/track-detail' || currentPath === '/track-detail/'; const isAlbumDetailRoute = currentPath === '/album-detail' || currentPath === '/album-detail/'; const isArtistDetailRoute = currentPath === '/artist-detail' || currentPath === '/artist-detail/'; + const isFavoritesRoute = currentPath === '/favorites' || currentPath === '/favorites/'; const dummyAlbumImage = { url: musicWizImage, height: 640, @@ -126,6 +128,13 @@ function App() { localStorage.setItem('music-wiz-selected-artist', JSON.stringify(artist)); navigateTo('/artist-detail'); }; + const handleFavoriteTrackClick = (track: Track | AlbumTrack) => { + if ('album' in track) { + handleTrackClick(track); + } else { + handleAlbumTrackClick(track); + } + }; const { data: results, isLoading, isFetching, isError, error } = useQuery({ queryKey: ['search', effectiveQuery], queryFn: () => search(effectiveQuery), @@ -199,6 +208,7 @@ function App() { if (isAlbumDetailRoute) return ; if (isArtistDetailRoute) return ; if (isSongDetailRoute) return ; + if (isFavoritesRoute) return ; return (
diff --git a/webdev-prosjekt1/src/components/Button.tsx b/webdev-prosjekt1/src/components/Button.tsx index 3070060..c2d7f3c 100644 --- a/webdev-prosjekt1/src/components/Button.tsx +++ b/webdev-prosjekt1/src/components/Button.tsx @@ -1,15 +1,16 @@ import type { MouseEventHandler, ReactNode } from "react"; -import './Button.css'; +import "./Button.css"; type ButtonProps = { content: ReactNode; + className?: string; onClick?: MouseEventHandler; } -export const Button = ({ content, onClick }: ButtonProps) => { - return ( - - ) -} +export const Button = ({ content, className, onClick }: ButtonProps) => { + return ( + + ); +}; \ No newline at end of file diff --git a/webdev-prosjekt1/src/components/FavButton.css b/webdev-prosjekt1/src/components/FavButton.css new file mode 100644 index 0000000..3e2004d --- /dev/null +++ b/webdev-prosjekt1/src/components/FavButton.css @@ -0,0 +1,21 @@ +.fav-button { + background: none; + border: none; + padding: 0; +} + +.fav-button:hover { + background: none; + transform: scale(1.1); +} + +.star-animate { + animation: bounce-click 0.4s ease-out; +} + +@keyframes bounce-click { + 0% { transform: scale(1); } + 30% { transform: scale(0.85); } /* Squash down on initial click */ + 60% { transform: scale(1.1); } /* Bounce back up past normal size */ + 100% { transform: scale(1); } /* Settle back to original size */ +} \ No newline at end of file diff --git a/webdev-prosjekt1/src/components/FavButton.tsx b/webdev-prosjekt1/src/components/FavButton.tsx new file mode 100644 index 0000000..ae2034a --- /dev/null +++ b/webdev-prosjekt1/src/components/FavButton.tsx @@ -0,0 +1,42 @@ +import { Button } from './Button'; +import { StarIcon } from "@phosphor-icons/react"; +import type {AlbumTrack, Track } from '../api/types'; +import { useFavorites } from '../hooks/useFavorites'; +import { useState } from 'react'; +import './FavButton.css'; + +type FavButtonProps = { + track: Track | AlbumTrack; +}; + + +export const FavButton = ({ track }: FavButtonProps) => { + const { favorites, addFavorite, removeFavorite } = useFavorites(); + const isFavorite = favorites.some( + favorite => favorite.id === track.id + ); + const [isAnimating, setIsAnimating] = useState(false); + + return( +