diff --git a/client/src/components/MainWindow.tsx b/client/src/components/MainWindow.tsx
index b028dfb..2839106 100644
--- a/client/src/components/MainWindow.tsx
+++ b/client/src/components/MainWindow.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
-}
-
export default function MainWindow(props: any) {
- const [windowState, windowDispatch] = useReducer(
- QueryWindowReducer,
- {
- editingQuery: false,
- query: null,
- resultsForQuery: null,
- },
- );
-
return
- { }}
- onCloseTab={(t: number) => { }}
- onAddTab={(w: NewTabProps) => { }}
- />
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
+
diff --git a/client/src/components/appbar/AddTabMenu.tsx b/client/src/components/appbar/AddTabMenu.tsx
deleted file mode 100644
index a9e0755..0000000
--- a/client/src/components/appbar/AddTabMenu.tsx
+++ /dev/null
@@ -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
-}
\ No newline at end of file
diff --git a/client/src/components/appbar/AppBar.tsx b/client/src/components/appbar/AppBar.tsx
index bf0f252..28809f2 100644
--- a/client/src/components/appbar/AppBar.tsx
+++ b/client/src/components/appbar/AppBar.tsx
@@ -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,
-}
-
-export interface TabProps {
- onClose: () => 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 function Tab(props: any) {
- const { onClose, label, ...restProps } = props;
-
- const labelElem =
- {label}
-
-
-
-
-
- ;
-
- return
+export const appBarTabProps: Record = {
+ [AppBarTab.Query]: {
+ label: Query,
+ path: "/query",
+ },
+ [AppBarTab.Tags]: {
+ label: Tags,
+ path: "/tags",
+ },
}
-export default function AppBar(props: IProps) {
- const [addMenuAnchorEl, setAddMenuAnchorEl] = React.useState(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 <>
@@ -66,23 +37,16 @@ export default function AppBar(props: IProps) {
props.setSelectedTab(v)}
+ onChange={(e: any, val: AppBarTab) => history.push(appBarTabProps[val].path)}
variant="scrollable"
scrollButtons="auto"
>
- {props.tabLabels.map((l: string, idx: number) => props.onCloseTab(idx)}
/>)}
-
-
>
}
\ No newline at end of file
diff --git a/client/src/components/tables/ResultsTable.tsx b/client/src/components/tables/ResultsTable.tsx
index 5952b95..5fee3d1 100644
--- a/client/src/components/tables/ResultsTable.tsx
+++ b/client/src/components/tables/ResultsTable.tsx
@@ -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({
diff --git a/client/src/components/windows/Windows.tsx b/client/src/components/windows/Windows.tsx
index f58966c..edd6768 100644
--- a/client/src/components/windows/Windows.tsx
+++ b/client/src/components/windows/Windows.tsx
@@ -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
- }
- if (props.type === WindowType.Artist) {
- return
- }
- if (props.type === WindowType.Album) {
- return
- }
- if (props.type === WindowType.ManageTags) {
- return
- }
- if (props.type === WindowType.Song) {
- return
- }
- if (props.type === WindowType.Tag) {
- return
- }
-
- throw new Error("Unsupported window type")
-}
-
export const newWindowReducer = {
[WindowType.Query]: QueryWindowReducer,
[WindowType.Artist]: ArtistWindowReducer,
diff --git a/client/src/components/windows/album/AlbumWindow.tsx b/client/src/components/windows/album/AlbumWindow.tsx
index 0d10dee..07daa38 100644
--- a/client/src/components/windows/album/AlbumWindow.tsx
+++ b/client/src/components/windows/album/AlbumWindow.tsx
@@ -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
+}
+
+export function AlbumWindowControlled(props: {
+ state: AlbumWindowState,
+ dispatch: (action: any) => void,
+}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;
@@ -94,7 +107,7 @@ export default function AlbumWindow(props: IProps) {
props.dispatch({
type: AlbumWindowStateActions.SetSongs,
value: songs,
- });
+ });
})();
}, [props.state.songsOnAlbum]);
diff --git a/client/src/components/windows/artist/ArtistWindow.tsx b/client/src/components/windows/artist/ArtistWindow.tsx
index b426ec3..ce28da5 100644
--- a/client/src/components/windows/artist/ArtistWindow.tsx
+++ b/client/src/components/windows/artist/ArtistWindow.tsx
@@ -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
+}
+
+export function ArtistWindowControlled(props: {
+ state: ArtistWindowState,
+ dispatch: (action: any) => void,
+}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;
@@ -94,7 +112,7 @@ export default function ArtistWindow(props: IProps) {
props.dispatch({
type: ArtistWindowStateActions.SetSongs,
value: songs,
- });
+ });
})();
}, [props.state.songsByArtist]);
diff --git a/client/src/components/windows/manage_tags/ManageTagsWindow.tsx b/client/src/components/windows/manage_tags/ManageTagsWindow.tsx
index 748931b..285dfdd 100644
--- a/client/src/components/windows/manage_tags/ManageTagsWindow.tsx
+++ b/client/src/components/windows/manage_tags/ManageTagsWindow.tsx
@@ -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);
- const [expanded, setExpanded] = useState(false);
+ const [expanded, setExpanded] = useState(true);
const theme = useTheme();
const history = useHistory();
@@ -340,7 +340,17 @@ function applyTagsChanges(tags: Record, 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
+}
+
+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);
diff --git a/client/src/components/windows/query/QueryWindow.tsx b/client/src/components/windows/query/QueryWindow.tsx
index 74f6bd9..9561883 100644
--- a/client/src/components/windows/query/QueryWindow.tsx
+++ b/client/src/components/windows/query/QueryWindow.tsx
@@ -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';
@@ -47,7 +47,7 @@ async function getArtistNames(filter: string) {
limit: -1,
});
- return [...(new Set([...(artists.map((a:any) => a.name))]))];
+ return [...(new Set([...(artists.map((a: any) => a.name))]))];
}
async function getAlbumNames(filter: string) {
@@ -61,7 +61,7 @@ async function getAlbumNames(filter: string) {
limit: -1,
});
- return [...(new Set([...(albums.map((a:any) => a.name))]))];
+ return [...(new Set([...(albums.map((a: any) => a.name))]))];
}
async function getSongTitles(filter: string) {
@@ -75,7 +75,7 @@ async function getSongTitles(filter: string) {
limit: -1,
});
- return [...(new Set([...(songs.map((s:any) => s.title))]))];
+ return [...(new Set([...(songs.map((s: any) => s.title))]))];
}
async function getTagItems() {
@@ -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
}
-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;
diff --git a/client/src/components/windows/song/SongWindow.tsx b/client/src/components/windows/song/SongWindow.tsx
index 3a54b5e..6016366 100644
--- a/client/src/components/windows/song/SongWindow.tsx
+++ b/client/src/components/windows/song/SongWindow.tsx
@@ -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
+}
+
+export function SongWindowControlled(props: {
+ state: SongWindowState,
+ dispatch: (action: any) => void,
+}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;
diff --git a/client/src/components/windows/tag/TagWindow.tsx b/client/src/components/windows/tag/TagWindow.tsx
index 3ec693e..fd092ee 100644
--- a/client/src/components/windows/tag/TagWindow.tsx
+++ b/client/src/components/windows/tag/TagWindow.tsx
@@ -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
+}
+
+export function TagWindowControlled(props: {
+ state: TagWindowState,
+ dispatch: (action: any) => void,
+}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;