Merge branch 'filterbutton' into 'master'
SortBY feature See merge request lukas/openmediacenter!49
This commit is contained in:
commit
c93d02ca14
@ -31,23 +31,46 @@ func getVideoHandlers() {
|
||||
*/
|
||||
AddHandler("getMovies", VideoNode, func(info *HandlerInfo) []byte {
|
||||
var args struct {
|
||||
Tag int
|
||||
Tag uint32
|
||||
Sort uint8
|
||||
}
|
||||
if err := FillStruct(&args, info.Data); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
date = iota
|
||||
likes = iota
|
||||
random = iota
|
||||
names = iota
|
||||
)
|
||||
|
||||
var SortClause string
|
||||
switch args.Sort {
|
||||
case date:
|
||||
SortClause = "ORDER BY create_date DESC, movie_name"
|
||||
break
|
||||
case likes:
|
||||
SortClause = "ORDER BY likes DESC"
|
||||
break
|
||||
case random:
|
||||
SortClause = "ORDER BY RAND()"
|
||||
break
|
||||
case names:
|
||||
SortClause = "ORDER BY movie_name"
|
||||
break
|
||||
}
|
||||
|
||||
var query string
|
||||
// 1 is the id of the ALL tag
|
||||
if args.Tag != 1 {
|
||||
query = fmt.Sprintf(`SELECT movie_id,movie_name,t.tag_name FROM videos
|
||||
INNER JOIN video_tags vt on videos.movie_id = vt.video_id
|
||||
INNER JOIN tags t on vt.tag_id = t.tag_id
|
||||
WHERE t.tag_id = %d
|
||||
ORDER BY likes DESC, create_date, movie_name`, args.Tag)
|
||||
WHERE t.tag_id = %d %s`, args.Tag, SortClause)
|
||||
} else {
|
||||
query = "SELECT movie_id,movie_name, (SELECT 'All' as tag_name) FROM videos ORDER BY create_date DESC, movie_name"
|
||||
query = fmt.Sprintf("SELECT movie_id,movie_name, (SELECT 'All' as tag_name) FROM videos %s", SortClause)
|
||||
}
|
||||
|
||||
var result struct {
|
||||
|
@ -41,18 +41,24 @@ class DynamicContentContainer<T> extends React.Component<Props<T>, state<T>> {
|
||||
|
||||
componentDidUpdate(prevProps: Props<T>): void {
|
||||
// when source props change force update!
|
||||
if (prevProps.data.length !== this.props.data.length) {
|
||||
this.clean();
|
||||
if (
|
||||
// diff the two arrays
|
||||
this.props.data
|
||||
.filter((x) => !prevProps.data.includes(x))
|
||||
.concat(prevProps.data.filter((x) => !this.props.data.includes(x))).length !== 0
|
||||
) {
|
||||
this.clean((): void => {
|
||||
this.loadPreviewBlock(this.InitialLoadNR);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clear all elements rendered...
|
||||
*/
|
||||
clean(): void {
|
||||
clean(callback: () => void): void {
|
||||
this.loadindex = 0;
|
||||
this.setState({loadeditems: []});
|
||||
this.setState({loadeditems: []}, callback);
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
|
@ -7,3 +7,61 @@
|
||||
float: right;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.sortbyLabel {
|
||||
color: grey;
|
||||
margin-right: 5px;
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
/* Style The Dropdown Button */
|
||||
.dropbtn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* The container <div> - needed to position the dropdown content */
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Dropdown Content (Hidden by Default) */
|
||||
.dropdownContent {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #f9f9f9;
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Links inside the dropdown */
|
||||
.dropdownContent span {
|
||||
color: black;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Change color of dropdown links on hover */
|
||||
.dropdownContent span:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Show the dropdown menu on hover */
|
||||
.dropdown:hover .dropdownContent {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Change the background color of the dropdown button when the dropdown content is shown */
|
||||
.dropdown:hover .dropbtn {
|
||||
background-color: #3574fe;
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {HomePage} from './HomePage';
|
||||
import {HomePage, SortBy} from './HomePage';
|
||||
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
||||
import {SearchHandling} from './SearchHandling';
|
||||
import exp from "constants";
|
||||
import {DefaultTags} from "../../types/GeneralTypes";
|
||||
|
||||
describe('<HomePage/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
@ -83,6 +85,20 @@ describe('<HomePage/>', function () {
|
||||
|
||||
testBtn(tags.first());
|
||||
});
|
||||
|
||||
it('test sortby type change', function () {
|
||||
const wrapper = shallow(<HomePage/>);
|
||||
|
||||
// expect those default values
|
||||
expect(wrapper.state().sortby).toBe('Date Added');
|
||||
expect(wrapper.instance().sortState).toBe(SortBy.date);
|
||||
expect(wrapper.instance().tagState).toBe(DefaultTags.all);
|
||||
|
||||
wrapper.instance().onDropDownItemClick(SortBy.name, 'namesort');
|
||||
|
||||
expect(wrapper.state().sortby).toBe('namesort');
|
||||
expect(wrapper.instance().sortState).toBe(SortBy.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('<SearchHandling/>', () => {
|
||||
|
@ -11,6 +11,16 @@ import {RouteComponentProps} from 'react-router';
|
||||
import SearchHandling from './SearchHandling';
|
||||
import {VideoTypes} from '../../types/ApiTypes';
|
||||
import {DefaultTags} from '../../types/GeneralTypes';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faSortDown} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
export enum SortBy {
|
||||
date,
|
||||
likes,
|
||||
random,
|
||||
name
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps {}
|
||||
|
||||
@ -19,6 +29,7 @@ interface state {
|
||||
subtitle: string;
|
||||
data: VideoTypes.VideoUnloadedType[];
|
||||
selectionnr: number;
|
||||
sortby: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,13 +53,17 @@ export class HomePage extends React.Component<Props, state> {
|
||||
},
|
||||
subtitle: 'All Videos',
|
||||
data: [],
|
||||
selectionnr: 0
|
||||
selectionnr: 0,
|
||||
sortby: 'Date Added'
|
||||
};
|
||||
}
|
||||
|
||||
sortState = SortBy.date;
|
||||
tagState = DefaultTags.all;
|
||||
|
||||
componentDidMount(): void {
|
||||
// initial get of all videos
|
||||
this.fetchVideoData(DefaultTags.all.TagId);
|
||||
this.fetchVideoData();
|
||||
this.fetchStartData();
|
||||
}
|
||||
|
||||
@ -58,14 +73,11 @@ export class HomePage extends React.Component<Props, state> {
|
||||
*
|
||||
* @param tag tag to fetch videos
|
||||
*/
|
||||
fetchVideoData(tag: number): void {
|
||||
fetchVideoData(): void {
|
||||
callAPI(
|
||||
APINode.Video,
|
||||
{action: 'getMovies', Tag: tag},
|
||||
{action: 'getMovies', Tag: this.tagState.TagId, Sort: this.sortState},
|
||||
(result: {Videos: VideoTypes.VideoUnloadedType[]; TagName: string}) => {
|
||||
this.setState({
|
||||
data: []
|
||||
});
|
||||
this.setState({
|
||||
data: result.Videos,
|
||||
selectionnr: result.Videos.length
|
||||
@ -135,32 +147,52 @@ export class HomePage extends React.Component<Props, state> {
|
||||
<Tag
|
||||
tagInfo={{TagName: 'All', TagId: DefaultTags.all.TagId}}
|
||||
onclick={(): void => {
|
||||
this.fetchVideoData(DefaultTags.all.TagId);
|
||||
this.tagState = DefaultTags.all;
|
||||
this.fetchVideoData();
|
||||
this.setState({subtitle: 'All Videos'});
|
||||
}}
|
||||
/>
|
||||
<Tag
|
||||
tagInfo={{TagName: 'Full Hd', TagId: DefaultTags.fullhd.TagId}}
|
||||
onclick={(): void => {
|
||||
this.fetchVideoData(DefaultTags.fullhd.TagId);
|
||||
this.tagState = DefaultTags.fullhd;
|
||||
this.fetchVideoData();
|
||||
this.setState({subtitle: 'Full Hd Videos'});
|
||||
}}
|
||||
/>
|
||||
<Tag
|
||||
tagInfo={{TagName: 'Low Quality', TagId: DefaultTags.lowq.TagId}}
|
||||
onclick={(): void => {
|
||||
this.fetchVideoData(DefaultTags.lowq.TagId);
|
||||
this.tagState = DefaultTags.lowq;
|
||||
this.fetchVideoData();
|
||||
this.setState({subtitle: 'Low Quality Videos'});
|
||||
}}
|
||||
/>
|
||||
<Tag
|
||||
tagInfo={{TagName: 'HD', TagId: DefaultTags.hd.TagId}}
|
||||
onclick={(): void => {
|
||||
this.fetchVideoData(DefaultTags.hd.TagId);
|
||||
this.tagState = DefaultTags.hd;
|
||||
this.fetchVideoData();
|
||||
this.setState({subtitle: 'HD Videos'});
|
||||
}}
|
||||
/>
|
||||
</SideBar>
|
||||
<div>
|
||||
<span className={style.sortbyLabel}>Sort By: </span>
|
||||
<div className={style.dropdown}>
|
||||
<span className={style.dropbtn}>
|
||||
<span>{this.state.sortby}</span>
|
||||
<FontAwesomeIcon style={{marginLeft: 3, paddingBottom: 3}} icon={faSortDown} size='1x' />
|
||||
</span>
|
||||
<div className={style.dropdownContent}>
|
||||
<span onClick={(): void => this.onDropDownItemClick(SortBy.date, 'Date Added')}>Date Added</span>
|
||||
<span onClick={(): void => this.onDropDownItemClick(SortBy.likes, 'Most likes')}>Most likes</span>
|
||||
<span onClick={(): void => this.onDropDownItemClick(SortBy.random, 'Random')}>Random</span>
|
||||
<span onClick={(): void => this.onDropDownItemClick(SortBy.name, 'Name')}>Name</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<VideoContainer data={this.state.data} />
|
||||
<div className={style.rightinfo} />
|
||||
</Route>
|
||||
@ -168,6 +200,17 @@ export class HomePage extends React.Component<Props, state> {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* click handler for sortby dropdown item click
|
||||
* @param type type of sort action
|
||||
* @param name new header title
|
||||
*/
|
||||
onDropDownItemClick(type: SortBy, name: string): void {
|
||||
this.sortState = type;
|
||||
this.setState({sortby: name});
|
||||
this.fetchVideoData();
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(HomePage);
|
||||
|
Loading…
Reference in New Issue
Block a user