Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { Section } from './components/Section';
import { MovieList } from './components/MovieList';
import { PersonList } from './components/PersonList';
import { useDebouncedValue } from './hooks/useDebouncedValue';
import './App.css';
import { useFavorites } from './hooks/useFavorites';
import { TrendingPreview } from './components/TrendingPreview';
import './App.css';

function App() {

Expand All @@ -19,6 +20,7 @@ function App() {
const debouncedQuery = useDebouncedValue(query, 400);
const search = useSearchMovies(debouncedQuery)
const isSearching = query.trim().length > 0;
const { isFavorite, toggleFavorite} = useFavorites();

return (
<div className="page">
Expand All @@ -27,19 +29,19 @@ function App() {
<main>
{isSearching ? (
<Section id="sok" title={`Søkeresultater for "${query}"`} isLoading={search.isLoading} error={search.error}>
{search.data && <MovieList movies={search.data} />}
{search.data && <MovieList movies={search.data} isFavorite={isFavorite} onToggleFavorite={toggleFavorite} />}
</Section>
) : (
) : (
<>
<Section id="trendingPreview" title="" isLoading={current.isLoading} error={current.error}>
{current.data && <TrendingPreview movies={current.data}/>}
</Section>
<Section id="populaere" title="Populære filmer" isLoading={popular.isLoading} error={popular.error}>
{popular.data && <MovieList movies={popular.data} />}
{popular.data && <MovieList movies={popular.data} isFavorite={isFavorite} onToggleFavorite={toggleFavorite} />}
</Section>

<Section id="nye-filmer" title="Nye filmer" isLoading={current.isLoading} error={current.error}>
{current.data && <MovieList movies={current.data} />}
{current.data && <MovieList movies={current.data} isFavorite={isFavorite} onToggleFavorite={toggleFavorite} />}
</Section>

<Section id="skuespillere" title="Skuespillere" isLoading={people.isLoading} error={people.error}>
Expand Down
4 changes: 4 additions & 0 deletions src/api/tmdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ export function fetchMovieCredits(movieId: number): Promise<CreditsResponse> {
`/movie/${movieId}/credits?language=en-US`,
'Klarte ikke hente skuespillere...',
);


}


17 changes: 16 additions & 1 deletion src/components/MovieCard.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@
z-index: 1;
}

/*Favorite merke som stjerne nede til venste*/
.movie-card__favorite {
position: absolute;
bottom: 0.5rem;
right: 0.5rem;
z-index: 1;
background: none;
border: none;
padding: 0;
font-size: 1.5rem;
line-height: 1;
color: #fbc02d;
cursor: pointer;
}

@media (max-width: 480px) {
.movie-card__poster {
width: 80px;
Expand All @@ -95,4 +110,4 @@
.movie-card__info {
padding-right: 3rem;
}
}
}
14 changes: 13 additions & 1 deletion src/components/MovieCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ interface MovieCardProps {
title: string; // Filmens tittel, skal vises som overskrift
posterUrl: string; // URL til filmplakat-bildet, brukes i <img>
rating: number; // Terningkast/prosent (0-100), f.eks. vote_average * 10 fra TMDB
isFavorite: boolean; // Om denne filmen er markert som favoritt
onToggleFavorite: (id: number) => void; // Kalles når stjernen klikkes
}

function MovieCard({ movieId, title, posterUrl, rating }: MovieCardProps) {
function MovieCard({ movieId, title, posterUrl, rating, isFavorite, onToggleFavorite }: MovieCardProps) {
// Henter skuespillere for akkurat denne filmen når kortet vises
const { data: credits, isLoading } = useMovieCredits(movieId);

Expand All @@ -19,6 +21,16 @@ function MovieCard({ movieId, title, posterUrl, rating }: MovieCardProps) {

return (
<div className="movie-card">
<button
type="button"
className="movie-card__favorite"
aria-pressed={isFavorite}
aria-label={isFavorite ? `Fjern ${title} fra favoritter` : `Legg ${title} til favoritter`}
onClick={() => onToggleFavorite(movieId)}
>
{isFavorite ? "★" : "☆"}
</button>

<span className="movie-card__rating">{rating}%</span>

<img className="movie-card__poster" src={posterUrl} alt={title} />
Expand Down
6 changes: 5 additions & 1 deletion src/components/MovieList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import "./MovieList.css";
// MovieCard selv vet ingenting om TMDB eller fetching, den bare mottar props.
type MovieListProps = {
movies: Movie[];
isFavorite: (id: number) => boolean;
onToggleFavorite: (id: number) => void;
};

export function MovieList({ movies }: MovieListProps) {
export function MovieList({ movies, isFavorite, onToggleFavorite }: MovieListProps) {
return (
// Ytre container for hele filmlisten, kan style'es med CSS senere
<div className="movie-list">
Expand All @@ -35,6 +37,8 @@ export function MovieList({ movies }: MovieListProps) {
// Skuespillere ligger ikke i dette API-svaret (krever eget kall til /credits),
// så vi sender en tom liste som placeholder inntil videre
movieId={movie.id}
isFavorite={isFavorite(movie.id)}
onToggleFavorite={onToggleFavorite}
/>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function Sidebar({onSearch, searchValue}: SidebarProps) {
{section.label}
</a>
</li>
))}
))}
</ul>
</nav>
</aside>
Expand Down
25 changes: 25 additions & 0 deletions src/hooks/useFavorites.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState, useEffect } from 'react';

const STORAGE_KEY = 'favorite-movies';

export function useFavorites() {
const [favoriteIds, setFavoriteIds] = useState<number[]>(() => {
const stored = localStorage.getItem(STORAGE_KEY);
return stored ? JSON.parse(stored) : [];
});

useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(favoriteIds));
}, [favoriteIds]);

function toggleFavorite(id: number) {
setFavoriteIds((prev) =>
prev.includes(id) ? prev.filter((favId) => favId !== id) : [...prev, id]
);
}

function isFavorite(id: number) {
return favoriteIds.includes(id);
}

return { toggleFavorite, isFavorite};}