parent
8f9e3bd184
commit
9c661d67ee
10 changed files with 270 additions and 33 deletions
@ -0,0 +1,196 @@ |
||||
import React, { useEffect, useState } from 'react'; |
||||
import { AppBar, Box, Button, Dialog, FormControl, FormControlLabel, IconButton, Link, List, ListItem, ListItemIcon, ListItemText, MenuItem, Radio, RadioGroup, Select, Tab, Tabs, TextField, Typography } from "@material-ui/core"; |
||||
import { SongMetadata } from "./SongWindow"; |
||||
import StoreLinkIcon, { ExternalStore, whichStore } from '../../common/StoreLinkIcon'; |
||||
import CheckIcon from '@material-ui/icons/Check'; |
||||
import CancelIcon from '@material-ui/icons/Cancel'; |
||||
import OpenInNewIcon from '@material-ui/icons/OpenInNew'; |
||||
import DeleteIcon from '@material-ui/icons/Delete'; |
||||
import { $enum } from "ts-enum-util"; |
||||
import { useIntegrations, IntegrationsState, IntegrationState } from '../../../lib/integration/useIntegrations'; |
||||
import { IntegrationFeature, IntegrationSong } from '../../../lib/integration/Integration'; |
||||
import { TabPanel } from '@material-ui/lab'; |
||||
import { v1 } from 'uuid'; |
||||
let _ = require('lodash') |
||||
|
||||
export function ProvideLinksWidget(props: { |
||||
providers: IntegrationState[], |
||||
metadata: SongMetadata, |
||||
store: ExternalStore, |
||||
onChange: (link: string | undefined) => void, |
||||
}) { |
||||
let [selectedProviderIdx, setSelectedProviderIdx] = useState<number | undefined>( |
||||
props.providers.length > 0 ? 0 : undefined |
||||
); |
||||
let [query, setQuery] = useState<string>( |
||||
`${props.metadata.title} ${props.metadata.artists && props.metadata.artists[0].name}` |
||||
) |
||||
let [results, setResults] = useState<IntegrationSong[]>([]); |
||||
|
||||
let selectedProvider: IntegrationState | undefined = selectedProviderIdx !== undefined ? |
||||
props.providers[selectedProviderIdx] : undefined; |
||||
|
||||
let currentLink = props.metadata.storeLinks ? props.metadata.storeLinks.find( |
||||
(l: string) => whichStore(l) === props.store |
||||
) : undefined; |
||||
|
||||
return <Box display="flex" flexDirection="column"> |
||||
<Box display="flex" alignItems="center"> |
||||
<Select |
||||
value={selectedProviderIdx} |
||||
onChange={(e: any) => setSelectedProviderIdx(e.target.value)} |
||||
> |
||||
{props.providers.map((p: IntegrationState, idx: number) => { |
||||
return <MenuItem value={idx}>{p.properties.name}</MenuItem> |
||||
})} |
||||
</Select> |
||||
<TextField |
||||
value={query} |
||||
onChange={(e: any) => setQuery(e.target.value)} |
||||
/> |
||||
<Button |
||||
onClick={() => { |
||||
selectedProvider?.integration.searchSong(query, 10) |
||||
.then((songs: IntegrationSong[]) => setResults(songs)) |
||||
}} |
||||
>Search</Button> |
||||
</Box> |
||||
<FormControl> |
||||
<RadioGroup value={currentLink} onChange={(e: any) => props.onChange(e.target.value)}> |
||||
{results.map((result: IntegrationSong, idx: number) => { |
||||
let pretty = `"${result.title}"
|
||||
${result.artist && ` by ${result.artist.name}`} |
||||
${result.album && ` (${result.album.name})`}`;
|
||||
return <FormControlLabel |
||||
value={result.url || idx} |
||||
control={<Radio checked={(result.url || idx) === currentLink} />} |
||||
label={<Box display="flex" alignItems="center"> |
||||
{pretty} |
||||
<a href={result.url || ""} target="_blank"> |
||||
<IconButton><OpenInNewIcon /></IconButton> |
||||
</a> |
||||
</Box>} |
||||
/> |
||||
})} |
||||
</RadioGroup> |
||||
</FormControl> |
||||
</Box> |
||||
} |
||||
|
||||
export function ExternalLinksEditor(props: { |
||||
metadata: SongMetadata, |
||||
onChange: (v: SongMetadata) => void, |
||||
}) { |
||||
let [selectedIdx, setSelectedIdx] = useState<number>(0); |
||||
let integrations = useIntegrations(); |
||||
|
||||
let linksSet: Record<string, string | null> = |
||||
$enum(ExternalStore).getValues().reduce((prev: any, store: string) => { |
||||
var maybeLink: string | null = null; |
||||
props.metadata.storeLinks && props.metadata.storeLinks.forEach((link: string) => { |
||||
if (whichStore(link) === store) { |
||||
maybeLink = link; |
||||
} |
||||
}) |
||||
return { |
||||
...prev, |
||||
[store]: maybeLink, |
||||
} |
||||
}, {}) |
||||
|
||||
let store = $enum(ExternalStore).getValues()[selectedIdx]; |
||||
let providers: IntegrationState[] = Array.isArray(integrations.state) ? |
||||
integrations.state.filter( |
||||
(iState: IntegrationState) => ( |
||||
iState.integration.getFeatures().includes(IntegrationFeature.SearchSong) && |
||||
iState.integration.providesStoreLink() === store |
||||
) |
||||
) : []; |
||||
|
||||
return <Box display="flex" width="100%"> |
||||
<Box width="30%"> |
||||
<List> |
||||
|
||||
{$enum(ExternalStore).getValues().map((store: string, idx: number) => { |
||||
let maybeLink = linksSet[store]; |
||||
return <ListItem |
||||
selected={selectedIdx === idx} |
||||
onClick={(e: any) => setSelectedIdx(idx)} |
||||
button |
||||
> |
||||
<ListItemIcon>{linksSet[store] !== null ? <CheckIcon /> : <CancelIcon />}</ListItemIcon> |
||||
<ListItemIcon><StoreLinkIcon whichStore={store} /></ListItemIcon> |
||||
<ListItemText primary={store} /> |
||||
{maybeLink && <a href={maybeLink} target="_blank"> |
||||
<ListItemIcon><IconButton><OpenInNewIcon /></IconButton></ListItemIcon> |
||||
</a>} |
||||
{maybeLink && <ListItemIcon><IconButton |
||||
onClick={() => { |
||||
let newLinks = props.metadata.storeLinks?.filter( |
||||
(l: string) => whichStore(l) !== store |
||||
) |
||||
props.onChange({ |
||||
...props.metadata, |
||||
storeLinks: newLinks, |
||||
}); |
||||
}} |
||||
><DeleteIcon /> |
||||
</IconButton></ListItemIcon>} |
||||
</ListItem> |
||||
})} |
||||
</List> |
||||
</Box> |
||||
<Box ml={2} width="60%"> |
||||
{providers.length === 0 ? |
||||
<Typography>None of your configured integrations provides URL links for {store}.</Typography> : |
||||
<ProvideLinksWidget |
||||
providers={providers} |
||||
metadata={props.metadata} |
||||
store={store} |
||||
onChange={(link: string | undefined) => { |
||||
let removed = props.metadata.storeLinks?.filter( |
||||
(link: string) => whichStore(link) !== store |
||||
) || []; |
||||
let newValue = link ? [...removed, link] : removed; |
||||
if (!_.isEqual(new Set(newValue), new Set(props.metadata.storeLinks || []))) { |
||||
props.onChange({ |
||||
...props.metadata, |
||||
storeLinks: newValue, |
||||
}) |
||||
} |
||||
}} |
||||
/> |
||||
} |
||||
</Box> |
||||
</Box > |
||||
} |
||||
|
||||
export default function EditSongDialog(props: { |
||||
open: boolean, |
||||
onClose: () => void, |
||||
onSubmit: (v: SongMetadata) => void, |
||||
id: number, |
||||
metadata: SongMetadata, |
||||
}) { |
||||
enum EditSongTabs { |
||||
Details = 0, |
||||
ExternalLinks, |
||||
} |
||||
|
||||
let [editingMetadata, setEditingMetadata] = useState<SongMetadata>(props.metadata); |
||||
let [activeTab, setActiveTab] = useState<EditSongTabs>(EditSongTabs.Details); |
||||
|
||||
return <Dialog |
||||
maxWidth="lg" |
||||
fullWidth |
||||
open={props.open} |
||||
onClose={props.onClose} |
||||
disableBackdropClick={true}> |
||||
<ExternalLinksEditor |
||||
metadata={editingMetadata} |
||||
onChange={(v: SongMetadata) => setEditingMetadata(v)} |
||||
/> |
||||
{!_.isEqual(editingMetadata, props.metadata) && <Typography>Changed!</Typography>} |
||||
</Dialog> |
||||
|
||||
} |
Loading…
Reference in new issue