import React, { useEffect, useState } 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'; import AlbumIcon from '@material-ui/icons/Album'; import * as serverApi from '../../api'; import { WindowState } from './Windows'; import { ArtistMetadata } from './ArtistWindow'; import { AlbumMetadata } from './AlbumWindow'; import StoreLinkIcon, { whichStore } from '../common/StoreLinkIcon'; import EditableText from '../common/EditableText'; import SubmitChangesButton from '../common/SubmitChangesButton'; import { saveSongChanges } from '../../lib/saveChanges'; export type SongMetadata = serverApi.SongDetails; export type SongMetadataChanges = serverApi.ModifySongRequest; export interface SongWindowState extends WindowState { songId: number, metadata: SongMetadata | null, pendingChanges: SongMetadataChanges | null, } export enum SongWindowStateActions { SetMetadata = "SetMetadata", SetPendingChanges = "SetPendingChanges", Reload = "Reload", } export function SongWindowReducer(state: SongWindowState, action: any) { switch (action.type) { case SongWindowStateActions.SetMetadata: return { ...state, metadata: action.value } case SongWindowStateActions.SetPendingChanges: return { ...state, pendingChanges: action.value } case SongWindowStateActions.Reload: return { ...state, metadata: null, pendingChanges: null } 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 song; })(); } export default function SongWindow(props: IProps) { let metadata = props.state.metadata; let pendingChanges = props.state.pendingChanges; useEffect(() => { getSongMetadata(props.state.songId) .then((m: SongMetadata) => { props.dispatch({ type: SongWindowStateActions.SetMetadata, value: m }); }) }, [metadata?.title]); const [editingTitle, setEditingTitle] = useState(null); const title = setEditingTitle(v)} onChangeChangedValue={(v: string | null) => { let newVal: any = { ...pendingChanges }; if (v) { newVal.title = v } else { delete newVal.title } props.dispatch({ type: SongWindowStateActions.SetPendingChanges, value: newVal, }) }} /> const artists = metadata?.artists && metadata?.artists.map((artist: ArtistMetadata) => { return {artist.name} }); const albums = metadata?.albums && metadata?.albums.map((album: AlbumMetadata) => { return {album.name} }); const storeLinks = metadata?.storeLinks && metadata?.storeLinks.map((link: string) => { const store = whichStore(link); return store && }); const [applying, setApplying] = useState(false); const maybeSubmitButton = pendingChanges && Object.keys(pendingChanges).length > 0 && { setApplying(true); saveSongChanges(props.state.songId, pendingChanges || {}) .then(() => { setApplying(false); props.dispatch({ type: SongWindowStateActions.Reload }) }) }} /> {applying && } return {metadata && {title} {artists} {albums} {storeLinks} } {maybeSubmitButton} }