Fix tags page too.

pull/21/head
Sander Vocke 5 years ago
parent 935614d12f
commit 97eb32bfb6
  1. 2
      client/src/components/tables/ResultsTable.tsx
  2. 6
      client/src/components/windows/AlbumWindow.tsx
  3. 158
      client/src/components/windows/TagWindow.tsx
  4. 2
      client/src/components/windows/Windows.tsx

@ -110,6 +110,8 @@ export default function SongTable(props: IProps) {
tabLabel: <><LocalOfferIcon />{name}</>, tabLabel: <><LocalOfferIcon />{name}</>,
tagId: id, tagId: id,
metadata: null, metadata: null,
songGetters: songGetters,
songsWithTag: null,
}, },
tabReducer: newWindowReducer[WindowType.Tag], tabReducer: newWindowReducer[WindowType.Tag],
tabType: WindowType.Tag, tabType: WindowType.Tag,

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Box, Typography, IconButton, Button, CircularProgress } from '@material-ui/core'; import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import PersonIcon from '@material-ui/icons/Person'; import AlbumIcon from '@material-ui/icons/Album';
import * as serverApi from '../../api'; import * as serverApi from '../../api';
import { WindowState } from './Windows'; import { WindowState } from './Windows';
import StoreLinkIcon, { whichStore } from '../common/StoreLinkIcon'; import StoreLinkIcon, { whichStore } from '../common/StoreLinkIcon';
@ -173,7 +173,7 @@ export default function AlbumWindow(props: IProps) {
mt={4} mt={4}
width="80%" width="80%"
> >
<PersonIcon style={{ fontSize: 80 }} /> <AlbumIcon style={{ fontSize: 80 }} />
</Box> </Box>
<Box <Box
m={1} m={1}

@ -1,24 +1,44 @@
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { Box, Typography } from '@material-ui/core'; import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import LocalOfferIcon from '@material-ui/icons/LocalOffer'; import LocalOfferIcon from '@material-ui/icons/LocalOffer';
import * as serverApi from '../../api'; import * as serverApi from '../../api';
import { WindowState } from './Windows'; import { WindowState } from './Windows';
import StoreLinkIcon, { whichStore } from '../common/StoreLinkIcon';
import EditableText from '../common/EditableText';
import SubmitChangesButton from '../common/SubmitChangesButton';
import SongTable, { SongGetters } from '../tables/ResultsTable';
var _ = require('lodash');
export type TagMetadata = serverApi.TagDetails; export interface FullTagMetadata extends serverApi.TagDetails {
fullName: string[],
fullId: number[],
}
export type TagMetadata = FullTagMetadata;
export type TagMetadataChanges = serverApi.ModifyTagRequest;
export interface TagWindowState extends WindowState { export interface TagWindowState extends WindowState {
tagId: number, tagId: number,
metadata: TagMetadata | null, metadata: TagMetadata | null,
pendingChanges: TagMetadataChanges | null,
songsWithTag: any[] | null,
songGetters: SongGetters,
} }
export enum TagWindowStateActions { export enum TagWindowStateActions {
SetMetadata = "SetMetadata", SetMetadata = "SetMetadata",
SetPendingChanges = "SetPendingChanges",
SetSongs = "SetSongs",
} }
export function TagWindowReducer(state: TagWindowState, action: any) { export function TagWindowReducer(state: TagWindowState, action: any) {
switch (action.type) { switch (action.type) {
case TagWindowStateActions.SetMetadata: case TagWindowStateActions.SetMetadata:
return { ...state, metadata: action.value } return { ...state, metadata: action.value }
case TagWindowStateActions.SetPendingChanges:
return { ...state, pendingChanges: action.value }
case TagWindowStateActions.SetSongs:
return { ...state, songsWithTag: action.value }
default: default:
throw new Error("Unimplemented TagWindow state update.") throw new Error("Unimplemented TagWindow state update.")
} }
@ -61,23 +81,118 @@ export async function getTagMetadata(id: number) {
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts) const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
let json: any = await response.json(); let json: any = await response.json();
let tag = json.tags[0]; let tag = json.tags[0];
// Recursively fetch parent tags to build the full metadata.
if (tag.parentId) {
const parent = await getTagMetadata(tag.parentId);
tag.fullName = [...parent.fullName, tag.name];
tag.fullId = [...parent.fullId, tag.tagId];
} else {
tag.fullName = [tag.name];
tag.fullId = [tag.tagId];
}
return tag; return tag;
})(); })();
} }
export default function TagWindow(props: IProps) { export default function TagWindow(props: IProps) {
let metadata = props.state.metadata; let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;
// Effect to get the tag's metadata.
useEffect(() => { useEffect(() => {
getTagMetadata(props.state.tagId) getTagMetadata(props.state.tagId)
.then((m: TagMetadata) => { .then((m: TagMetadata) => {
console.log("metadata", m);
props.dispatch({ props.dispatch({
type: TagWindowStateActions.SetMetadata, type: TagWindowStateActions.SetMetadata,
value: m value: m
}); });
}) })
}, [props.state.metadata?.name]); }, [metadata?.name]);
// Effect to get the tag's songs.
useEffect(() => {
if (props.state.songsWithTag) { return; }
var q: serverApi.QueryRequest = {
query: {
prop: serverApi.QueryElemProperty.tagId,
propOperator: serverApi.QueryFilterOp.Eq,
propOperand: props.state.tagId,
},
offsetsLimits: {
songOffset: 0,
songLimit: 100,
},
ordering: {
orderBy: {
type: serverApi.OrderByType.Name,
},
ascending: true,
},
};
const requestOpts = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(q),
};
(async () => {
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
let json: any = await response.json();
props.dispatch({
type: TagWindowStateActions.SetSongs,
value: json.songs,
});
})();
}, [props.state.songsWithTag]);
const [editingName, setEditingName] = useState<string | null>(null);
const name = <Typography variant="h4"><EditableText
defaultValue={metadata?.name || "(Unknown name)"}
changedValue={pendingChanges?.name || null}
editingValue={editingName}
editingLabel="Name"
onChangeEditingValue={(v: string | null) => setEditingName(v)}
onChangeChangedValue={(v: string | null) => {
let newVal: any = { ...pendingChanges };
if (v) { newVal.name = v }
else { delete newVal.name }
props.dispatch({
type: TagWindowStateActions.SetPendingChanges,
value: newVal,
})
}}
/></Typography>
const fullName = <Box display="flex" alignItems="center">
{metadata?.fullName.map((n: string, i: number) => {
if (metadata?.fullName && i == metadata?.fullName.length - 1) {
return name;
} else if (i >= (metadata?.fullName.length || 0) - 1) {
return undefined;
} else {
return <Typography variant="h4">{n}&nbsp;/&nbsp;</Typography>
}
})}
</Box>
const storeLinks = metadata?.storeLinks && metadata?.storeLinks.map((link: string) => {
const store = whichStore(link);
return store && <a
href={link} target="_blank"
>
<IconButton><StoreLinkIcon
whichStore={store}
style={{ height: '40px', width: '40px' }}
/>
</IconButton>
</a>
});
const maybeSubmitButton = pendingChanges && Object.keys(pendingChanges).length > 0 &&
<SubmitChangesButton />
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap">
<Box <Box
@ -85,13 +200,42 @@ export default function TagWindow(props: IProps) {
mt={4} mt={4}
width="80%" width="80%"
> >
<LocalOfferIcon style={{ fontSize: 80 }}/> <LocalOfferIcon style={{ fontSize: 80 }} />
</Box> </Box>
<Box <Box
m={1} m={1}
width="80%" width="80%"
> >
{metadata && <Typography variant="h4">{metadata.name}</Typography>} {metadata && <Box>
<Box m={2}>
{fullName}
</Box>
<Box m={1}>
<Box display="flex" alignItems="center" m={0.5}>
{storeLinks}
</Box>
</Box>
</Box>}
</Box>
<Box
m={1}
width="80%"
>
{maybeSubmitButton}
</Box>
<Box
m={1}
width="80%"
>
<Box display="flex" flexDirection="column" alignItems="left">
<Typography>Songs with this tag in your library:</Typography>
</Box>
{props.state.songsWithTag && <SongTable
songs={props.state.songsWithTag}
songGetters={props.state.songGetters}
mainDispatch={props.mainDispatch}
/>}
{!props.state.songsWithTag && <CircularProgress />}
</Box> </Box>
</Box> </Box>
} }

@ -74,6 +74,8 @@ export const newWindowState = {
tagId: 1, tagId: 1,
metadata: null, metadata: null,
pendingChanges: null, pendingChanges: null,
songGetters: songGetters,
songsWithTag: null,
} }
}, },
} }
Loading…
Cancel
Save