From 546f9c34d2aa4083f205f85692542d873bfcfc48 Mon Sep 17 00:00:00 2001 From: lukas Date: Sun, 1 Aug 2021 22:56:24 +0200 Subject: [PATCH] JSX component for api requests --- src/elements/APIComponent.tsx | 48 +++++ src/elements/KeyComponent.tsx | 32 +++ src/pages/HomePage/HomePage.tsx | 289 ++++++++++++---------------- src/pages/RandomPage/RandomPage.tsx | 111 +++-------- 4 files changed, 237 insertions(+), 243 deletions(-) create mode 100644 src/elements/APIComponent.tsx create mode 100644 src/elements/KeyComponent.tsx diff --git a/src/elements/APIComponent.tsx b/src/elements/APIComponent.tsx new file mode 100644 index 0000000..83047ad --- /dev/null +++ b/src/elements/APIComponent.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import {APINode, callAPI} from '../utils/Api'; + +interface Props { + onLoad?: JSX.Element; + render: (data: T, actions: {refresh: () => void}) => JSX.Element; + node: APINode; + action: string; + params?: {[_: string]: string | number | boolean | object}; +} + +interface State { + loaded: boolean; +} + +export default class APIComponent extends React.Component, State> { + state = { + loaded: false + }; + + data: T | undefined; + + render(): JSX.Element { + if (this.state.loaded && this.data !== undefined) { + return this.props.render(this.data, {refresh: () => this.loadData()}); + } else { + return this.props.onLoad ? this.props.onLoad : <>; + } + } + + componentDidMount(): void { + this.loadData(); + } + + componentDidUpdate(prevProps: Readonly>): void { + if (prevProps.params !== this.props.params) { + this.loadData(); + } + } + + private loadData(): void { + this.setState({loaded: false}); + callAPI(this.props.node, {action: this.props.action, ...this.props.params}, (result: T) => { + this.data = result; + this.setState({loaded: true}); + }); + } +} diff --git a/src/elements/KeyComponent.tsx b/src/elements/KeyComponent.tsx new file mode 100644 index 0000000..b24e939 --- /dev/null +++ b/src/elements/KeyComponent.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +interface Props { + listenKey: string; + onKey: () => void; +} + +export default class KeyComponent extends React.Component { + constructor(props: Props) { + super(props); + + this.handler = this.handler.bind(this); + } + + render(): JSX.Element { + return <>{this.props.children}; + } + + componentDidMount(): void { + document.addEventListener('keyup', this.handler); + } + + componentWillUnmount(): void { + document.removeEventListener('keyup', this.handler); + } + + private handler(e: KeyboardEvent): void { + if (e.key === this.props.listenKey) { + this.props.onKey(); + } + } +} diff --git a/src/pages/HomePage/HomePage.tsx b/src/pages/HomePage/HomePage.tsx index 752e259..47bff82 100644 --- a/src/pages/HomePage/HomePage.tsx +++ b/src/pages/HomePage/HomePage.tsx @@ -5,7 +5,7 @@ import VideoContainer from '../../elements/VideoContainer/VideoContainer'; import style from './HomePage.module.css'; import PageTitle, {Line} from '../../elements/PageTitle/PageTitle'; -import {APINode, callAPI} from '../../utils/Api'; +import {APINode} from '../../utils/Api'; import {Route, Switch, withRouter} from 'react-router-dom'; import {RouteComponentProps} from 'react-router'; import SearchHandling from './SearchHandling'; @@ -13,6 +13,8 @@ import {VideoTypes} from '../../types/ApiTypes'; import {DefaultTags} from '../../types/GeneralTypes'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {faSortDown} from '@fortawesome/free-solid-svg-icons'; +import APIComponent from '../../elements/APIComponent'; +import {TagType} from '../../types/VideoTypes'; // eslint-disable-next-line no-shadow export enum SortBy { @@ -26,11 +28,10 @@ export enum SortBy { interface Props extends RouteComponentProps {} interface state { - sideinfo: VideoTypes.startDataType; subtitle: string; - data: VideoTypes.VideoUnloadedType[]; - selectionnr: number; sortby: string; + sortState: SortBy; + tagState: TagType; } /** @@ -40,166 +41,130 @@ export class HomePage extends React.Component { /** keyword variable needed temporary store search keyword */ keyword = ''; - constructor(props: Props) { - super(props); - - this.state = { - sideinfo: { - VideoNr: 0, - FullHdNr: 0, - HDNr: 0, - SDNr: 0, - DifferentTags: 0, - Tagged: 0 - }, - subtitle: 'All Videos', - data: [], - selectionnr: 0, - sortby: 'Date Added' - }; - } - - sortState = SortBy.date; - tagState = DefaultTags.all; - - componentDidMount(): void { - // initial get of all videos - this.fetchVideoData(); - this.fetchStartData(); - } - - /** - * fetch available videos for specified tag - * this function clears all preview elements an reloads gravity with tag - * - * @param tag tag to fetch videos - */ - fetchVideoData(): void { - callAPI( - APINode.Video, - {action: 'getMovies', Tag: this.tagState.TagId, Sort: this.sortState}, - (result: {Videos: VideoTypes.VideoUnloadedType[]; TagName: string}) => { - this.setState({ - data: result.Videos, - selectionnr: result.Videos.length - }); - } - ); - } - - /** - * fetch the necessary data for left info box - */ - fetchStartData(): void { - callAPI(APINode.Video, {action: 'getStartData'}, (result: VideoTypes.startDataType) => { - this.setState({sideinfo: result}); - }); - } + state = { + subtitle: 'All Videos', + sortby: 'Date Added', + sortState: SortBy.date, + tagState: DefaultTags.all + }; render(): JSX.Element { return ( - <> - - - - - - -
{ - e.preventDefault(); - this.props.history.push('/search/' + this.keyword); - }}> - { - this.keyword = e.target.value; - }} - /> - -
-
- - Infos: - - - {this.state.sideinfo.VideoNr} Videos Total! - - - {this.state.sideinfo.FullHdNr} FULL-HD Videos! - - - {this.state.sideinfo.HDNr} HD Videos! - - - {this.state.sideinfo.SDNr} SD Videos! - - - {this.state.sideinfo.DifferentTags} different Tags! - - - Default Tags: - { - this.tagState = DefaultTags.all; - this.fetchVideoData(); - this.setState({subtitle: 'All Videos'}); - }} - /> - { - this.tagState = DefaultTags.fullhd; - this.fetchVideoData(); - this.setState({subtitle: 'Full Hd Videos'}); - }} - /> - { - this.tagState = DefaultTags.lowq; - this.fetchVideoData(); - this.setState({subtitle: 'Low Quality Videos'}); - }} - /> - { - this.tagState = DefaultTags.hd; - this.fetchVideoData(); - this.setState({subtitle: 'HD Videos'}); - }} - /> - -
- Sort By: -
- - {this.state.sortby} - - -
- this.onDropDownItemClick(SortBy.date, 'Date Added')}>Date Added - this.onDropDownItemClick(SortBy.likes, 'Most likes')}>Most likes - this.onDropDownItemClick(SortBy.random, 'Random')}>Random - this.onDropDownItemClick(SortBy.name, 'Name')}>Name - this.onDropDownItemClick(SortBy.length, 'Length')}>Length + + + + + + ( + <> + +
{ + e.preventDefault(); + this.props.history.push('/search/' + this.keyword); + }}> + { + this.keyword = e.target.value; + }} + /> + +
+
+ + ( + <> + Infos: + + + {sidebardata.VideoNr} Videos Total! + + + {sidebardata.FullHdNr} FULL-HD Videos! + + + {sidebardata.HDNr} HD Videos! + + + {sidebardata.SDNr} SD Videos! + + + {sidebardata.DifferentTags} different Tags! + + + Default Tags: + { + this.setState({tagState: DefaultTags.all, subtitle: 'All Videos'}); + }} + /> + { + this.setState({tagState: DefaultTags.fullhd, subtitle: 'Full Hd Videos'}); + }} + /> + { + this.setState({ + tagState: DefaultTags.lowq, + subtitle: 'Low Quality Videos' + }); + }} + /> + { + this.setState({tagState: DefaultTags.hd, subtitle: 'HD Videos'}); + }} + /> + + )} + node={APINode.Video} + action='getStartData' + /> + +
+ Sort By: +
+ + {this.state.sortby} + + +
+ this.onDropDownItemClick(SortBy.date, 'Date Added')}> + Date Added + + this.onDropDownItemClick(SortBy.likes, 'Most likes')}> + Most likes + + this.onDropDownItemClick(SortBy.random, 'Random')}>Random + this.onDropDownItemClick(SortBy.name, 'Name')}>Name + this.onDropDownItemClick(SortBy.length, 'Length')}>Length +
+
-
-
- - -
- - - + +
+ + )} + node={APINode.Video} + action='getMovies' + params={{Tag: this.state.tagState.TagId, Sort: this.state.sortState}} + /> + + ); } @@ -209,9 +174,7 @@ export class HomePage extends React.Component { * @param name new header title */ onDropDownItemClick(type: SortBy, name: string): void { - this.sortState = type; - this.setState({sortby: name}); - this.fetchVideoData(); + this.setState({sortby: name, sortState: type}); } } diff --git a/src/pages/RandomPage/RandomPage.tsx b/src/pages/RandomPage/RandomPage.tsx index baa7b0a..f4076e1 100644 --- a/src/pages/RandomPage/RandomPage.tsx +++ b/src/pages/RandomPage/RandomPage.tsx @@ -4,15 +4,11 @@ import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar'; import Tag from '../../elements/Tag/Tag'; import PageTitle from '../../elements/PageTitle/PageTitle'; import VideoContainer from '../../elements/VideoContainer/VideoContainer'; -import {APINode, callAPI} from '../../utils/Api'; +import {APINode} from '../../utils/Api'; import {TagType} from '../../types/VideoTypes'; import {VideoTypes} from '../../types/ApiTypes'; -import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler'; - -interface state { - videos: VideoTypes.VideoUnloadedType[]; - tags: TagType[]; -} +import APIComponent from '../../elements/APIComponent'; +import KeyComponent from '../../elements/KeyComponent'; interface GetRandomMoviesType { Videos: VideoTypes.VideoUnloadedType[]; @@ -22,88 +18,43 @@ interface GetRandomMoviesType { /** * Randompage shuffles random viedeopreviews and provides a shuffle btn */ -class RandomPage extends React.Component<{}, state> { +class RandomPage extends React.Component { readonly LoadNR = 3; - constructor(props: {}) { - super(props); - - this.state = { - videos: [], - tags: [] - }; - - this.keypress = this.keypress.bind(this); - } - - componentDidMount(): void { - addKeyHandler(this.keypress); - - this.loadShuffledvideos(this.LoadNR); - } - - componentWillUnmount(): void { - removeKeyHandler(this.keypress); - } - render(): JSX.Element { return (
- + + ( + + + Visible Tags: + {data.Tags.map((m) => ( + + ))} + - - Visible Tags: - {this.state.tags.map((m) => ( - - ))} - - - {this.state.videos.length !== 0 ? ( - -
- -
-
- ) : ( -
No Data found!
- )} + {data.Videos.length !== 0 ? ( + +
+ +
+
+ ) : ( +
No Data found!
+ )} +
+ )} + node={APINode.Video} + action='getRandomMovies' + params={{Number: this.LoadNR}} + />
); } - - /** - * click handler for shuffle btn - */ - shuffleclick(): void { - this.loadShuffledvideos(this.LoadNR); - } - - /** - * load random videos from backend - * @param nr number of videos to load - */ - loadShuffledvideos(nr: number): void { - callAPI(APINode.Video, {action: 'getRandomMovies', Number: nr}, (result) => { - this.setState({videos: []}); // needed to trigger rerender of main videoview - this.setState({ - videos: result.Videos, - tags: result.Tags - }); - }); - } - - /** - * key event handling - * @param event keyevent - */ - private keypress(event: KeyboardEvent): void { - // bind s to shuffle - if (event.key === 's') { - this.loadShuffledvideos(4); - } - } } export default RandomPage;