From 57a7a9a827f5e363c29d4d0e4539852323f2730b Mon Sep 17 00:00:00 2001 From: lukas Date: Fri, 16 Apr 2021 18:20:39 +0200 Subject: [PATCH] abstract dynamic video tile load within new dynamicloader class to allow to load other elements dynamically. --- .../DynamicContentContainer.module.css} | 2 +- .../DynamicContentContainer.test.js | 30 +++++++ .../DynamicContentContainer.tsx | 87 ++++++++++++++++++ .../VideoContainer/VideoContainer.test.js | 20 ----- .../VideoContainer/VideoContainer.tsx | 90 +++---------------- src/pages/CategoryPage/TagView.tsx | 24 ++--- src/pages/TVShowPage/TVShowPage.tsx | 2 +- 7 files changed, 144 insertions(+), 111 deletions(-) rename src/elements/{VideoContainer/VideoContainer.module.css => DynamicContentContainer/DynamicContentContainer.module.css} (96%) create mode 100644 src/elements/DynamicContentContainer/DynamicContentContainer.test.js create mode 100644 src/elements/DynamicContentContainer/DynamicContentContainer.tsx diff --git a/src/elements/VideoContainer/VideoContainer.module.css b/src/elements/DynamicContentContainer/DynamicContentContainer.module.css similarity index 96% rename from src/elements/VideoContainer/VideoContainer.module.css rename to src/elements/DynamicContentContainer/DynamicContentContainer.module.css index 972756a..ee66207 100644 --- a/src/elements/VideoContainer/VideoContainer.module.css +++ b/src/elements/DynamicContentContainer/DynamicContentContainer.module.css @@ -1,4 +1,4 @@ .maincontent { float: left; width: 70%; -} \ No newline at end of file +} diff --git a/src/elements/DynamicContentContainer/DynamicContentContainer.test.js b/src/elements/DynamicContentContainer/DynamicContentContainer.test.js new file mode 100644 index 0000000..176fd4f --- /dev/null +++ b/src/elements/DynamicContentContainer/DynamicContentContainer.test.js @@ -0,0 +1,30 @@ +import {shallow} from 'enzyme'; +import React from 'react'; +import DynamicContentContainer from './DynamicContentContainer'; + +describe('', function () { + it('renders without crashing ', function () { + const wrapper = shallow( (<>)}/>); + wrapper.unmount(); + }); + + it('inserts tiles correctly if enough available', () => { + const wrapper = shallow( ()}/>); + expect(wrapper.find('a')).toHaveLength(16); + }); + + it('inserts tiles correctly if not enough available', () => { + const wrapper = shallow( ()}/>); + expect(wrapper.find('a')).toHaveLength(4); + }); + + it('no items available', () => { + const wrapper = shallow( ()}/>); + expect(wrapper.find('a')).toHaveLength(0); + expect(wrapper.find('.maincontent').text()).toBe('no items to show!'); + }); +}); diff --git a/src/elements/DynamicContentContainer/DynamicContentContainer.tsx b/src/elements/DynamicContentContainer/DynamicContentContainer.tsx new file mode 100644 index 0000000..c49300b --- /dev/null +++ b/src/elements/DynamicContentContainer/DynamicContentContainer.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import style from './DynamicContentContainer.module.css'; + +interface Props { + renderElement: (elem: T) => JSX.Element; + data: T[]; + initialLoadNr?: number; +} + +interface state { + loadeditems: T[]; + selectionnr: number; +} + +/** + * A videocontainer storing lots of Preview elements + * includes scroll handling and loading of preview infos + */ +class DynamicContentContainer extends React.Component, state> { + // stores current index of loaded elements + loadindex: number = 0; + + constructor(props: Props) { + super(props); + + this.state = { + loadeditems: [], + selectionnr: 0 + }; + } + + componentDidMount(): void { + document.addEventListener('scroll', this.trackScrolling); + + this.loadPreviewBlock(this.props.initialLoadNr ? this.props.initialLoadNr : 16); + } + + render(): JSX.Element { + return ( +
+ {this.state.loadeditems.map((elem) => { + return this.props.renderElement(elem); + })} + {/*todo css for no items to show*/} + {this.state.loadeditems.length === 0 ? 'no items to show!' : null} + {this.props.children} +
+ ); + } + + componentWillUnmount(): void { + // unbind scroll listener when unmounting component + document.removeEventListener('scroll', this.trackScrolling); + } + + /** + * load previews to the container + * @param nr number of previews to load + */ + loadPreviewBlock(nr: number): void { + let ret = []; + for (let i = 0; i < nr; i++) { + // only add if not end + if (this.props.data.length > this.loadindex + i) { + ret.push(this.props.data[this.loadindex + i]); + } + } + + this.setState({ + loadeditems: [...this.state.loadeditems, ...ret] + }); + + this.loadindex += nr; + } + + /** + * scroll event handler -> load new previews if on bottom + */ + trackScrolling = (): void => { + // comparison if current scroll position is on bottom --> 200 is bottom offset to trigger load + if (window.innerHeight + document.documentElement.scrollTop + 200 >= document.documentElement.offsetHeight) { + this.loadPreviewBlock(8); + } + }; +} + +export default DynamicContentContainer; diff --git a/src/elements/VideoContainer/VideoContainer.test.js b/src/elements/VideoContainer/VideoContainer.test.js index 3a76654..18f5187 100644 --- a/src/elements/VideoContainer/VideoContainer.test.js +++ b/src/elements/VideoContainer/VideoContainer.test.js @@ -7,24 +7,4 @@ describe('', function () { const wrapper = shallow(); wrapper.unmount(); }); - - it('inserts tiles correctly if enough available', () => { - const wrapper = shallow(); - expect(wrapper.find('Preview')).toHaveLength(16); - }); - - it('inserts tiles correctly if not enough available', () => { - const wrapper = shallow(); - expect(wrapper.find('Preview')).toHaveLength(4); - }); - - it('no items available', () => { - const wrapper = shallow(); - expect(wrapper.find('Preview')).toHaveLength(0); - expect(wrapper.find('.maincontent').text()).toBe('no items to show!'); - }); }); diff --git a/src/elements/VideoContainer/VideoContainer.tsx b/src/elements/VideoContainer/VideoContainer.tsx index 01fb205..efce56c 100644 --- a/src/elements/VideoContainer/VideoContainer.tsx +++ b/src/elements/VideoContainer/VideoContainer.tsx @@ -1,89 +1,21 @@ import React from 'react'; import Preview from '../Preview/Preview'; -import style from './VideoContainer.module.css'; import {VideoTypes} from '../../types/ApiTypes'; +import DynamicContentContainer from '../DynamicContentContainer/DynamicContentContainer'; interface Props { data: VideoTypes.VideoUnloadedType[]; + children?: JSX.Element; } -interface state { - loadeditems: VideoTypes.VideoUnloadedType[]; - selectionnr: number; -} - -/** - * A videocontainer storing lots of Preview elements - * includes scroll handling and loading of preview infos - */ -class VideoContainer extends React.Component { - // stores current index of loaded elements - loadindex: number = 0; - - constructor(props: Props) { - super(props); - - this.state = { - loadeditems: [], - selectionnr: 0 - }; - } - - componentDidMount(): void { - document.addEventListener('scroll', this.trackScrolling); - - this.loadPreviewBlock(16); - } - - render(): JSX.Element { - return ( -
- {this.state.loadeditems.map((elem) => ( - - ))} - {/*todo css for no items to show*/} - {this.state.loadeditems.length === 0 ? 'no items to show!' : null} - {this.props.children} -
- ); - } - - componentWillUnmount(): void { - this.setState({}); - // unbind scroll listener when unmounting component - document.removeEventListener('scroll', this.trackScrolling); - } - - /** - * load previews to the container - * @param nr number of previews to load - */ - loadPreviewBlock(nr: number): void { - console.log('loadpreviewblock called ...'); - let ret = []; - for (let i = 0; i < nr; i++) { - // only add if not end - if (this.props.data.length > this.loadindex + i) { - ret.push(this.props.data[this.loadindex + i]); - } - } - - this.setState({ - loadeditems: [...this.state.loadeditems, ...ret] - }); - - this.loadindex += nr; - } - - /** - * scroll event handler -> load new previews if on bottom - */ - trackScrolling = (): void => { - // comparison if current scroll position is on bottom --> 200 is bottom offset to trigger load - if (window.innerHeight + document.documentElement.scrollTop + 200 >= document.documentElement.offsetHeight) { - this.loadPreviewBlock(8); - } - }; -} +const VideoContainer = (props: Props): JSX.Element => { + return ( + } + data={props.data}> + {props.children} + + ); +}; export default VideoContainer; diff --git a/src/pages/CategoryPage/TagView.tsx b/src/pages/CategoryPage/TagView.tsx index 616b698..cf99f4d 100644 --- a/src/pages/CategoryPage/TagView.tsx +++ b/src/pages/CategoryPage/TagView.tsx @@ -1,6 +1,5 @@ import {TagType} from '../../types/VideoTypes'; import React from 'react'; -import videocontainerstyle from '../../elements/VideoContainer/VideoContainer.module.css'; import {Link} from 'react-router-dom'; import {TagPreview} from '../../elements/Preview/Preview'; import {APINode, callAPI} from '../../utils/Api'; @@ -9,6 +8,7 @@ import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar'; import Tag from '../../elements/Tag/Tag'; import {DefaultTags} from '../../types/GeneralTypes'; import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup'; +import DynamicContentContainer from '../../elements/DynamicContentContainer/DynamicContentContainer'; interface TagViewState { loadedtags: TagType[]; @@ -53,15 +53,19 @@ class TagView extends React.Component { Add a new Tag! -
- {this.state.loadedtags - ? this.state.loadedtags.map((m) => ( - - - - )) - : 'loading'} -
+ {this.state.loadedtags.length !== 0 ? ( + ( + + + + )} + initialLoadNr={20} + /> + ) : ( + 'loading' + )} {this.handlePopups()} ); diff --git a/src/pages/TVShowPage/TVShowPage.tsx b/src/pages/TVShowPage/TVShowPage.tsx index 49c2f9e..7d6edd3 100644 --- a/src/pages/TVShowPage/TVShowPage.tsx +++ b/src/pages/TVShowPage/TVShowPage.tsx @@ -2,7 +2,7 @@ import React from 'react'; class TVShowPage extends React.Component { render(): JSX.Element { - return <>; + return <>TvShowPage; } }