|
|
|
@ -1,19 +1,13 @@ |
|
|
|
|
import React, { useState, useEffect } from 'react'; |
|
|
|
|
|
|
|
|
|
import { Query, toApiQuery } from '../types/Query'; |
|
|
|
|
import { Query, toApiQuery, QueryOrdering, TypesIncluded, QueryKeys, OrderKey } from '../types/Query'; |
|
|
|
|
import FilterControl from './FilterControl'; |
|
|
|
|
import * as serverApi from '../api'; |
|
|
|
|
import BrowseWindow, { Item } from './BrowseWindow'; |
|
|
|
|
import { FormControl, FormLabel, FormGroup, FormControlLabel, Checkbox } from '@material-ui/core'; |
|
|
|
|
import { FormControl, FormLabel, FormGroup, FormControlLabel, Checkbox, Select, MenuItem } from '@material-ui/core'; |
|
|
|
|
|
|
|
|
|
const _ = require('lodash'); |
|
|
|
|
|
|
|
|
|
export interface TypesIncluded { |
|
|
|
|
songs: boolean, |
|
|
|
|
artists: boolean, |
|
|
|
|
tags: boolean, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
interface ItemTypeCheckboxesProps { |
|
|
|
|
types: TypesIncluded, |
|
|
|
|
onChange: (types: TypesIncluded) => void; |
|
|
|
@ -22,23 +16,23 @@ interface ItemTypeCheckboxesProps { |
|
|
|
|
function ItemTypeCheckboxes(props: ItemTypeCheckboxesProps) { |
|
|
|
|
const songChange = (v: any) => { |
|
|
|
|
props.onChange({ |
|
|
|
|
songs: v.target.checked, |
|
|
|
|
artists: props.types.artists, |
|
|
|
|
tags: props.types.tags |
|
|
|
|
[QueryKeys.Songs]: v.target.checked, |
|
|
|
|
[QueryKeys.Artists]: props.types[QueryKeys.Artists], |
|
|
|
|
[QueryKeys.Tags]: props.types[QueryKeys.Tags] |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
const artistChange = (v: any) => { |
|
|
|
|
props.onChange({ |
|
|
|
|
songs: props.types.songs, |
|
|
|
|
artists: v.target.checked, |
|
|
|
|
tags: props.types.tags |
|
|
|
|
[QueryKeys.Songs]: props.types[QueryKeys.Songs], |
|
|
|
|
[QueryKeys.Artists]: v.target.checked, |
|
|
|
|
[QueryKeys.Tags]: props.types[QueryKeys.Tags] |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
const tagChange = (v: any) => { |
|
|
|
|
props.onChange({ |
|
|
|
|
songs: props.types.songs, |
|
|
|
|
artists: props.types.artists, |
|
|
|
|
tags: v.target.checked |
|
|
|
|
[QueryKeys.Songs]: props.types[QueryKeys.Songs], |
|
|
|
|
[QueryKeys.Artists]: props.types[QueryKeys.Artists], |
|
|
|
|
[QueryKeys.Tags]: v.target.checked |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -46,26 +40,91 @@ function ItemTypeCheckboxes(props: ItemTypeCheckboxesProps) { |
|
|
|
|
<FormLabel component='legend'>Result types</FormLabel> |
|
|
|
|
<FormGroup> |
|
|
|
|
<FormControlLabel |
|
|
|
|
control={<Checkbox checked={props.types.songs} onChange={songChange} name='Songs' />} |
|
|
|
|
control={<Checkbox checked={props.types[QueryKeys.Songs]} onChange={songChange} name='Songs' />} |
|
|
|
|
label="Songs" |
|
|
|
|
/> |
|
|
|
|
<FormControlLabel |
|
|
|
|
control={<Checkbox checked={props.types.artists} onChange={artistChange} name='Artists' />} |
|
|
|
|
control={<Checkbox checked={props.types[QueryKeys.Artists]} onChange={artistChange} name='Artists' />} |
|
|
|
|
label="Artists" |
|
|
|
|
/> |
|
|
|
|
<FormControlLabel |
|
|
|
|
control={<Checkbox checked={props.types.tags} onChange={tagChange} name='Tags' />} |
|
|
|
|
control={<Checkbox checked={props.types[QueryKeys.Tags]} onChange={tagChange} name='Tags' />} |
|
|
|
|
label="Tags" |
|
|
|
|
/> |
|
|
|
|
</FormGroup> |
|
|
|
|
</FormControl>; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
interface OrderingWidgetProps { |
|
|
|
|
ordering: QueryOrdering, |
|
|
|
|
onChange: (o: QueryOrdering) => void; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function OrderingWidget(props: OrderingWidgetProps) { |
|
|
|
|
const onTypeChange = (e: any) => { |
|
|
|
|
props.onChange({ |
|
|
|
|
[QueryKeys.OrderBy]: { |
|
|
|
|
[QueryKeys.OrderKey]: e.target.value, |
|
|
|
|
}, |
|
|
|
|
[QueryKeys.Ascending]: props.ordering[QueryKeys.Ascending], |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
const onAscendingChange = (e: any) => { |
|
|
|
|
props.onChange({ |
|
|
|
|
[QueryKeys.OrderBy]: props.ordering[QueryKeys.OrderBy], |
|
|
|
|
[QueryKeys.Ascending]: (e.target.value == 'asc'), |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return <FormControl component='fieldset'> |
|
|
|
|
<FormLabel component='legend'>Ordering</FormLabel> |
|
|
|
|
<FormGroup> |
|
|
|
|
<Select |
|
|
|
|
onChange={onTypeChange} |
|
|
|
|
value={props.ordering[QueryKeys.OrderBy][QueryKeys.OrderKey]} |
|
|
|
|
> |
|
|
|
|
<MenuItem value={OrderKey.Name}>Name</MenuItem> |
|
|
|
|
</Select> |
|
|
|
|
<Select |
|
|
|
|
onChange={onAscendingChange} |
|
|
|
|
value={props.ordering[QueryKeys.Ascending] ? 'asc' : 'desc'} |
|
|
|
|
> |
|
|
|
|
<MenuItem value={'asc'}>Ascending</MenuItem> |
|
|
|
|
<MenuItem value={'desc'}>Descending</MenuItem> |
|
|
|
|
</Select> |
|
|
|
|
</FormGroup> |
|
|
|
|
</FormControl>; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function toServerOrdering(o: QueryOrdering | undefined) : serverApi.Ordering { |
|
|
|
|
if(!o) { |
|
|
|
|
return { |
|
|
|
|
orderBy: { |
|
|
|
|
type: serverApi.OrderByType.Name |
|
|
|
|
}, |
|
|
|
|
ascending: true |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const keys = { |
|
|
|
|
[OrderKey.Name]: serverApi.OrderByType.Name, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
orderBy: { |
|
|
|
|
type: keys[o[QueryKeys.OrderBy][QueryKeys.OrderKey]] |
|
|
|
|
}, |
|
|
|
|
ascending: o[QueryKeys.Ascending], |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export interface IProps { |
|
|
|
|
query: Query | undefined, |
|
|
|
|
typesIncluded: TypesIncluded, |
|
|
|
|
typesIncluded: TypesIncluded | undefined, |
|
|
|
|
resultOrder: QueryOrdering | undefined, |
|
|
|
|
onQueryChange: (q: Query) => void, |
|
|
|
|
onTypesChange: (t: TypesIncluded) => void, |
|
|
|
|
onOrderChange: (o: QueryOrdering) => void, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export default function QueryBrowseWindow(props: IProps) { |
|
|
|
@ -74,12 +133,14 @@ export default function QueryBrowseWindow(props: IProps) { |
|
|
|
|
//const [tags, setTags] = useState<serverApi.TagDetails[]>([]);
|
|
|
|
|
|
|
|
|
|
var items: Item[] = []; |
|
|
|
|
props.typesIncluded.songs && items.push(...songs); |
|
|
|
|
props.typesIncluded.artists && items.push(...artists); |
|
|
|
|
props.typesIncluded && props.typesIncluded[QueryKeys.Songs] && items.push(...songs); |
|
|
|
|
props.typesIncluded && props.typesIncluded[QueryKeys.Artists] && items.push(...artists); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
if (!props.query) { return; } |
|
|
|
|
const q = _.cloneDeep(props.query); |
|
|
|
|
const r = _.cloneDeep(props.resultOrder); |
|
|
|
|
const t = _.cloneDeep(props.typesIncluded); |
|
|
|
|
|
|
|
|
|
const request: serverApi.QueryRequest = { |
|
|
|
|
query: toApiQuery(props.query), |
|
|
|
@ -89,10 +150,7 @@ export default function QueryBrowseWindow(props: IProps) { |
|
|
|
|
artistLimit: 5, |
|
|
|
|
tagOffset: 0, |
|
|
|
|
tagLimit: 5, |
|
|
|
|
ordering: { |
|
|
|
|
orderBy: serverApi.OrderBy.Name, |
|
|
|
|
ascending: true, |
|
|
|
|
} |
|
|
|
|
ordering: toServerOrdering(props.resultOrder), |
|
|
|
|
} |
|
|
|
|
const requestOpts = { |
|
|
|
|
method: 'POST', |
|
|
|
@ -102,20 +160,37 @@ export default function QueryBrowseWindow(props: IProps) { |
|
|
|
|
fetch(serverApi.QueryEndpoint, requestOpts) |
|
|
|
|
.then((response: any) => response.json()) |
|
|
|
|
.then((json: any) => { |
|
|
|
|
'songs' in json && _.isEqual(q, props.query) && setSongs(json.songs); |
|
|
|
|
'artists' in json && _.isEqual(q, props.query) && setArtists(json.artists); |
|
|
|
|
const match = _.isEqual(q, props.query) && _.isEqual(r, props.resultOrder) && _.isEqual(t, props.typesIncluded); |
|
|
|
|
'songs' in json && match && setSongs(json.songs); |
|
|
|
|
'artists' in json && match && setArtists(json.artists); |
|
|
|
|
}); |
|
|
|
|
}, [props.query]); |
|
|
|
|
|
|
|
|
|
return <> |
|
|
|
|
<FilterControl |
|
|
|
|
query={props.query} |
|
|
|
|
onChangeQuery={props.onQueryChange} |
|
|
|
|
/> |
|
|
|
|
<FormControl component='fieldset'> |
|
|
|
|
<FormLabel component='legend'>Query</FormLabel> |
|
|
|
|
<FilterControl |
|
|
|
|
query={props.query} |
|
|
|
|
onChangeQuery={props.onQueryChange} |
|
|
|
|
/> |
|
|
|
|
</FormControl> |
|
|
|
|
<ItemTypeCheckboxes |
|
|
|
|
types={props.typesIncluded} |
|
|
|
|
types={props.typesIncluded || { |
|
|
|
|
[QueryKeys.Songs]: true, |
|
|
|
|
[QueryKeys.Artists]: true, |
|
|
|
|
[QueryKeys.Tags]: true, |
|
|
|
|
}} |
|
|
|
|
onChange={props.onTypesChange} |
|
|
|
|
/> |
|
|
|
|
<OrderingWidget |
|
|
|
|
ordering={props.resultOrder || { |
|
|
|
|
[QueryKeys.OrderBy]: { |
|
|
|
|
[QueryKeys.OrderKey]: OrderKey.Name |
|
|
|
|
}, |
|
|
|
|
[QueryKeys.Ascending]: true |
|
|
|
|
}} |
|
|
|
|
onChange={props.onOrderChange} |
|
|
|
|
/> |
|
|
|
|
<BrowseWindow items={items} /> |
|
|
|
|
</> |
|
|
|
|
} |
|
|
|
|