Skip to content

Commit

Permalink
Merge pull request #27 from nnivxix/feat/watchlist
Browse files Browse the repository at this point in the history
Feat/watchlist
  • Loading branch information
nnivxix authored May 24, 2024
2 parents 81412ab + 7335919 commit 74abf9a
Show file tree
Hide file tree
Showing 28 changed files with 1,407 additions and 783 deletions.
25 changes: 24 additions & 1 deletion .eslintrc-auto-import.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,29 @@
"Avatar": true,
"AvatarFallback": true,
"AvatarImage": true,
"AccountProvider": true
"AccountProvider": true,
"AccountProviderContext": true,
"ThemeProviderContext": true,
"AddToWatchlistButton": true,
"DropdownMenu": true,
"DropdownMenuCheckboxItem": true,
"DropdownMenuContent": true,
"DropdownMenuGroup": true,
"DropdownMenuItem": true,
"DropdownMenuLabel": true,
"DropdownMenuPortal": true,
"DropdownMenuRadioGroup": true,
"DropdownMenuRadioItem": true,
"DropdownMenuSeparator": true,
"DropdownMenuShortcut": true,
"DropdownMenuSub": true,
"DropdownMenuSubContent": true,
"DropdownMenuSubTrigger": true,
"DropdownMenuTrigger": true,
"Watchlist": true,
"Movies": true,
"TvShows": true,
"WatchlistLayout": true,
"BackdropCard": true
}
}
32 changes: 27 additions & 5 deletions auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ export {}
declare global {
const $fetch: typeof import('./src/utils/$fetch')['default']
const $localStorage: typeof import('./src/utils/$local-storage')['default']
const AccountProvider: typeof import('./src/providers/account')['AccountProvider']
const AccountProvider: typeof import('./src/contexts/AccountContext/AccountProvider')['AccountProvider']
const AccountProviderContext: typeof import('./src/contexts/AccountContext/AccountProvider')['AccountProviderContext']
const AddToWatchlistButton: typeof import('./src/components/AddToWatchlistButton')['default']
const Avatar: typeof import('./src/components/ui/avatar')['Avatar']
const AvatarFallback: typeof import('./src/components/ui/avatar')['AvatarFallback']
const AvatarImage: typeof import('./src/components/ui/avatar')['AvatarImage']
const BackdropCard: typeof import('./src/components/BackdropCard')['default']
const Badge: typeof import('./src/components/ui/badge')['Badge']
const Button: typeof import('./src/components/ui/button')['Button']
const CardItem: typeof import('./src/components/CardItem')['default']
Expand All @@ -29,11 +32,27 @@ declare global {
const DialogPortal: typeof import('./src/components/ui/dialog')['DialogPortal']
const DialogTitle: typeof import('./src/components/ui/dialog')['DialogTitle']
const DialogTrigger: typeof import('./src/components/ui/dialog')['DialogTrigger']
const DropdownMenu: typeof import('./src/components/ui/dropdown-menu')['DropdownMenu']
const DropdownMenuCheckboxItem: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuCheckboxItem']
const DropdownMenuContent: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuContent']
const DropdownMenuGroup: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuGroup']
const DropdownMenuItem: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuItem']
const DropdownMenuLabel: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuLabel']
const DropdownMenuPortal: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuPortal']
const DropdownMenuRadioGroup: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuRadioGroup']
const DropdownMenuRadioItem: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuRadioItem']
const DropdownMenuSeparator: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuSeparator']
const DropdownMenuShortcut: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuShortcut']
const DropdownMenuSub: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuSub']
const DropdownMenuSubContent: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuSubContent']
const DropdownMenuSubTrigger: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuSubTrigger']
const DropdownMenuTrigger: typeof import('./src/components/ui/dropdown-menu')['DropdownMenuTrigger']
const Home: typeof import('./src/pages/Home')['default']
const Input: typeof import('./src/components/ui/input')['Input']
const Label: typeof import('./src/components/ui/label')['Label']
const Link: typeof import('react-router-dom')['Link']
const Movie: typeof import('./src/pages/Show/Movie')['default']
const Movies: typeof import('./src/pages/Watchlist/Movies')['default']
const NavLink: typeof import('react-router-dom')['NavLink']
const Navbar: typeof import('./src/components/Navbar')['default']
const Navigate: typeof import('react-router-dom')['Navigate']
Expand All @@ -58,7 +77,8 @@ declare global {
const SelectValue: typeof import('./src/components/ui/select')['SelectValue']
const Setting: typeof import('./src/pages/Setting')['default']
const SimilarCardItem: typeof import('./src/components/SimilarCardItem')['default']
const ThemeProvider: typeof import('./src/providers/theme')['ThemeProvider']
const ThemeProvider: typeof import('./src/contexts/ThemeContext/ThemeProvider')['ThemeProvider']
const ThemeProviderContext: typeof import('./src/contexts/ThemeContext/ThemeProvider')['ThemeProviderContext']
const Toast: typeof import('./src/components/ui/toast')['Toast']
const ToastAction: typeof import('./src/components/ui/toast')['ToastAction']
const ToastClose: typeof import('./src/components/ui/toast')['ToastClose']
Expand All @@ -68,8 +88,10 @@ declare global {
const ToastViewport: typeof import('./src/components/ui/toast')['ToastViewport']
const Toaster: typeof import('./src/components/ui/toaster')['Toaster']
const Tv: typeof import('./src/pages/Show/Tv')['default']
const TvShows: typeof import('./src/pages/Watchlist/TvShows')['default']
const WatchProvider: typeof import('./src/components/WatchProvider')['default']
const WatchProviderContainer: typeof import('./src/components/WatchProviderContainer')['default']
const WatchlistLayout: typeof import('./src/layouts/WatchlistLayout')['default']
const badgeVariants: typeof import('./src/components/ui/badge')['badgeVariants']
const buttonVariants: typeof import('./src/components/ui/button')['buttonVariants']
const cn: typeof import('./src/lib/utils')['cn']
Expand All @@ -89,7 +111,7 @@ declare global {
const runtimeDuration: typeof import('./src/utils/runtime-duration')['default']
const startTransition: typeof import('react')['startTransition']
const toast: typeof import('./src/components/ui/use-toast')['toast']
const useAccount: typeof import('./src/providers/account')['useAccount']
const useAccount: typeof import('./src/contexts/AccountContext/useAccount')['useAccount']
const useCallback: typeof import('react')['useCallback']
const useContext: typeof import('react')['useContext']
const useDebugValue: typeof import('react')['useDebugValue']
Expand All @@ -104,7 +126,7 @@ declare global {
const useInsertionEffect: typeof import('react')['useInsertionEffect']
const useLayoutEffect: typeof import('react')['useLayoutEffect']
const useLinkClickHandler: typeof import('react-router-dom')['useLinkClickHandler']
const useLocalStorage: typeof import('./src/hooks/useLocalStorage')['default']
const useLocalStorage: typeof import("./src/hooks/useLocalStorage")["default"]
const useLocation: typeof import('react-router-dom')['useLocation']
const useMemo: typeof import('react')['useMemo']
const useNavigate: typeof import('react-router-dom')['useNavigate']
Expand All @@ -119,7 +141,7 @@ declare global {
const useSearchParams: typeof import('react-router-dom')['useSearchParams']
const useState: typeof import('react')['useState']
const useSyncExternalStore: typeof import('react')['useSyncExternalStore']
const useTheme: typeof import('./src/providers/theme')['useTheme']
const useTheme: typeof import('./src/contexts/ThemeContext/useTheme')['useTheme']
const useToast: typeof import('./src/components/ui/use-toast')['useToast']
const useTransition: typeof import('react')['useTransition']
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-select": "^2.0.0",
Expand Down
97 changes: 97 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 20 additions & 18 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import "./App.css";

function App() {
return (
<ThemeProvider defaultTheme="dark" storageKey="vilm-theme">
<AccountProvider>
<Router>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/show/tv/:id" element={<Tv />} />
<Route path="/show/movie/:id" element={<Movie />} />
<Route path="/search" element={<Search />} />
<Route path="/setting" element={<Setting />} />
<Route path="*" element={<NotFound />} />
</Routes>
<Toaster />
</Router>
</AccountProvider>
</ThemeProvider>
);
return (
<ThemeProvider defaultTheme="dark" storageKey="vilm-theme">
<AccountProvider>
<Router>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/show/tv/:id" element={<Tv />} />
<Route path="/show/movie/:id" element={<Movie />} />
<Route path="/search" element={<Search />} />
<Route path="/setting" element={<Setting />} />
<Route path="/watchlist/movie" element={<Movies />} />
<Route path="/watchlist/tv" element={<TvShows />} />
<Route path="*" element={<NotFound />} />
</Routes>
<Toaster />
</Router>
</AccountProvider>
</ThemeProvider>
);
}

export default App;
40 changes: 40 additions & 0 deletions src/components/AddToWatchlistButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { AccountStates } from '@/types/response';
import { ResponseMessage } from '@/utils/$fetch';

interface Props {
states: AccountStates;
type: 'movie' | 'tv';
mediaId: number | string;
}

export default function AddToWatchlistButton({ states, type, mediaId }: Props) {
const { account } = useAccount();
const { toast } = useToast();
const [isWatchlisted, setIsWatchlisted] = useState(states.watchlist)
const addToWatchlist = async () => {
try {
const { data } = await $fetch<ResponseMessage>(`/account/${account?.id}/watchlist`, {
method: 'POST',
body: {
"media_type": type,
"media_id": mediaId,
"watchlist": isWatchlisted === true ? false : true,
}
});
setIsWatchlisted(!isWatchlisted);

toast({
description: data.status_message
})
} catch (e) {
console.error(e)
}
};


return (
<Button className='lg:col-span-1 col-span-full' onClick={addToWatchlist} variant={isWatchlisted ? 'secondary' : 'default'} >
{isWatchlisted === true ? 'Remove from' : 'Add to'} Watchlist
</Button>
)
}
40 changes: 40 additions & 0 deletions src/components/BackdropCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { ComponentPropsWithRef } from "react";

export interface SimpleBaseMedia {
adult: boolean;
backdrop_path: string;
genre_ids: number[];
id: number;
original_language: string;
overview: string;
popularity: number;
poster_path: string;
vote_average: number;
vote_count: number;
}


interface BackdropCardProps<T> extends ComponentPropsWithRef<'div'> {
media: T;
title: string;
}

export default function BackdropCard<T extends SimpleBaseMedia>({ media, title, ...props }: BackdropCardProps<T>) {
const isMovieType = Object.prototype.hasOwnProperty.call(media, 'video');

return (
<div {...props} className={`${props.className} rounded-md group transition-transform overflow-clip hover:scale-110`}>
<RImage src={imageUrl({
path: media.backdrop_path,
size: 'w300',
type: 'backdrop'
})}
type="backdrop"
alt={media.overview}
/>
<div className="block lg:hidden group-hover:block">
<Link to={`/show/${isMovieType ? 'movie' : 'tv'}/${media.id}`}>{title}</Link>
</div>
</div>
)
}
Loading

0 comments on commit 74abf9a

Please sign in to comment.