commit
8f4157add4
14 changed files with 739 additions and 81 deletions
@ -0,0 +1,32 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { WindowType } from '../windows/Windows'; |
||||||
|
import { Menu, MenuItem } from '@material-ui/core'; |
||||||
|
|
||||||
|
export interface NewTabProps { |
||||||
|
windowType: WindowType, |
||||||
|
} |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
anchorEl: null | HTMLElement, |
||||||
|
onClose: () => void, |
||||||
|
onCreateTab: (q: NewTabProps) => void, |
||||||
|
} |
||||||
|
|
||||||
|
export default function AddTabMenu(props: IProps) { |
||||||
|
return <Menu |
||||||
|
anchorEl={props.anchorEl} |
||||||
|
keepMounted |
||||||
|
open={Boolean(props.anchorEl)} |
||||||
|
onClose={props.onClose} |
||||||
|
> |
||||||
|
<MenuItem disabled={true}>New Tab</MenuItem> |
||||||
|
<MenuItem |
||||||
|
onClick={() => { |
||||||
|
props.onClose(); |
||||||
|
props.onCreateTab({ |
||||||
|
windowType: WindowType.Query, |
||||||
|
}) |
||||||
|
}} |
||||||
|
>{WindowType.Query}</MenuItem> |
||||||
|
</Menu> |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
import React, { useEffect } from 'react'; |
||||||
|
import { Box, Typography } from '@material-ui/core'; |
||||||
|
import AlbumIcon from '@material-ui/icons/Album'; |
||||||
|
import * as serverApi from '../../api'; |
||||||
|
import { WindowState } from './Windows'; |
||||||
|
|
||||||
|
export interface AlbumMetadata { |
||||||
|
name: string, |
||||||
|
} |
||||||
|
|
||||||
|
export interface AlbumWindowState extends WindowState { |
||||||
|
albumId: number, |
||||||
|
metadata: AlbumMetadata | null, |
||||||
|
} |
||||||
|
|
||||||
|
export enum AlbumWindowStateActions { |
||||||
|
SetMetadata = "SetMetadata", |
||||||
|
} |
||||||
|
|
||||||
|
export function AlbumWindowReducer(state: AlbumWindowState, action: any) { |
||||||
|
switch (action.type) { |
||||||
|
case AlbumWindowStateActions.SetMetadata: |
||||||
|
return { ...state, metadata: action.value } |
||||||
|
default: |
||||||
|
throw new Error("Unimplemented AlbumWindow state update.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
state: AlbumWindowState, |
||||||
|
dispatch: (action: any) => void, |
||||||
|
mainDispatch: (action: any) => void, |
||||||
|
} |
||||||
|
|
||||||
|
export async function getAlbumMetadata(id: number) { |
||||||
|
const query = { |
||||||
|
prop: serverApi.QueryElemProperty.albumId, |
||||||
|
propOperand: id, |
||||||
|
propOperator: serverApi.QueryFilterOp.Eq, |
||||||
|
}; |
||||||
|
|
||||||
|
var q: serverApi.QueryRequest = { |
||||||
|
query: query, |
||||||
|
offsetsLimits: { |
||||||
|
albumOffset: 0, |
||||||
|
albumLimit: 1, |
||||||
|
}, |
||||||
|
ordering: { |
||||||
|
orderBy: { |
||||||
|
type: serverApi.OrderByType.Name, |
||||||
|
}, |
||||||
|
ascending: true, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const requestOpts = { |
||||||
|
method: 'POST', |
||||||
|
headers: { 'Content-Type': 'application/json' }, |
||||||
|
body: JSON.stringify(q), |
||||||
|
}; |
||||||
|
|
||||||
|
return (async () => { |
||||||
|
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts) |
||||||
|
let json: any = await response.json(); |
||||||
|
let album = json.albums[0]; |
||||||
|
return { |
||||||
|
name: album.name |
||||||
|
} |
||||||
|
})(); |
||||||
|
} |
||||||
|
|
||||||
|
export default function AlbumWindow(props: IProps) { |
||||||
|
let metadata = props.state.metadata; |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getAlbumMetadata(props.state.albumId) |
||||||
|
.then((m: AlbumMetadata) => { |
||||||
|
console.log("metadata", m); |
||||||
|
props.dispatch({ |
||||||
|
type: AlbumWindowStateActions.SetMetadata, |
||||||
|
value: m |
||||||
|
}); |
||||||
|
}) |
||||||
|
}, [props.state.metadata?.name]); |
||||||
|
|
||||||
|
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
mt={4} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
<AlbumIcon style={{ fontSize: 80 }}/> |
||||||
|
</Box> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
{metadata && <Typography variant="h4">{metadata.name}</Typography>} |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
import React, { useEffect } from 'react'; |
||||||
|
import { Box, Typography } from '@material-ui/core'; |
||||||
|
import PersonIcon from '@material-ui/icons/Person'; |
||||||
|
import * as serverApi from '../../api'; |
||||||
|
import { WindowState } from './Windows'; |
||||||
|
|
||||||
|
export interface ArtistMetadata { |
||||||
|
name: string, |
||||||
|
} |
||||||
|
|
||||||
|
export interface ArtistWindowState extends WindowState { |
||||||
|
artistId: number, |
||||||
|
metadata: ArtistMetadata | null, |
||||||
|
} |
||||||
|
|
||||||
|
export enum ArtistWindowStateActions { |
||||||
|
SetMetadata = "SetMetadata", |
||||||
|
} |
||||||
|
|
||||||
|
export function ArtistWindowReducer(state: ArtistWindowState, action: any) { |
||||||
|
switch (action.type) { |
||||||
|
case ArtistWindowStateActions.SetMetadata: |
||||||
|
return { ...state, metadata: action.value } |
||||||
|
default: |
||||||
|
throw new Error("Unimplemented ArtistWindow state update.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
state: ArtistWindowState, |
||||||
|
dispatch: (action: any) => void, |
||||||
|
mainDispatch: (action: any) => void, |
||||||
|
} |
||||||
|
|
||||||
|
export async function getArtistMetadata(id: number) { |
||||||
|
const query = { |
||||||
|
prop: serverApi.QueryElemProperty.artistId, |
||||||
|
propOperand: id, |
||||||
|
propOperator: serverApi.QueryFilterOp.Eq, |
||||||
|
}; |
||||||
|
|
||||||
|
var q: serverApi.QueryRequest = { |
||||||
|
query: query, |
||||||
|
offsetsLimits: { |
||||||
|
artistOffset: 0, |
||||||
|
artistLimit: 1, |
||||||
|
}, |
||||||
|
ordering: { |
||||||
|
orderBy: { |
||||||
|
type: serverApi.OrderByType.Name, |
||||||
|
}, |
||||||
|
ascending: true, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const requestOpts = { |
||||||
|
method: 'POST', |
||||||
|
headers: { 'Content-Type': 'application/json' }, |
||||||
|
body: JSON.stringify(q), |
||||||
|
}; |
||||||
|
|
||||||
|
return (async () => { |
||||||
|
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts) |
||||||
|
let json: any = await response.json(); |
||||||
|
let artist = json.artists[0]; |
||||||
|
return { |
||||||
|
name: artist.name |
||||||
|
} |
||||||
|
})(); |
||||||
|
} |
||||||
|
|
||||||
|
export default function ArtistWindow(props: IProps) { |
||||||
|
let metadata = props.state.metadata; |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getArtistMetadata(props.state.artistId) |
||||||
|
.then((m: ArtistMetadata) => { |
||||||
|
console.log("metadata", m); |
||||||
|
props.dispatch({ |
||||||
|
type: ArtistWindowStateActions.SetMetadata, |
||||||
|
value: m |
||||||
|
}); |
||||||
|
}) |
||||||
|
}, [props.state.metadata?.name]); |
||||||
|
|
||||||
|
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
mt={4} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
<PersonIcon style={{ fontSize: 80 }}/> |
||||||
|
</Box> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
{metadata && <Typography variant="h4">{metadata.name}</Typography>} |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
import React, { useEffect } from 'react'; |
||||||
|
import { Box, Typography } from '@material-ui/core'; |
||||||
|
import AudiotrackIcon from '@material-ui/icons/Audiotrack'; |
||||||
|
import * as serverApi from '../../api'; |
||||||
|
import { WindowState } from './Windows'; |
||||||
|
|
||||||
|
export interface SongMetadata { |
||||||
|
title: string, |
||||||
|
} |
||||||
|
|
||||||
|
export interface SongWindowState extends WindowState { |
||||||
|
songId: number, |
||||||
|
metadata: SongMetadata | null, |
||||||
|
} |
||||||
|
|
||||||
|
export enum SongWindowStateActions { |
||||||
|
SetMetadata = "SetMetadata", |
||||||
|
} |
||||||
|
|
||||||
|
export function SongWindowReducer(state: SongWindowState, action: any) { |
||||||
|
switch (action.type) { |
||||||
|
case SongWindowStateActions.SetMetadata: |
||||||
|
return { ...state, metadata: action.value } |
||||||
|
default: |
||||||
|
throw new Error("Unimplemented SongWindow state update.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
state: SongWindowState, |
||||||
|
dispatch: (action: any) => void, |
||||||
|
mainDispatch: (action: any) => void, |
||||||
|
} |
||||||
|
|
||||||
|
export async function getSongMetadata(id: number) { |
||||||
|
const query = { |
||||||
|
prop: serverApi.QueryElemProperty.songId, |
||||||
|
propOperand: id, |
||||||
|
propOperator: serverApi.QueryFilterOp.Eq, |
||||||
|
}; |
||||||
|
|
||||||
|
var q: serverApi.QueryRequest = { |
||||||
|
query: query, |
||||||
|
offsetsLimits: { |
||||||
|
songOffset: 0, |
||||||
|
songLimit: 1, |
||||||
|
}, |
||||||
|
ordering: { |
||||||
|
orderBy: { |
||||||
|
type: serverApi.OrderByType.Name, |
||||||
|
}, |
||||||
|
ascending: true, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const requestOpts = { |
||||||
|
method: 'POST', |
||||||
|
headers: { 'Content-Type': 'application/json' }, |
||||||
|
body: JSON.stringify(q), |
||||||
|
}; |
||||||
|
|
||||||
|
return (async () => { |
||||||
|
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts) |
||||||
|
let json: any = await response.json(); |
||||||
|
let song = json.songs[0]; |
||||||
|
return { |
||||||
|
title: song.title |
||||||
|
} |
||||||
|
})(); |
||||||
|
} |
||||||
|
|
||||||
|
export default function SongWindow(props: IProps) { |
||||||
|
let metadata = props.state.metadata; |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getSongMetadata(props.state.songId) |
||||||
|
.then((m: SongMetadata) => { |
||||||
|
console.log("metadata", m); |
||||||
|
props.dispatch({ |
||||||
|
type: SongWindowStateActions.SetMetadata, |
||||||
|
value: m |
||||||
|
}); |
||||||
|
}) |
||||||
|
}, [props.state.metadata?.title]); |
||||||
|
|
||||||
|
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
mt={4} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
<AudiotrackIcon style={{ fontSize: 80 }}/> |
||||||
|
</Box> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
{metadata && <Typography variant="h4">{metadata.title}</Typography>} |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
import React, { useEffect } from 'react'; |
||||||
|
import { Box, Typography } from '@material-ui/core'; |
||||||
|
import LocalOfferIcon from '@material-ui/icons/LocalOffer'; |
||||||
|
import * as serverApi from '../../api'; |
||||||
|
import { WindowState } from './Windows'; |
||||||
|
|
||||||
|
export interface TagMetadata { |
||||||
|
name: string, |
||||||
|
} |
||||||
|
|
||||||
|
export interface TagWindowState extends WindowState { |
||||||
|
tagId: number, |
||||||
|
metadata: TagMetadata | null, |
||||||
|
} |
||||||
|
|
||||||
|
export enum TagWindowStateActions { |
||||||
|
SetMetadata = "SetMetadata", |
||||||
|
} |
||||||
|
|
||||||
|
export function TagWindowReducer(state: TagWindowState, action: any) { |
||||||
|
switch (action.type) { |
||||||
|
case TagWindowStateActions.SetMetadata: |
||||||
|
return { ...state, metadata: action.value } |
||||||
|
default: |
||||||
|
throw new Error("Unimplemented TagWindow state update.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
state: TagWindowState, |
||||||
|
dispatch: (action: any) => void, |
||||||
|
mainDispatch: (action: any) => void, |
||||||
|
} |
||||||
|
|
||||||
|
export async function getTagMetadata(id: number) { |
||||||
|
const query = { |
||||||
|
prop: serverApi.QueryElemProperty.tagId, |
||||||
|
propOperand: id, |
||||||
|
propOperator: serverApi.QueryFilterOp.Eq, |
||||||
|
}; |
||||||
|
|
||||||
|
var q: serverApi.QueryRequest = { |
||||||
|
query: query, |
||||||
|
offsetsLimits: { |
||||||
|
tagOffset: 0, |
||||||
|
tagLimit: 1, |
||||||
|
}, |
||||||
|
ordering: { |
||||||
|
orderBy: { |
||||||
|
type: serverApi.OrderByType.Name, |
||||||
|
}, |
||||||
|
ascending: true, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const requestOpts = { |
||||||
|
method: 'POST', |
||||||
|
headers: { 'Content-Type': 'application/json' }, |
||||||
|
body: JSON.stringify(q), |
||||||
|
}; |
||||||
|
|
||||||
|
return (async () => { |
||||||
|
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts) |
||||||
|
let json: any = await response.json(); |
||||||
|
let tag = json.tags[0]; |
||||||
|
return { |
||||||
|
name: tag.name |
||||||
|
} |
||||||
|
})(); |
||||||
|
} |
||||||
|
|
||||||
|
export default function TagWindow(props: IProps) { |
||||||
|
let metadata = props.state.metadata; |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getTagMetadata(props.state.tagId) |
||||||
|
.then((m: TagMetadata) => { |
||||||
|
console.log("metadata", m); |
||||||
|
props.dispatch({ |
||||||
|
type: TagWindowStateActions.SetMetadata, |
||||||
|
value: m |
||||||
|
}); |
||||||
|
}) |
||||||
|
}, [props.state.metadata?.name]); |
||||||
|
|
||||||
|
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
mt={4} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
<LocalOfferIcon style={{ fontSize: 80 }}/> |
||||||
|
</Box> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
width="80%" |
||||||
|
> |
||||||
|
{metadata && <Typography variant="h4">{metadata.name}</Typography>} |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { QueryWindowReducer } from "./QueryWindow"; |
||||||
|
import { ArtistWindowReducer } from "./ArtistWindow"; |
||||||
|
import SearchIcon from '@material-ui/icons/Search'; |
||||||
|
import PersonIcon from '@material-ui/icons/Person'; |
||||||
|
import AlbumIcon from '@material-ui/icons/Album'; |
||||||
|
import LocalOfferIcon from '@material-ui/icons/LocalOffer'; |
||||||
|
import AudiotrackIcon from '@material-ui/icons/Audiotrack'; |
||||||
|
import { SongWindowReducer } from './SongWindow'; |
||||||
|
import { AlbumWindowReducer } from './AlbumWindow'; |
||||||
|
import { TagWindowReducer } from './TagWindow'; |
||||||
|
|
||||||
|
export enum WindowType { |
||||||
|
Query = "Query", |
||||||
|
Artist = "Artist", |
||||||
|
Album = "Album", |
||||||
|
Tag = "Tag", |
||||||
|
Song = "Song", |
||||||
|
} |
||||||
|
|
||||||
|
export interface WindowState { |
||||||
|
tabLabel: string, |
||||||
|
} |
||||||
|
|
||||||
|
export const newWindowReducer = { |
||||||
|
[WindowType.Query]: QueryWindowReducer, |
||||||
|
[WindowType.Artist]: ArtistWindowReducer, |
||||||
|
[WindowType.Album]: AlbumWindowReducer, |
||||||
|
[WindowType.Song]: SongWindowReducer, |
||||||
|
[WindowType.Tag]: TagWindowReducer, |
||||||
|
} |
||||||
|
|
||||||
|
export const newWindowState = { |
||||||
|
[WindowType.Query]: () => { |
||||||
|
return { |
||||||
|
tabLabel: <><SearchIcon/>Query</>, |
||||||
|
editingQuery: false, |
||||||
|
query: null, |
||||||
|
resultsForQuery: null, |
||||||
|
}; |
||||||
|
}, |
||||||
|
[WindowType.Artist]: () => { |
||||||
|
return { |
||||||
|
tabLabel: <><PersonIcon/>Artist</>, |
||||||
|
artistId: 1, |
||||||
|
metadata: null, |
||||||
|
} |
||||||
|
}, |
||||||
|
[WindowType.Album]: () => { |
||||||
|
return { |
||||||
|
tabLabel: <><AlbumIcon/>Album</>, |
||||||
|
albumId: 1, |
||||||
|
metadata: null, |
||||||
|
} |
||||||
|
}, |
||||||
|
[WindowType.Song]: () => { |
||||||
|
return { |
||||||
|
tabLabel: <><AudiotrackIcon/>Song</>, |
||||||
|
songId: 1, |
||||||
|
metadata: null, |
||||||
|
} |
||||||
|
}, |
||||||
|
[WindowType.Tag]: () => { |
||||||
|
return { |
||||||
|
tabLabel: <><LocalOfferIcon/>Tag</>, |
||||||
|
tagId: 1, |
||||||
|
metadata: null, |
||||||
|
} |
||||||
|
}, |
||||||
|
} |
Loading…
Reference in new issue