abstract dynamic video tile load within new dynamicloader class to allow to load other elements dynamically.
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
			
		||||
.maincontent {
 | 
			
		||||
    float: left;
 | 
			
		||||
    width: 70%;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
import {shallow} from 'enzyme';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import DynamicContentContainer from './DynamicContentContainer';
 | 
			
		||||
 | 
			
		||||
describe('<DynamicContentContainer/>', function () {
 | 
			
		||||
    it('renders without crashing ', function () {
 | 
			
		||||
        const wrapper = shallow(<DynamicContentContainer data={[]} renderElement={(el) => (<></>)}/>);
 | 
			
		||||
        wrapper.unmount();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('inserts tiles correctly if enough available', () => {
 | 
			
		||||
        const wrapper = shallow(<DynamicContentContainer data={[
 | 
			
		||||
            {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}
 | 
			
		||||
        ]} renderElement={(el) => (<a/>)}/>);
 | 
			
		||||
        expect(wrapper.find('a')).toHaveLength(16);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('inserts tiles correctly if not enough available', () => {
 | 
			
		||||
        const wrapper = shallow(<DynamicContentContainer data={[
 | 
			
		||||
            {}, {}, {}, {}
 | 
			
		||||
        ]} renderElement={(el) => (<a/>)}/>);
 | 
			
		||||
        expect(wrapper.find('a')).toHaveLength(4);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('no items available', () => {
 | 
			
		||||
        const wrapper = shallow(<DynamicContentContainer data={[]} renderElement={(el) => (<a/>)}/>);
 | 
			
		||||
        expect(wrapper.find('a')).toHaveLength(0);
 | 
			
		||||
        expect(wrapper.find('.maincontent').text()).toBe('no items to show!');
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -0,0 +1,87 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import style from './DynamicContentContainer.module.css';
 | 
			
		||||
 | 
			
		||||
interface Props<T> {
 | 
			
		||||
    renderElement: (elem: T) => JSX.Element;
 | 
			
		||||
    data: T[];
 | 
			
		||||
    initialLoadNr?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface state<T> {
 | 
			
		||||
    loadeditems: T[];
 | 
			
		||||
    selectionnr: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A videocontainer storing lots of Preview elements
 | 
			
		||||
 * includes scroll handling and loading of preview infos
 | 
			
		||||
 */
 | 
			
		||||
class DynamicContentContainer<T> extends React.Component<Props<T>, state<T>> {
 | 
			
		||||
    // stores current index of loaded elements
 | 
			
		||||
    loadindex: number = 0;
 | 
			
		||||
 | 
			
		||||
    constructor(props: Props<T>) {
 | 
			
		||||
        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 (
 | 
			
		||||
            <div className={style.maincontent}>
 | 
			
		||||
                {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}
 | 
			
		||||
            </div>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
@@ -7,24 +7,4 @@ describe('<VideoContainer/>', function () {
 | 
			
		||||
        const wrapper = shallow(<VideoContainer data={[]}/>);
 | 
			
		||||
        wrapper.unmount();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('inserts tiles correctly if enough available', () => {
 | 
			
		||||
        const wrapper = shallow(<VideoContainer data={[
 | 
			
		||||
            {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}
 | 
			
		||||
        ]}/>);
 | 
			
		||||
        expect(wrapper.find('Preview')).toHaveLength(16);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('inserts tiles correctly if not enough available', () => {
 | 
			
		||||
        const wrapper = shallow(<VideoContainer data={[
 | 
			
		||||
            {}, {}, {}, {}
 | 
			
		||||
        ]}/>);
 | 
			
		||||
        expect(wrapper.find('Preview')).toHaveLength(4);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('no items available', () => {
 | 
			
		||||
        const wrapper = shallow(<VideoContainer data={[]}/>);
 | 
			
		||||
        expect(wrapper.find('Preview')).toHaveLength(0);
 | 
			
		||||
        expect(wrapper.find('.maincontent').text()).toBe('no items to show!');
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Props, 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(16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render(): JSX.Element {
 | 
			
		||||
        return (
 | 
			
		||||
            <div className={style.maincontent}>
 | 
			
		||||
                {this.state.loadeditems.map((elem) => (
 | 
			
		||||
                    <Preview key={elem.MovieId} name={elem.MovieName} movieId={elem.MovieId} />
 | 
			
		||||
                ))}
 | 
			
		||||
                {/*todo css for no items to show*/}
 | 
			
		||||
                {this.state.loadeditems.length === 0 ? 'no items to show!' : null}
 | 
			
		||||
                {this.props.children}
 | 
			
		||||
            </div>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 (
 | 
			
		||||
        <DynamicContentContainer
 | 
			
		||||
            renderElement={(el): JSX.Element => <Preview key={el.MovieId} name={el.MovieName} movieId={el.MovieId} />}
 | 
			
		||||
            data={props.data}>
 | 
			
		||||
            {props.children}
 | 
			
		||||
        </DynamicContentContainer>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default VideoContainer;
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Props, TagViewState> {
 | 
			
		||||
                        Add a new Tag!
 | 
			
		||||
                    </button>
 | 
			
		||||
                </SideBar>
 | 
			
		||||
                <div className={videocontainerstyle.maincontent}>
 | 
			
		||||
                    {this.state.loadedtags
 | 
			
		||||
                        ? this.state.loadedtags.map((m) => (
 | 
			
		||||
                              <Link to={'/categories/' + m.TagId} key={m.TagId}>
 | 
			
		||||
                                  <TagPreview name={m.TagName} />
 | 
			
		||||
                              </Link>
 | 
			
		||||
                          ))
 | 
			
		||||
                        : 'loading'}
 | 
			
		||||
                </div>
 | 
			
		||||
                {this.state.loadedtags.length !== 0 ? (
 | 
			
		||||
                    <DynamicContentContainer
 | 
			
		||||
                        data={this.state.loadedtags}
 | 
			
		||||
                        renderElement={(m): JSX.Element => (
 | 
			
		||||
                            <Link to={'/categories/' + m.TagId} key={m.TagId}>
 | 
			
		||||
                                <TagPreview name={m.TagName} />
 | 
			
		||||
                            </Link>
 | 
			
		||||
                        )}
 | 
			
		||||
                        initialLoadNr={20}
 | 
			
		||||
                    />
 | 
			
		||||
                ) : (
 | 
			
		||||
                    'loading'
 | 
			
		||||
                )}
 | 
			
		||||
                {this.handlePopups()}
 | 
			
		||||
            </>
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import React from 'react';
 | 
			
		||||
 | 
			
		||||
class TVShowPage extends React.Component {
 | 
			
		||||
    render(): JSX.Element {
 | 
			
		||||
        return <></>;
 | 
			
		||||
        return <>TvShowPage</>;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user