Basic component structure

This commit is contained in:
Jelle van Snik 2022-02-07 23:22:35 +01:00
parent b3c4ad5e15
commit 81373da404
17 changed files with 182 additions and 22 deletions

View File

@ -1,24 +1,16 @@
import { GetProviderFromId, SearchProviders, MWMedia, MWMediaType } from '@/scrapers'; import { Route, Switch } from 'react-router-dom';
import { useState } from 'react';
import './index.css'; import './index.css';
import { MovieView } from './views/MovieView';
import { SearchView } from './views/SearchView';
import { SeriesView } from './views/SeriesView';
function App() { function App() {
const [results, setResults] = useState<MWMedia[]>([]);
async function runSearch() {
const results = await SearchProviders({ type: MWMediaType.MOVIE, searchQuery: "abc" });
setResults(results);
}
return ( return (
<> <Switch>
<h1>Search</h1> <Route exact path="/" component={SearchView} />
<button onClick={() => runSearch()}>Search</button> <Route exact path="/media/movie" component={MovieView} />
<h1>Search results</h1> <Route exact path="/media/series" component={SeriesView} />
{results.map(v=>( </Switch>
<p>{v.title} ({GetProviderFromId(v.providerId)?.displayName})</p>
))}
</>
); );
} }

View 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
View 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] }} />;
}

View 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>
)
}

View 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;
}

View 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>
)
}

View 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} />
}

View File

View File

@ -1,9 +1,11 @@
import { theFlixScraper } from "./list/theflix"; import { theFlixMovieScraper } from "./list/theflixmovie";
import { theFlixSeriesScraper } from "./list/theflixseries";
import { MWMediaProvider, MWQuery } from "./types"; import { MWMediaProvider, MWQuery } from "./types";
export * from "./types"; export * from "./types";
const mediaProvidersUnchecked: MWMediaProvider[] = [ const mediaProvidersUnchecked: MWMediaProvider[] = [
theFlixScraper theFlixMovieScraper,
theFlixSeriesScraper,
] ]
export const mediaProviders: MWMediaProvider[] = mediaProvidersUnchecked.filter(v=>v.enabled); export const mediaProviders: MWMediaProvider[] = mediaProvidersUnchecked.filter(v=>v.enabled);

View File

@ -1,7 +1,7 @@
import { MWMedia, MWMediaProvider, MWMediaType, MWPortableMedia, MWQuery } from "@/scrapers/types"; import { MWMedia, MWMediaProvider, MWMediaType, MWPortableMedia, MWQuery } from "@/scrapers/types";
export const theFlixScraper: MWMediaProvider = { export const theFlixMovieScraper: MWMediaProvider = {
id: "theflix", id: "theflixmovie",
enabled: true, enabled: true,
type: MWMediaType.MOVIE, type: MWMediaType.MOVIE,
displayName: "TheFlix", displayName: "TheFlix",
@ -17,7 +17,7 @@ export const theFlixScraper: MWMediaProvider = {
return [{ return [{
mediaId: "a", mediaId: "a",
providerId: this.id, providerId: this.id,
title: "testing", title: `movie test`,
}]; }];
}, },
} }

View 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`,
}];
},
}

View File

View File

7
src/views/MovieView.tsx Normal file
View File

@ -0,0 +1,7 @@
export function MovieView() {
return (
<div>
<p>Movie view here</p>
</div>
)
}

23
src/views/SearchView.tsx Normal file
View 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
View File

@ -0,0 +1,7 @@
export function SeriesView() {
return (
<div>
<p>Series view here</p>
</div>
)
}