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 {
|
AddHandler("getMovies", VideoNode, func(info *HandlerInfo) []byte {
|
||||||
var args struct {
|
var args struct {
|
||||||
Tag int
|
Tag uint32
|
||||||
|
Sort uint8
|
||||||
}
|
}
|
||||||
if err := FillStruct(&args, info.Data); err != nil {
|
if err := FillStruct(&args, info.Data); err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
return nil
|
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
|
var query string
|
||||||
// 1 is the id of the ALL tag
|
// 1 is the id of the ALL tag
|
||||||
if args.Tag != 1 {
|
if args.Tag != 1 {
|
||||||
query = fmt.Sprintf(`SELECT movie_id,movie_name,t.tag_name FROM videos
|
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 video_tags vt on videos.movie_id = vt.video_id
|
||||||
INNER JOIN tags t on vt.tag_id = t.tag_id
|
INNER JOIN tags t on vt.tag_id = t.tag_id
|
||||||
WHERE t.tag_id = %d
|
WHERE t.tag_id = %d %s`, args.Tag, SortClause)
|
||||||
ORDER BY likes DESC, create_date, movie_name`, args.Tag)
|
|
||||||
} else {
|
} 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 {
|
var result struct {
|
||||||
|
@ -41,18 +41,24 @@ class DynamicContentContainer<T> extends React.Component<Props<T>, state<T>> {
|
|||||||
|
|
||||||
componentDidUpdate(prevProps: Props<T>): void {
|
componentDidUpdate(prevProps: Props<T>): void {
|
||||||
// when source props change force update!
|
// when source props change force update!
|
||||||
if (prevProps.data.length !== this.props.data.length) {
|
if (
|
||||||
this.clean();
|
// diff the two arrays
|
||||||
this.loadPreviewBlock(this.InitialLoadNR);
|
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...
|
* clear all elements rendered...
|
||||||
*/
|
*/
|
||||||
clean(): void {
|
clean(callback: () => void): void {
|
||||||
this.loadindex = 0;
|
this.loadindex = 0;
|
||||||
this.setState({loadeditems: []});
|
this.setState({loadeditems: []}, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
|
@ -7,3 +7,61 @@
|
|||||||
float: right;
|
float: right;
|
||||||
margin-top: 25px;
|
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 {shallow} from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {HomePage} from './HomePage';
|
import {HomePage, SortBy} from './HomePage';
|
||||||
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
||||||
import {SearchHandling} from './SearchHandling';
|
import {SearchHandling} from './SearchHandling';
|
||||||
|
import exp from "constants";
|
||||||
|
import {DefaultTags} from "../../types/GeneralTypes";
|
||||||
|
|
||||||
describe('<HomePage/>', function () {
|
describe('<HomePage/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
@ -83,6 +85,20 @@ describe('<HomePage/>', function () {
|
|||||||
|
|
||||||
testBtn(tags.first());
|
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/>', () => {
|
describe('<SearchHandling/>', () => {
|
||||||
|
@ -11,6 +11,16 @@ import {RouteComponentProps} from 'react-router';
|
|||||||
import SearchHandling from './SearchHandling';
|
import SearchHandling from './SearchHandling';
|
||||||
import {VideoTypes} from '../../types/ApiTypes';
|
import {VideoTypes} from '../../types/ApiTypes';
|
||||||
import {DefaultTags} from '../../types/GeneralTypes';
|
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 {}
|
interface Props extends RouteComponentProps {}
|
||||||
|
|
||||||
@ -19,6 +29,7 @@ interface state {
|
|||||||
subtitle: string;
|
subtitle: string;
|
||||||
data: VideoTypes.VideoUnloadedType[];
|
data: VideoTypes.VideoUnloadedType[];
|
||||||
selectionnr: number;
|
selectionnr: number;
|
||||||
|
sortby: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,13 +53,17 @@ export class HomePage extends React.Component<Props, state> {
|
|||||||
},
|
},
|
||||||
subtitle: 'All Videos',
|
subtitle: 'All Videos',
|
||||||
data: [],
|
data: [],
|
||||||
selectionnr: 0
|
selectionnr: 0,
|
||||||
|
sortby: 'Date Added'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sortState = SortBy.date;
|
||||||
|
tagState = DefaultTags.all;
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
// initial get of all videos
|
// initial get of all videos
|
||||||
this.fetchVideoData(DefaultTags.all.TagId);
|
this.fetchVideoData();
|
||||||
this.fetchStartData();
|
this.fetchStartData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,14 +73,11 @@ export class HomePage extends React.Component<Props, state> {
|
|||||||
*
|
*
|
||||||
* @param tag tag to fetch videos
|
* @param tag tag to fetch videos
|
||||||
*/
|
*/
|
||||||
fetchVideoData(tag: number): void {
|
fetchVideoData(): void {
|
||||||
callAPI(
|
callAPI(
|
||||||
APINode.Video,
|
APINode.Video,
|
||||||
{action: 'getMovies', Tag: tag},
|
{action: 'getMovies', Tag: this.tagState.TagId, Sort: this.sortState},
|
||||||
(result: {Videos: VideoTypes.VideoUnloadedType[]; TagName: string}) => {
|
(result: {Videos: VideoTypes.VideoUnloadedType[]; TagName: string}) => {
|
||||||
this.setState({
|
|
||||||
data: []
|
|
||||||
});
|
|
||||||
this.setState({
|
this.setState({
|
||||||
data: result.Videos,
|
data: result.Videos,
|
||||||
selectionnr: result.Videos.length
|
selectionnr: result.Videos.length
|
||||||
@ -135,32 +147,52 @@ export class HomePage extends React.Component<Props, state> {
|
|||||||
<Tag
|
<Tag
|
||||||
tagInfo={{TagName: 'All', TagId: DefaultTags.all.TagId}}
|
tagInfo={{TagName: 'All', TagId: DefaultTags.all.TagId}}
|
||||||
onclick={(): void => {
|
onclick={(): void => {
|
||||||
this.fetchVideoData(DefaultTags.all.TagId);
|
this.tagState = DefaultTags.all;
|
||||||
|
this.fetchVideoData();
|
||||||
this.setState({subtitle: 'All Videos'});
|
this.setState({subtitle: 'All Videos'});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tag
|
<Tag
|
||||||
tagInfo={{TagName: 'Full Hd', TagId: DefaultTags.fullhd.TagId}}
|
tagInfo={{TagName: 'Full Hd', TagId: DefaultTags.fullhd.TagId}}
|
||||||
onclick={(): void => {
|
onclick={(): void => {
|
||||||
this.fetchVideoData(DefaultTags.fullhd.TagId);
|
this.tagState = DefaultTags.fullhd;
|
||||||
|
this.fetchVideoData();
|
||||||
this.setState({subtitle: 'Full Hd Videos'});
|
this.setState({subtitle: 'Full Hd Videos'});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tag
|
<Tag
|
||||||
tagInfo={{TagName: 'Low Quality', TagId: DefaultTags.lowq.TagId}}
|
tagInfo={{TagName: 'Low Quality', TagId: DefaultTags.lowq.TagId}}
|
||||||
onclick={(): void => {
|
onclick={(): void => {
|
||||||
this.fetchVideoData(DefaultTags.lowq.TagId);
|
this.tagState = DefaultTags.lowq;
|
||||||
|
this.fetchVideoData();
|
||||||
this.setState({subtitle: 'Low Quality Videos'});
|
this.setState({subtitle: 'Low Quality Videos'});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tag
|
<Tag
|
||||||
tagInfo={{TagName: 'HD', TagId: DefaultTags.hd.TagId}}
|
tagInfo={{TagName: 'HD', TagId: DefaultTags.hd.TagId}}
|
||||||
onclick={(): void => {
|
onclick={(): void => {
|
||||||
this.fetchVideoData(DefaultTags.hd.TagId);
|
this.tagState = DefaultTags.hd;
|
||||||
|
this.fetchVideoData();
|
||||||
this.setState({subtitle: 'HD Videos'});
|
this.setState({subtitle: 'HD Videos'});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SideBar>
|
</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} />
|
<VideoContainer data={this.state.data} />
|
||||||
<div className={style.rightinfo} />
|
<div className={style.rightinfo} />
|
||||||
</Route>
|
</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);
|
export default withRouter(HomePage);
|
||||||
|
Loading…
Reference in New Issue
Block a user