diff --git a/components/Checkbox.tsx b/components/Checkbox.tsx new file mode 100644 index 0000000..09539c8 --- /dev/null +++ b/components/Checkbox.tsx @@ -0,0 +1,95 @@ +import { Signal, useSignal } from "@preact/signals"; +import { useId, useState } from "preact/hooks"; + +interface CheckboxProps { + label: string; + isChecked?: boolean; + onChange: (isChecked: boolean) => void; +} + +const Checkbox2: preact.FunctionalComponent = ( + { label, isChecked = false, onChange }, +) => { + const [checked, setChecked] = useState(isChecked); + + const toggleCheckbox = () => { + const newChecked = !checked; + setChecked(newChecked); + onChange(newChecked); + }; + + return ( +
+ + {label} + + +
+ ); +}; + +const Checkbox = ( + { label, checked = useSignal(false) }: { + label: string; + checked?: Signal; + }, +) => { + const _id = useId(); + const id = `checkbox-${_id}`; + return ( + + ); +}; + +export default Checkbox; diff --git a/components/Image.tsx b/components/Image.tsx new file mode 100644 index 0000000..c965741 --- /dev/null +++ b/components/Image.tsx @@ -0,0 +1,14 @@ +import { isLocalImage } from "@lib/string.ts"; + +export function Image( + props: { class: string; src: string; width?: number; height?: number }, +) { + if (isLocalImage(props.src)) { + } + + return ( +
+ +
+ ); +} diff --git a/components/MovieCard.tsx b/components/MovieCard.tsx index 559d103..5e323c4 100644 --- a/components/MovieCard.tsx +++ b/components/MovieCard.tsx @@ -1,5 +1,6 @@ import { Card } from "@components/Card.tsx"; import { Movie } from "@lib/resource/movies.ts"; +import { Series } from "@lib/resource/series.ts"; export function MovieCard({ movie }: { movie: Movie }) { const { meta: { image = "/placeholder.svg" } = {} } = movie; @@ -16,3 +17,19 @@ export function MovieCard({ movie }: { movie: Movie }) { /> ); } + +export function SeriesCard({ series }: { series: Series }) { + const { meta: { image = "/placeholder.svg" } = {} } = series; + + const imageUrl = image?.startsWith("Media/series/") + ? `/api/images?image=${image}&width=200&height=200` + : image; + + return ( + + ); +} diff --git a/components/Rating.tsx b/components/Rating.tsx new file mode 100644 index 0000000..df22853 --- /dev/null +++ b/components/Rating.tsx @@ -0,0 +1,34 @@ +import { IconStar, IconStarFilled } from "@components/icons.tsx"; +import { useSignal } from "@preact/signals"; +import { useState } from "preact/hooks"; + +export const Rating = ( + props: { max?: number; rating: number }, +) => { + const [rating, setRating] = useState(props.rating); + const [hover, setHover] = useState(0); + const max = useSignal(props.max || 5); + + return ( +
+ {Array.from({ length: max.value }).map((_, i) => { + return ( + setHover(i + 1)} + onClick={() => setRating(i + 1)} + > + {(i + 1) <= rating || (i + 1) <= hover + ? + : } + + ); + })} +
+ ); +}; diff --git a/fresh.gen.ts b/fresh.gen.ts index 41b7cc3..93d662f 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -21,19 +21,21 @@ import * as $15 from "./routes/api/query/index.ts"; import * as $16 from "./routes/api/recipes/[name].ts"; import * as $17 from "./routes/api/recipes/index.ts"; import * as $18 from "./routes/api/resources.ts"; -import * as $19 from "./routes/api/series/index.ts"; -import * as $20 from "./routes/api/tmdb/[id].ts"; -import * as $21 from "./routes/api/tmdb/credits/[id].ts"; -import * as $22 from "./routes/api/tmdb/query.ts"; -import * as $23 from "./routes/articles/[name].tsx"; -import * as $24 from "./routes/articles/index.tsx"; -import * as $25 from "./routes/index.tsx"; -import * as $26 from "./routes/movies/[name].tsx"; -import * as $27 from "./routes/movies/index.tsx"; -import * as $28 from "./routes/recipes/[name].tsx"; -import * as $29 from "./routes/recipes/index.tsx"; -import * as $30 from "./routes/series/[name].tsx"; -import * as $31 from "./routes/series/index.tsx"; +import * as $19 from "./routes/api/series/[name].ts"; +import * as $20 from "./routes/api/series/enhance/[name].ts"; +import * as $21 from "./routes/api/series/index.ts"; +import * as $22 from "./routes/api/tmdb/[id].ts"; +import * as $23 from "./routes/api/tmdb/credits/[id].ts"; +import * as $24 from "./routes/api/tmdb/query.ts"; +import * as $25 from "./routes/articles/[name].tsx"; +import * as $26 from "./routes/articles/index.tsx"; +import * as $27 from "./routes/index.tsx"; +import * as $28 from "./routes/movies/[name].tsx"; +import * as $29 from "./routes/movies/index.tsx"; +import * as $30 from "./routes/recipes/[name].tsx"; +import * as $31 from "./routes/recipes/index.tsx"; +import * as $32 from "./routes/series/[name].tsx"; +import * as $33 from "./routes/series/index.tsx"; import * as $$0 from "./islands/Counter.tsx"; import * as $$1 from "./islands/IngredientsList.tsx"; import * as $$2 from "./islands/KMenu.tsx"; @@ -66,19 +68,21 @@ const manifest = { "./routes/api/recipes/[name].ts": $16, "./routes/api/recipes/index.ts": $17, "./routes/api/resources.ts": $18, - "./routes/api/series/index.ts": $19, - "./routes/api/tmdb/[id].ts": $20, - "./routes/api/tmdb/credits/[id].ts": $21, - "./routes/api/tmdb/query.ts": $22, - "./routes/articles/[name].tsx": $23, - "./routes/articles/index.tsx": $24, - "./routes/index.tsx": $25, - "./routes/movies/[name].tsx": $26, - "./routes/movies/index.tsx": $27, - "./routes/recipes/[name].tsx": $28, - "./routes/recipes/index.tsx": $29, - "./routes/series/[name].tsx": $30, - "./routes/series/index.tsx": $31, + "./routes/api/series/[name].ts": $19, + "./routes/api/series/enhance/[name].ts": $20, + "./routes/api/series/index.ts": $21, + "./routes/api/tmdb/[id].ts": $22, + "./routes/api/tmdb/credits/[id].ts": $23, + "./routes/api/tmdb/query.ts": $24, + "./routes/articles/[name].tsx": $25, + "./routes/articles/index.tsx": $26, + "./routes/index.tsx": $27, + "./routes/movies/[name].tsx": $28, + "./routes/movies/index.tsx": $29, + "./routes/recipes/[name].tsx": $30, + "./routes/recipes/index.tsx": $31, + "./routes/series/[name].tsx": $32, + "./routes/series/index.tsx": $33, }, islands: { "./islands/Counter.tsx": $$0, diff --git a/islands/KMenu/commands.ts b/islands/KMenu/commands.ts index 707449e..bd75c12 100644 --- a/islands/KMenu/commands.ts +++ b/islands/KMenu/commands.ts @@ -3,6 +3,7 @@ import { addMovieInfos } from "@islands/KMenu/commands/add_movie_infos.ts"; import { createNewMovie } from "@islands/KMenu/commands/create_movie.ts"; import { createNewArticle } from "@islands/KMenu/commands/create_article.ts"; import { getCookie } from "@lib/string.ts"; +import { addSeriesInfo } from "@islands/KMenu/commands/add_series_infos.ts"; export const menus: Record = { main: { @@ -54,6 +55,7 @@ export const menus: Record = { return !!getCookie("session_cookie"); }, }, + addSeriesInfo, createNewArticle, createNewMovie, addMovieInfos, diff --git a/islands/KMenu/commands/add_movie_infos.ts b/islands/KMenu/commands/add_movie_infos.ts index 651643b..09a5e9a 100644 --- a/islands/KMenu/commands/add_movie_infos.ts +++ b/islands/KMenu/commands/add_movie_infos.ts @@ -46,7 +46,6 @@ export const addMovieInfos: MenuEntry = { const loc = globalThis["location"]; if (!getCookie("session_cookie")) return false; return (loc?.pathname?.includes("movie") && - !loc.pathname.endsWith("movies")) || - (loc?.pathname?.includes("series") && !loc.pathname.endsWith("series")); + !loc.pathname.endsWith("movies")); }, }; diff --git a/islands/KMenu/commands/add_series_infos.ts b/islands/KMenu/commands/add_series_infos.ts index 58a405b..3e8c92d 100644 --- a/islands/KMenu/commands/add_series_infos.ts +++ b/islands/KMenu/commands/add_series_infos.ts @@ -1,39 +1,43 @@ import { MenuEntry } from "@islands/KMenu/types.ts"; -import { TMDBMovie } from "@lib/types.ts"; +import { TMDBSeries } from "@lib/types.ts"; import { getCookie } from "@lib/string.ts"; import { Series } from "@lib/resource/series.ts"; -export const addMovieInfos: MenuEntry = { - title: "Add Movie infos", +export const addSeriesInfo: MenuEntry = { + title: "Add Series infos", meta: "", icon: "IconReportSearch", cb: async (state, context) => { + console.log({ state, context }); state.activeState.value = "loading"; - const movie = context as Series; + const series = context as Series; - const query = movie.name; + const query = series.name; const response = await fetch( `/api/tmdb/query?q=${encodeURIComponent(query)}&type=serie`, ); - const json = await response.json() as TMDBMovie[]; + const json = await response.json() as TMDBSeries[]; - const menuID = `result/${movie.name}`; + console.log({ json }); + + const menuID = `result/${series.name}`; state.menus[menuID] = { title: "Select", entries: json.map((m) => ({ - title: `${m.title} released ${m.release_date}`, + title: `${m.name || m.original_name} released ${m.first_air_date}`, cb: async () => { state.activeState.value = "loading"; - await fetch(`/api/movies/enhance/${movie.name}/`, { + console.log({ m }); + await fetch(`/api/series/enhance/${series.name}/`, { method: "POST", body: JSON.stringify({ tmdbId: m.id }), }); state.visible.value = false; state.activeState.value = "normal"; - window.location.reload(); + //window.location.reload(); }, })), }; @@ -45,8 +49,7 @@ export const addMovieInfos: MenuEntry = { visible: () => { const loc = globalThis["location"]; if (!getCookie("session_cookie")) return false; - return (loc?.pathname?.includes("movie") && - !loc.pathname.endsWith("movies")) || - (loc?.pathname?.includes("series") && !loc.pathname.endsWith("series")); + return (loc?.pathname?.includes("series") && + !loc.pathname.endsWith("series")); }, }; diff --git a/islands/Search.tsx b/islands/Search.tsx index a27a7b2..f7b1402 100644 --- a/islands/Search.tsx +++ b/islands/Search.tsx @@ -6,6 +6,9 @@ import { SearchResult } from "@lib/types.ts"; import { resources } from "@lib/resources.ts"; import { isLocalImage } from "@lib/string.ts"; import { IS_BROWSER } from "$fresh/runtime.ts"; +import Checkbox from "@components/Checkbox.tsx"; +import { Rating } from "@components/Rating.tsx"; +import { useSignal } from "@preact/signals"; export const RedirectSearchHandler = () => { useEventListener("keydown", (e: KeyboardEvent) => { @@ -79,15 +82,24 @@ const SearchComponent = ( const [searchQuery, setSearchQuery] = useState(q); const [data, setData] = useState(); const [isLoading, setIsLoading] = useState(false); + const showSeenStatus = useSignal(false); const inputRef = useRef(null); if ("history" in globalThis) { const u = new URL(window.location.href); if (u.searchParams.get("q") !== searchQuery) { u.searchParams.set("q", searchQuery); - window.history.replaceState({}, "", u); } + if (showSeenStatus.value) { + u.searchParams.set("status", "not-seen"); + } else { + u.searchParams.delete("status"); + } + + window.history.replaceState({}, "", u); } + console.log({ showSeen: showSeenStatus.value }); + const fetchData = async (query: string) => { try { setIsLoading(true); @@ -98,6 +110,9 @@ const SearchComponent = ( } else { return; } + if (showSeenStatus.value) { + fetchUrl.searchParams.set("status", "not-seen"); + } if (type) { fetchUrl.searchParams.set("type", type); } @@ -131,25 +146,28 @@ const SearchComponent = ( debouncedFetchData(q); }, []); - console.log({ data, isLoading }); return (
-
- {isLoading && searchQuery - ? - : } - -
+
+
+ {isLoading && searchQuery + ? + : } + +
+ + +
{data?.hits?.length && !isLoading ? : isLoading diff --git a/lib/hooks/useEventListener.ts b/lib/hooks/useEventListener.ts index a3e3416..445df3f 100644 --- a/lib/hooks/useEventListener.ts +++ b/lib/hooks/useEventListener.ts @@ -5,7 +5,6 @@ export function useEventListener( handler: (event: T) => void, element: Window | HTMLElement = window, ) { - console.log("Add Eventlistener", { eventName, element, handler }); // Create a ref that stores handler const savedHandler = useRef<(event: Event) => void>(); diff --git a/lib/tmdb.ts b/lib/tmdb.ts index f68bbaf..f90c058 100644 --- a/lib/tmdb.ts +++ b/lib/tmdb.ts @@ -14,10 +14,27 @@ export function getMovie(id: number) { return moviedb.movieInfo({ id }); } +export function getSeries(id: number) { + return moviedb.tvInfo({ id }); +} + export function getMovieCredits(id: number) { return moviedb.movieCredits(id); } +export function getSeriesCredits(id: number) { + return moviedb.tvCredits(id); +} + +export async function getMovieGenre(id: number) { + const genres = await cache.get("/genres/movies"); + return moviedb.genreTvList(); +} + +export async function getSeriesGenre(id: number) { + const genres = await cache.get("/genres/series"); +} + export async function getMoviePoster(id: string): Promise { const cachedPoster = await cache.get("posters:" + id); diff --git a/lib/types.ts b/lib/types.ts index b5fb9d8..5157a90 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -17,6 +17,22 @@ export interface TMDBMovie { vote_average: number; vote_count: number; } +export interface TMDBSeries { + adult: boolean; + backdrop_path: string; + genre_ids: number[]; + id: number; + origin_country: string[]; + original_language: string; + original_name: string; + overview: string; + popularity: number; + poster_path: string; + first_air_date: string; + name: string; + vote_average: number; + vote_count: number; +} export interface GiteaOauthUser { sub: string; diff --git a/lib/typesense.ts b/lib/typesense.ts index c281200..a2484fd 100644 --- a/lib/typesense.ts +++ b/lib/typesense.ts @@ -76,7 +76,7 @@ async function initializeTypesense() { { name: "name", type: "string" }, { name: "type", type: "string", facet: true }, { name: "date", type: "string", optional: true }, - { name: "author", type: "string", facet: true }, + { name: "author", type: "string", facet: true, optional: true }, { name: "rating", type: "int32", facet: true }, { name: "tags", type: "string[]", facet: true }, { name: "description", type: "string", optional: true }, diff --git a/routes/_app.tsx b/routes/_app.tsx index c77ae4d..51e8dd6 100644 --- a/routes/_app.tsx +++ b/routes/_app.tsx @@ -9,6 +9,10 @@ export default function App({ Component }: AppProps) {