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 - New Tab - { - props.onClose(); - props.onCreateTab({ - windowType: WindowType.Query, - }) - }} - >{WindowType.Query} - { - props.onClose(); - props.onCreateTab({ - windowType: WindowType.ManageTags, - }) - }} - >Manage Tags - -} \ 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;