mirror of
https://github.com/movie-web/movie-web.git
synced 2024-11-10 23:55:05 +01:00
Basic component structure
This commit is contained in:
parent
b3c4ad5e15
commit
81373da404
26
src/App.tsx
26
src/App.tsx
@ -1,24 +1,16 @@
|
||||
import { GetProviderFromId, SearchProviders, MWMedia, MWMediaType } from '@/scrapers';
|
||||
import { useState } from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import './index.css';
|
||||
import { MovieView } from './views/MovieView';
|
||||
import { SearchView } from './views/SearchView';
|
||||
import { SeriesView } from './views/SeriesView';
|
||||
|
||||
function App() {
|
||||
const [results, setResults] = useState<MWMedia[]>([]);
|
||||
|
||||
async function runSearch() {
|
||||
const results = await SearchProviders({ type: MWMediaType.MOVIE, searchQuery: "abc" });
|
||||
setResults(results);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Search</h1>
|
||||
<button onClick={() => runSearch()}>Search</button>
|
||||
<h1>Search results</h1>
|
||||
{results.map(v=>(
|
||||
<p>{v.title} ({GetProviderFromId(v.providerId)?.displayName})</p>
|
||||
))}
|
||||
</>
|
||||
<Switch>
|
||||
<Route exact path="/" component={SearchView} />
|
||||
<Route exact path="/media/movie" component={MovieView} />
|
||||
<Route exact path="/media/series" component={SeriesView} />
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
|
8
src/components/Buttons/ButtonControl.tsx
Normal file
8
src/components/Buttons/ButtonControl.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
export interface ButtonControlProps {
|
||||
onClick?: () => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function ButtonControl({ onClick, children }: ButtonControlProps) {
|
||||
return <button onClick={onClick}>{children}</button>
|
||||
}
|
15
src/components/Icon.tsx
Normal file
15
src/components/Icon.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
export enum Icons {
|
||||
SEARCH = "search",
|
||||
}
|
||||
|
||||
export interface IconProps {
|
||||
icon: Icons;
|
||||
}
|
||||
|
||||
const iconList = {
|
||||
search: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>`,
|
||||
}
|
||||
|
||||
export function Icon(props: IconProps) {
|
||||
return <span dangerouslySetInnerHTML={{ __html: iconList[props.icon] }} />;
|
||||
}
|
20
src/components/SearchBar.tsx
Normal file
20
src/components/SearchBar.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { ButtonControl } from './Buttons/ButtonControl';
|
||||
import { Icon, Icons } from './Icon';
|
||||
import { TextInputControl, TextInputControlPropsNoLabel } from './TextInputs/TextInputControl';
|
||||
|
||||
export interface SearchBarProps extends TextInputControlPropsNoLabel {
|
||||
buttonText?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export function SearchBarInput(props: SearchBarProps) {
|
||||
return (
|
||||
<div>
|
||||
<TextInputControl onChange={props.onChange} value={props.value} />
|
||||
<ButtonControl onClick={props.onClick}>
|
||||
<Icon icon={Icons.SEARCH} />
|
||||
{ props.buttonText || "Search" }
|
||||
</ButtonControl>
|
||||
</div>
|
||||
)
|
||||
}
|
23
src/components/TextInputs/TextInputControl.tsx
Normal file
23
src/components/TextInputs/TextInputControl.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
export interface TextInputControlPropsNoLabel {
|
||||
onChange?: (data: string) => void;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface TextInputControlProps extends TextInputControlPropsNoLabel {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export function TextInputControl({ onChange, value, label }: TextInputControlProps) {
|
||||
const input = <input type="text" onChange={(e) => onChange && onChange(e.target.value)} value={value} />
|
||||
|
||||
if (label) {
|
||||
return (
|
||||
<label>
|
||||
<span>{label}</span>
|
||||
{input}
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
30
src/components/media/MediaCard.tsx
Normal file
30
src/components/media/MediaCard.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { GetProviderFromId, MWMedia, MWMediaType } from "@/scrapers";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export interface MediaCardProps {
|
||||
media: MWMedia;
|
||||
watchedPercentage: Number;
|
||||
}
|
||||
|
||||
function MediaCardContent({ media, watchedPercentage }: MediaCardProps) {
|
||||
return (
|
||||
<>
|
||||
<p>{media.title} ({GetProviderFromId(media.providerId)?.displayName})</p>
|
||||
<p>{watchedPercentage}% watched</p>
|
||||
<hr/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function MediaCard(props: MediaCardProps) {
|
||||
const provider = GetProviderFromId(props.media.providerId);
|
||||
let link = "movie"
|
||||
if (provider?.type === MWMediaType.SERIES)
|
||||
link = "series";
|
||||
|
||||
return (
|
||||
<Link to={`/media/${link}`}>
|
||||
<MediaCardContent {...props} />
|
||||
</Link>
|
||||
)
|
||||
}
|
10
src/components/media/WatchedMediaCard.tsx
Normal file
10
src/components/media/WatchedMediaCard.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { MWMedia } from "@/scrapers";
|
||||
import { MediaCard } from "./MediaCard";
|
||||
|
||||
export interface WatchedMediaCardProps {
|
||||
media: MWMedia;
|
||||
}
|
||||
|
||||
export function WatchedMediaCard(props: WatchedMediaCardProps) {
|
||||
return <MediaCard watchedPercentage={42} media={props.media} />
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
import { theFlixScraper } from "./list/theflix";
|
||||
import { theFlixMovieScraper } from "./list/theflixmovie";
|
||||
import { theFlixSeriesScraper } from "./list/theflixseries";
|
||||
import { MWMediaProvider, MWQuery } from "./types";
|
||||
export * from "./types";
|
||||
|
||||
const mediaProvidersUnchecked: MWMediaProvider[] = [
|
||||
theFlixScraper
|
||||
theFlixMovieScraper,
|
||||
theFlixSeriesScraper,
|
||||
]
|
||||
export const mediaProviders: MWMediaProvider[] = mediaProvidersUnchecked.filter(v=>v.enabled);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { MWMedia, MWMediaProvider, MWMediaType, MWPortableMedia, MWQuery } from "@/scrapers/types";
|
||||
|
||||
export const theFlixScraper: MWMediaProvider = {
|
||||
id: "theflix",
|
||||
export const theFlixMovieScraper: MWMediaProvider = {
|
||||
id: "theflixmovie",
|
||||
enabled: true,
|
||||
type: MWMediaType.MOVIE,
|
||||
displayName: "TheFlix",
|
||||
@ -17,7 +17,7 @@ export const theFlixScraper: MWMediaProvider = {
|
||||
return [{
|
||||
mediaId: "a",
|
||||
providerId: this.id,
|
||||
title: "testing",
|
||||
title: `movie test`,
|
||||
}];
|
||||
},
|
||||
}
|
23
src/scrapers/list/theflixseries/index.ts
Normal file
23
src/scrapers/list/theflixseries/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { MWMedia, MWMediaProvider, MWMediaType, MWPortableMedia, MWQuery } from "@/scrapers/types";
|
||||
|
||||
export const theFlixSeriesScraper: MWMediaProvider = {
|
||||
id: "theflixseries",
|
||||
enabled: true,
|
||||
type: MWMediaType.SERIES,
|
||||
displayName: "TheFlix",
|
||||
|
||||
async getMediaFromPortable(media: MWPortableMedia): Promise<MWMedia> {
|
||||
return {
|
||||
...media,
|
||||
title: "title here"
|
||||
}
|
||||
},
|
||||
|
||||
async searchForMedia(query: MWQuery): Promise<MWMedia[]> {
|
||||
return [{
|
||||
mediaId: "b",
|
||||
providerId: this.id,
|
||||
title: `series test`,
|
||||
}];
|
||||
},
|
||||
}
|
7
src/views/MovieView.tsx
Normal file
7
src/views/MovieView.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export function MovieView() {
|
||||
return (
|
||||
<div>
|
||||
<p>Movie view here</p>
|
||||
</div>
|
||||
)
|
||||
}
|
23
src/views/SearchView.tsx
Normal file
23
src/views/SearchView.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { WatchedMediaCard } from "@/components/media/WatchedMediaCard";
|
||||
import { SearchBarInput } from "@/components/SearchBar";
|
||||
import { GetProviderFromId, MWMedia, MWMediaType, SearchProviders } from "@/scrapers";
|
||||
import { useState } from "react";
|
||||
|
||||
export function SearchView() {
|
||||
const [results, setResults] = useState<MWMedia[]>([]);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
async function runSearch() {
|
||||
const results = await SearchProviders({ type: MWMediaType.MOVIE, searchQuery: search });
|
||||
setResults(results);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Search</h1>
|
||||
<SearchBarInput onChange={setSearch} value={search} onClick={runSearch}/>
|
||||
<h1>Search results</h1>
|
||||
{results.map((v)=>(<WatchedMediaCard media={v} />))}
|
||||
</div>
|
||||
)
|
||||
}
|
7
src/views/SeriesView.tsx
Normal file
7
src/views/SeriesView.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export function SeriesView() {
|
||||
return (
|
||||
<div>
|
||||
<p>Series view here</p>
|
||||
</div>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user