Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/watchlist #27

Merged
merged 5 commits into from
May 24, 2024
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
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