Fix routing.

pull/28/head
Sander Vocke 5 years ago
parent 679a00b77d
commit c249c7495d
  1. 48
      client/src/components/MainWindow.tsx
  2. 40
      client/src/components/appbar/AddTabMenu.tsx
  3. 86
      client/src/components/appbar/AppBar.tsx
  4. 14
      client/src/components/tables/ResultsTable.tsx
  5. 35
      client/src/components/windows/Windows.tsx
  6. 27
      client/src/components/windows/album/AlbumWindow.tsx
  7. 22
      client/src/components/windows/artist/ArtistWindow.tsx
  8. 20
      client/src/components/windows/manage_tags/ManageTagsWindow.tsx
  9. 17
      client/src/components/windows/query/QueryWindow.tsx
  10. 20
      client/src/components/windows/song/SongWindow.tsx
  11. 27
      client/src/components/windows/tag/TagWindow.tsx

@ -1,10 +1,9 @@
import React, { useReducer, Reducer } from 'react';
import React, { useReducer, Reducer, useContext } from 'react';
import { ThemeProvider, CssBaseline, createMuiTheme } from '@material-ui/core';
import { grey } from '@material-ui/core/colors';
import AppBar from './appbar/AppBar';
import AppBar, { AppBarTab } from './appbar/AppBar';
import QueryWindow, { QueryWindowReducer } from './windows/query/QueryWindow';
import { NewTabProps } from './appbar/AddTabMenu';
import { newWindowState, newWindowReducer, WindowType, Window } from './windows/Windows';
import { newWindowState, newWindowReducer, WindowType } from './windows/Windows';
import ArtistWindow from './windows/artist/ArtistWindow';
import AlbumWindow from './windows/album/AlbumWindow';
import TagWindow from './windows/tag/TagWindow';
@ -22,54 +21,37 @@ const darkTheme = createMuiTheme({
},
});
function WindowContent(props: {
type: WindowType,
}) {
const { id } = useParams();
return <Window type={props.type} stateOverride={id ? { "id": id } : {}} />
}
export default function MainWindow(props: any) {
const [windowState, windowDispatch] = useReducer(
QueryWindowReducer,
{
editingQuery: false,
query: null,
resultsForQuery: null,
},
);
return <ThemeProvider theme={darkTheme}>
<CssBaseline />
<BrowserRouter>
<AppBar
tabLabels={[]}
selectedTab={0}
setSelectedTab={(t: number) => { }}
onCloseTab={(t: number) => { }}
onAddTab={(w: NewTabProps) => { }}
/>
<Switch>
<Route exact path="/">
<Redirect to={"/query"} />
</Route>
<Route path="/query">
<WindowContent type={WindowType.Query} />
<AppBar selectedTab={AppBarTab.Query} />
<QueryWindow/>
</Route>
<Route path="/artist/:id">
<WindowContent type={WindowType.Artist} />
<AppBar selectedTab={null} />
<ArtistWindow/>
</Route>
<Route path="/tag/:id">
<WindowContent type={WindowType.Tag} />
<AppBar selectedTab={null} />
<TagWindow/>
</Route>
<Route path="/album/:id">
<WindowContent type={WindowType.Album} />
<AppBar selectedTab={null} />
<AlbumWindow/>
</Route>
<Route path="/song/:id">
<WindowContent type={WindowType.Song} />
<AppBar selectedTab={null} />
<SongWindow/>
</Route>
<Route path="/tags">
<WindowContent type={WindowType.ManageTags} />
<AppBar selectedTab={AppBarTab.Tags} />
<ManageTagsWindow/>
</Route>
</Switch>
</BrowserRouter>

@ -1,40 +0,0 @@
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>
<MenuItem
onClick={() => {
props.onClose();
props.onCreateTab({
windowType: WindowType.ManageTags,
})
}}
>Manage Tags</MenuItem>
</Menu>
}

@ -1,60 +1,31 @@
import React from 'react';
import { AppBar as MuiAppBar, Box, Tab as MuiTab, Tabs, IconButton } from '@material-ui/core';
import { AppBar as MuiAppBar, Box, Tab as MuiTab, Tabs, IconButton, Typography } from '@material-ui/core';
import CloseIcon from '@material-ui/icons/Close';
import AddIcon from '@material-ui/icons/Add';
import AddTabMenu, { NewTabProps } from './AddTabMenu';
import { Link } from 'react-router-dom';
export interface IProps {
tabLabels: string[],
selectedTab: number,
setSelectedTab: (n: number) => void,
onCloseTab: (idx: number) => void,
onAddTab: (w: NewTabProps) => void,
import SearchIcon from '@material-ui/icons/Search';
import LocalOfferIcon from '@material-ui/icons/LocalOffer';
import { Link, useHistory } from 'react-router-dom';
import { WindowType } from '../windows/Windows';
export enum AppBarTab {
Query = 0,
Tags,
}
export interface TabProps {
onClose: () => void,
export const appBarTabProps: Record<any, any> = {
[AppBarTab.Query]: {
label: <Box display="flex"><SearchIcon/><Typography variant="button">Query</Typography></Box>,
path: "/query",
},
[AppBarTab.Tags]: {
label: <Box display="flex"><LocalOfferIcon/><Typography variant="button">Tags</Typography></Box>,
path: "/tags",
},
}
export function Tab(props: any) {
const { onClose, label, ...restProps } = props;
const labelElem = <Box
display="flex"
alignItems="center"
justifyContent="center"
>
{label}
<Box ml={1}>
<IconButton
size="small"
color="inherit"
onClick={onClose}
>
<CloseIcon />
</IconButton>
</Box>
</Box>;
return <MuiTab
label={labelElem}
{...restProps}
/>
}
export default function AppBar(props: IProps) {
const [addMenuAnchorEl, setAddMenuAnchorEl] = React.useState<null | HTMLElement>(null);
const onOpenAddMenu = (event: any) => {
setAddMenuAnchorEl(event.currentTarget);
};
const onCloseAddMenu = () => {
setAddMenuAnchorEl(null);
};
const onAddTab = (w: NewTabProps) => {
props.onAddTab(w);
};
export default function AppBar(props: {
selectedTab: AppBarTab | null
}) {
const history = useHistory();
return <>
<MuiAppBar position="static" style={{ background: 'grey' }}>
@ -66,23 +37,16 @@ export default function AppBar(props: IProps) {
</Link>
<Tabs
value={props.selectedTab}
onChange={(e: any, v: number) => props.setSelectedTab(v)}
onChange={(e: any, val: AppBarTab) => history.push(appBarTabProps[val].path)}
variant="scrollable"
scrollButtons="auto"
>
{props.tabLabels.map((l: string, idx: number) => <Tab
label={l}
{Object.keys(appBarTabProps).map((tab: any, idx: number) => <MuiTab
label={appBarTabProps[tab].label}
value={idx}
onClose={() => props.onCloseTab(idx)}
/>)}
</Tabs>
<IconButton color="inherit" onClick={onOpenAddMenu}><AddIcon /></IconButton>
</Box>
</MuiAppBar>
<AddTabMenu
anchorEl={addMenuAnchorEl}
onClose={onCloseAddMenu}
onCreateTab={onAddTab}
/>
</>
}

@ -1,13 +1,7 @@
import React from 'react';
import { TableContainer, Table, TableHead, TableRow, TableCell, Paper, makeStyles, TableBody, Chip, Box, Button } from '@material-ui/core';
import stringifyList from '../../lib/stringifyList';
import { newWindowReducer, WindowType } from '../windows/Windows';
import PersonIcon from '@material-ui/icons/Person';
import AlbumIcon from '@material-ui/icons/Album';
import AudiotrackIcon from '@material-ui/icons/Audiotrack';
import LocalOfferIcon from '@material-ui/icons/LocalOffer';
import { songGetters } from '../../lib/songGetters';
import { Redirect, useHistory } from 'react-router';
import { useHistory } from 'react-router';
export interface SongGetters {
getTitle: (song: any) => string,
@ -20,12 +14,10 @@ export interface SongGetters {
getTagIds: (song: any) => number[][], // Each tag is represented as a series of ids.
}
export interface IProps {
export default function SongTable(props: {
songs: any[],
songGetters: SongGetters,
}
export default function SongTable(props: IProps) {
}) {
const history = useHistory();
const classes = makeStyles({

@ -24,41 +24,6 @@ export enum WindowType {
export interface WindowState { }
export function Window(props: {
type: WindowType,
stateOverride: any,
}) {
const [state, dispatch] = useReducer(
newWindowReducer[props.type],
newWindowState[props.type](),
);
const _state: any = {
...state,
...props.stateOverride
};
if (props.type === WindowType.Query) {
return <QueryWindow state={_state} dispatch={dispatch}/>
}
if (props.type === WindowType.Artist) {
return <ArtistWindow state={_state} dispatch={dispatch}/>
}
if (props.type === WindowType.Album) {
return <AlbumWindow state={_state} dispatch={dispatch}/>
}
if (props.type === WindowType.ManageTags) {
return <ManageTagsWindow state={_state} dispatch={dispatch}/>
}
if (props.type === WindowType.Song) {
return <SongWindow state={_state} dispatch={dispatch}/>
}
if (props.type === WindowType.Tag) {
return <TagWindow state={_state} dispatch={dispatch}/>
}
throw new Error("Unsupported window type")
}
export const newWindowReducer = {
[WindowType.Query]: QueryWindowReducer,
[WindowType.Artist]: ArtistWindowReducer,

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useReducer } from 'react';
import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import AlbumIcon from '@material-ui/icons/Album';
import * as serverApi from '../../../api';
@ -10,6 +10,8 @@ import SongTable, { SongGetters } from '../../tables/ResultsTable';
import { saveAlbumChanges } from '../../../lib/saveChanges';
import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { queryAlbums, querySongs } from '../../../lib/backend/queries';
import { songGetters } from '../../../lib/songGetters';
import { useParams } from 'react-router';
var _ = require('lodash');
export type AlbumMetadata = serverApi.AlbumDetails;
@ -45,11 +47,6 @@ export function AlbumWindowReducer(state: AlbumWindowState, action: any) {
}
}
export interface IProps {
state: AlbumWindowState,
dispatch: (action: any) => void,
}
export async function getAlbumMetadata(id: number) {
return (await queryAlbums({
query: {
@ -62,7 +59,23 @@ export async function getAlbumMetadata(id: number) {
}))[0];
}
export default function AlbumWindow(props: IProps) {
export default function AlbumWindow(props: {}) {
const { id } = useParams();
const [state, dispatch] = useReducer(AlbumWindowReducer, {
id: id,
metadata: null,
pendingChanges: null,
songGetters: songGetters,
songsOnAlbum: null,
});
return <AlbumWindowControlled state={state} dispatch={dispatch} />
}
export function AlbumWindowControlled(props: {
state: AlbumWindowState,
dispatch: (action: any) => void,
}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useReducer } from 'react';
import { Box, Typography, IconButton, Button, CircularProgress } from '@material-ui/core';
import PersonIcon from '@material-ui/icons/Person';
import * as serverApi from '../../../api';
@ -10,6 +10,8 @@ import SongTable, { SongGetters } from '../../tables/ResultsTable';
import { saveArtistChanges } from '../../../lib/saveChanges';
import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { queryArtists, querySongs } from '../../../lib/backend/queries';
import { songGetters } from '../../../lib/songGetters';
import { useParams } from 'react-router';
var _ = require('lodash');
export type ArtistMetadata = serverApi.ArtistDetails;
@ -62,7 +64,23 @@ export async function getArtistMetadata(id: number) {
}))[0];
}
export default function ArtistWindow(props: IProps) {
export default function ArtistWindow(props: {}) {
const { id } = useParams();
const [state, dispatch] = useReducer(ArtistWindowReducer, {
id: id,
metadata: null,
pendingChanges: null,
songGetters: songGetters,
songsByArtist: null,
});
return <ArtistWindowControlled state={state} dispatch={dispatch} />
}
export function ArtistWindowControlled(props: {
state: ArtistWindowState,
dispatch: (action: any) => void,
}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;

@ -1,4 +1,4 @@
import React, { useEffect, useState, ReactFragment } from 'react';
import React, { useEffect, useState, ReactFragment, useReducer } from 'react';
import { WindowState, newWindowReducer, WindowType } from '../Windows';
import { Box, Typography, Chip, IconButton, useTheme, Button } from '@material-ui/core';
import LoyaltyIcon from '@material-ui/icons/Loyalty';
@ -127,7 +127,7 @@ export function SingleTag(props: {
const hasChildren = 'children' in tag && tag.children.length > 0;
const [menuPos, setMenuPos] = React.useState<null | number[]>(null);
const [expanded, setExpanded] = useState<boolean>(false);
const [expanded, setExpanded] = useState<boolean>(true);
const theme = useTheme();
const history = useHistory();
@ -340,7 +340,17 @@ function applyTagsChanges(tags: Record<string, any>, changes: TagChange[]) {
return retval;
}
export default function ManageTagsWindow(props: {
export default function ManageTagsWindow(props: {}) {
const [state, dispatch] = useReducer(ManageTagsWindowReducer, {
fetchedTags: null,
alert: null,
pendingChanges: [],
});
return <ManageTagsWindowControlled state={state} dispatch={dispatch} />
}
export function ManageTagsWindowControlled(props: {
state: ManageTagsWindowState,
dispatch: (action: any) => void,
}) {
@ -368,9 +378,9 @@ export default function ManageTagsWindow(props: {
})();
}, [props.state.fetchedTags]);
const tagsWithChanges = annotateTagsWithChanges(props.state.fetchedTags || {}, props.state.pendingChanges)
const tagsWithChanges = annotateTagsWithChanges(props.state.fetchedTags || {}, props.state.pendingChanges || [])
const changedTags = organiseTags(
applyTagsChanges(props.state.fetchedTags || {}, props.state.pendingChanges),
applyTagsChanges(props.state.fetchedTags || {}, props.state.pendingChanges || []),
null);
const tags = organiseTags(tagsWithChanges, null);

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useReducer } from 'react';
import { createMuiTheme, Box, LinearProgress } from '@material-ui/core';
import { QueryElem, toApiQuery, QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import QueryBuilder from '../../querybuilder/QueryBuilder';
@ -98,13 +98,20 @@ export function QueryWindowReducer(state: QueryWindowState, action: any) {
throw new Error("Unimplemented QueryWindow state update.")
}
}
export default function QueryWindow(props: {}) {
const [state, dispatch] = useReducer(QueryWindowReducer, {
editingQuery: false,
query: null,
resultsForQuery: null,
});
export interface IProps {
state: QueryWindowState,
dispatch: (action: any) => void,
return <QueryWindowControlled state={state} dispatch={dispatch} />
}
export default function QueryWindow(props: IProps) {
export function QueryWindowControlled(props: {
state: QueryWindowState,
dispatch: (action: any) => void,
}) {
let query = props.state.query;
let editing = props.state.editingQuery;
let resultsFor = props.state.resultsForQuery;

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useReducer } from 'react';
import { Box, Typography, IconButton, Button, CircularProgress } from '@material-ui/core';
import AudiotrackIcon from '@material-ui/icons/Audiotrack';
import PersonIcon from '@material-ui/icons/Person';
@ -13,6 +13,8 @@ import SubmitChangesButton from '../../common/SubmitChangesButton';
import { saveSongChanges } from '../../../lib/saveChanges';
import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { querySongs } from '../../../lib/backend/queries';
import { songGetters } from '../../../lib/songGetters';
import { useParams } from 'react-router';
export type SongMetadata = serverApi.SongDetails;
export type SongMetadataChanges = serverApi.ModifySongRequest;
@ -59,7 +61,21 @@ export async function getSongMetadata(id: number) {
}))[0];
}
export default function SongWindow(props: IProps) {
export default function SongWindow(props: {}) {
const { id } = useParams();
const [state, dispatch] = useReducer(SongWindowReducer, {
id: id,
metadata: null,
pendingChanges: null,
});
return <SongWindowControlled state={state} dispatch={dispatch} />
}
export function SongWindowControlled(props: {
state: SongWindowState,
dispatch: (action: any) => void,
}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useReducer } from 'react';
import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import LocalOfferIcon from '@material-ui/icons/LocalOffer';
import * as serverApi from '../../../api';
@ -10,6 +10,8 @@ import SongTable, { SongGetters } from '../../tables/ResultsTable';
import { saveTagChanges } from '../../../lib/saveChanges';
import { queryTags, querySongs } from '../../../lib/backend/queries';
import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { songGetters } from '../../../lib/songGetters';
import { useParams } from 'react-router';
var _ = require('lodash');
export interface FullTagMetadata extends serverApi.TagDetails {
@ -50,11 +52,6 @@ export function TagWindowReducer(state: TagWindowState, action: any) {
}
}
export interface IProps {
state: TagWindowState,
dispatch: (action: any) => void,
}
export async function getTagMetadata(id: number) {
var tag = (await queryTags({
query: {
@ -79,7 +76,23 @@ export async function getTagMetadata(id: number) {
return tag;
}
export default function TagWindow(props: IProps) {
export default function TagWindow(props: {}) {
const { id } = useParams();
const [state, dispatch] = useReducer(TagWindowReducer,{
id: id,
metadata: null,
pendingChanges: null,
songGetters: songGetters,
songsWithTag: null,
});
return <TagWindowControlled state={state} dispatch={dispatch} />
}
export function TagWindowControlled(props: {
state: TagWindowState,
dispatch: (action: any) => void,
}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;

Loading…
Cancel
Save