diff --git a/client/src/components/windows/manage_links/BatchLinkDialog.tsx b/client/src/components/windows/manage_links/BatchLinkDialog.tsx index 737abe9..c5360d5 100644 --- a/client/src/components/windows/manage_links/BatchLinkDialog.tsx +++ b/client/src/components/windows/manage_links/BatchLinkDialog.tsx @@ -89,7 +89,6 @@ async function makeTasks( if (linkTracks) { promises.push(doForType(ResourceType.Track)); } if (linkArtists) { promises.push(doForType(ResourceType.Artist)); } if (linkAlbums) { promises.push(doForType(ResourceType.Album)); } - console.log("Awaiting answer...") await Promise.all(promises); } @@ -98,8 +97,6 @@ async function doLinking( setStatus: any, integrations: IntegrationState[], ) { - console.log("Linking start!", toLink); - // Start the collecting phase. setStatus((s: any) => { return { @@ -109,15 +106,12 @@ async function doLinking( tasksFailed: 0, } }); - - console.log("Starting collection"); var tasks: Task[] = []; let collectionPromises = toLink.map((v: any) => { let { integrationId, tracks, artists, albums } = v; let integration = integrations.find((i: IntegrationState) => i.id === integrationId); if (!integration) { return; } - console.log('integration collect:', integration) return makeTasks( integration, tracks, @@ -126,9 +120,7 @@ async function doLinking( (t: Task) => { tasks.push(t) } ); }) - console.log("Awaiting collection.") await Promise.all(collectionPromises); - console.log("Done collecting.", tasks) // Start the linking phase. setStatus((status: BatchJobStatus) => { return { @@ -157,7 +149,6 @@ async function doLinking( }); try { if (integration === undefined) { return; } - console.log('integration search:', integration) let _integration = integration as IntegrationState; let searchFuncs: any = { [ResourceType.Track]: (q: any, l: any) => { return _integration.integration.searchTrack(q, l) }, @@ -202,7 +193,6 @@ async function doLinking( success = true; } - console.log(query, candidates); if (success) { onSuccess(); } else { diff --git a/client/src/components/windows/manage_links/LinksStatusWidget.tsx b/client/src/components/windows/manage_links/LinksStatusWidget.tsx index 59b28c6..30cdd57 100644 --- a/client/src/components/windows/manage_links/LinksStatusWidget.tsx +++ b/client/src/components/windows/manage_links/LinksStatusWidget.tsx @@ -12,7 +12,7 @@ export default function LinksStatusWidget(props: { }) { type Counts = { - songs: number | undefined, + tracks: number | undefined, albums: number | undefined, artists: number | undefined, }; @@ -27,7 +27,7 @@ export default function LinksStatusWidget(props: { [ResourceType.Album]: QueryLeafBy.AlbumStoreLinks, } let whichElem: any = { - [ResourceType.Track]: 'songs', + [ResourceType.Track]: 'tracks', [ResourceType.Artist]: 'artists', [ResourceType.Album]: 'albums', } @@ -42,6 +42,7 @@ export default function LinksStatusWidget(props: { undefined, QueryResponseType.Count ); + console.log("Result: ", type, store, r); return r[whichElem[type]]; } @@ -55,6 +56,7 @@ export default function LinksStatusWidget(props: { undefined, QueryResponseType.Count ); + console.log("Got total counts: ", counts) setTotalCounts(counts); } )(); @@ -64,16 +66,17 @@ export default function LinksStatusWidget(props: { useEffect(() => { (async () => { let promises = $enum(IntegrationWith).getValues().map((s: IntegrationWith) => { - let songsPromise: Promise = queryStoreCount(s, ResourceType.Track); + let tracksPromise: Promise = queryStoreCount(s, ResourceType.Track); let albumsPromise: Promise = queryStoreCount(s, ResourceType.Album); let artistsPromise: Promise = queryStoreCount(s, ResourceType.Artist); - let updatePromise = Promise.all([songsPromise, albumsPromise, artistsPromise]).then( + let updatePromise = Promise.all([tracksPromise, albumsPromise, artistsPromise]).then( (r: any[]) => { + console.log("Grouped: ", r) setLinkedCounts((prev: Record) => { return { ...prev, [s]: { - songs: r[0], + tracks: r[0], artists: r[2], albums: r[1], } @@ -101,7 +104,7 @@ export default function LinksStatusWidget(props: { let tot = totalCounts; let lin = linkedCounts[s]; let perc = { - songs: Math.round((lin.songs || 0) / (tot.songs || 1) * 100), + tracks: Math.round((lin.tracks || 0) / (tot.tracks || 1) * 100), artists: Math.round((lin.artists || 0) / (tot.artists || 1) * 100), albums: Math.round((lin.albums || 0) / (tot.albums || 1) * 100), } @@ -124,9 +127,9 @@ export default function LinksStatusWidget(props: { {lin.albums} / {tot.albums} - Linked songs: - - {lin.songs} / {tot.songs} + Linked tracks: + + {lin.tracks} / {tot.tracks}   diff --git a/client/src/components/windows/manage_tags/ManageTagsWindow.tsx b/client/src/components/windows/manage_tags/ManageTagsWindow.tsx index 7b533d9..f94005c 100644 --- a/client/src/components/windows/manage_tags/ManageTagsWindow.tsx +++ b/client/src/components/windows/manage_tags/ManageTagsWindow.tsx @@ -14,13 +14,14 @@ import { useHistory } from 'react-router'; import { NotLoggedInError, handleNotLoggedIn } from '../../../lib/backend/request'; import { useAuth } from '../../../lib/useAuth'; import * as serverApi from '../../../api/api'; +import { Id, QueryResponseTagDetails, Tag, Name } from '../../../api/api'; var _ = require('lodash'); export interface ManageTagsWindowState extends WindowState { // Tags are indexed by a string ID. This can be a stringified MuDBase ID integer, // or a UID for tags which only exist in the front-end and haven't been committed // to the database. - fetchedTags: Record | null, + fetchedTags: Record | null, pendingChanges: TagChange[], alert: ReactFragment | null, // For notifications such as errors } @@ -61,36 +62,50 @@ export function ManageTagsWindowReducer(state: ManageTagsWindowState, action: an } } -export function organiseTags(allTags: Record, fromId: string | null): any[] { - const base = Object.values(allTags).filter((tag: any) => { - var par: any = ("proposedParent" in tag) ? tag.proposedParent : tag.parentId; +export function organiseTags(allTags: Record, fromId: string | null): + ManagedTag[] { + const base = Object.values(allTags).filter((tag: ManagedTag) => { + var par = ("proposedParent" in tag) ? tag.proposedParent : tag.parentId; return (fromId === null && !par) || (par && par === fromId) }); - return base.map((tag: any) => { + return base.map((tag: ManagedTag) => { return { ...tag, - children: organiseTags(allTags, tag.tagId), + children: organiseTags(allTags, tag.strTagId), } }); } -export async function getAllTags() { +// Work with strings so we can have temporary randomized IDs. +type ManagedTag = (Tag & Name & { + id?: number, + strTagId: string, + strParentId: string | null, + strChildIds: string[], + proposeDelete?: boolean, + proposedName?: string, + proposedParent?: string | null, + proposedMergeInto?: string, + isNewTag?: boolean, + children?: ManagedTag[], +}); +export async function getAllTags(): Promise> { return (async () => { - var retval: Record = {}; - const tags: any = await queryTags( + var retval: Record = {}; + const tags: QueryResponseTagDetails[] = await queryTags( undefined, 0, -1, serverApi.QueryResponseType.Details, - ); - // Convert numeric IDs to string IDs because that is - // what we work with within this component. - tags.forEach((tag: any) => { - retval[tag.tagId.toString()] = { + ) as QueryResponseTagDetails[]; + tags.forEach((tag: QueryResponseTagDetails) => { + retval[tag.id.toString()] = { ...tag, - tagId: tag.tagId && tag.tagId.toString(), - parentId: tag.parentId && tag.parentId.toString(), - childIds: tag.childIds && tag.childIds.map((c: number) => c.toString()), + strTagId: tag.id.toString(), + strParentId: tag.parentId?.toString() || null, + strChildIds: tags + .filter((t: QueryResponseTagDetails) => t.parentId == tag.id) + .map((t: QueryResponseTagDetails) => t.id.toString() ) } }); return retval; @@ -116,14 +131,14 @@ export function CreateTagButton(props: any) { } export function SingleTag(props: { - tag: any, + tag: ManagedTag, prependElems: any[], dispatch: (action: any) => void, state: ManageTagsWindowState, - changedTags: any[], + changedTags: ManagedTag[], }) { const tag = props.tag; - const hasChildren = 'children' in tag && tag.children.length > 0; + const hasChildren = tag.children && tag.children.length > 0; const [menuPos, setMenuPos] = React.useState(null); const [expanded, setExpanded] = useState(true); @@ -163,9 +178,9 @@ export function SingleTag(props: { {props.prependElems} - {hasChildren && expanded && tag.children - .sort((a: any, b: any) => a.name.localeCompare(b.name)) - .map((child: any) => a.name.localeCompare(b.name)) + .map((child: ManagedTag) => , @@ -179,7 +194,7 @@ export function SingleTag(props: { open={menuPos !== null} onClose={onCloseMenu} onOpenTag={() => { - history.push('/tag/' + tag.tagId); + history.push('/tag/' + tag.strTagId); }} onRename={(s: string) => { props.dispatch({ @@ -189,7 +204,7 @@ export function SingleTag(props: { { type: TagChangeType.Rename, name: s, - id: tag.tagId, + id: tag.strTagId, } ] }) @@ -205,7 +220,7 @@ export function SingleTag(props: { ...props.state.pendingChanges, { type: TagChangeType.Delete, - id: tag.tagId, + id: tag.strTagId, } ] }) @@ -221,7 +236,7 @@ export function SingleTag(props: { ...props.state.pendingChanges, { type: TagChangeType.MoveTo, - id: tag.tagId, + id: tag.strTagId, parent: to, } ] @@ -238,7 +253,7 @@ export function SingleTag(props: { ...props.state.pendingChanges, { type: TagChangeType.MergeTo, - id: tag.tagId, + id: tag.strTagId, into: into, } ] @@ -254,13 +269,14 @@ export function SingleTag(props: { } -function annotateTagsWithChanges(tags: Record, changes: TagChange[]) { - var retval: Record = _.cloneDeep(tags); +function annotateTagsWithChanges(tags: Record, changes: TagChange[]) + : Record { + var retval: Record = _.cloneDeep(tags); const applyDelete = (id: string) => { retval[id].proposeDelete = true; - Object.values(tags).filter((t: any) => t.parentId === id) - .forEach((child: any) => applyDelete(child.tagId)); + Object.values(tags).filter((t: ManagedTag) => t.strParentId === id) + .forEach((child: ManagedTag) => applyDelete(child.strTagId)); } changes.forEach((change: TagChange) => { @@ -278,15 +294,20 @@ function annotateTagsWithChanges(tags: Record, changes: TagChange[] retval[change.id].proposedMergeInto = change.into; break; case TagChangeType.Create: + if (!change.name) { + throw new Error("Trying to create a tag without a name"); + } retval[change.id] = { + mbApi_typename: 'tag', isNewTag: true, name: change.name, - parentId: change.parent, - tagId: change.id, + strTagId: change.id, + strParentId: change.parent || null, + strChildIds: [], } if (change.parent) { - retval[change.parent].childIds = - [...retval[change.parent].childIds, change.id] + retval[change.parent].strChildIds = + [...retval[change.parent].strChildIds, change.id] } break; default: @@ -296,12 +317,12 @@ function annotateTagsWithChanges(tags: Record, changes: TagChange[] return retval; } -function applyTagsChanges(tags: Record, changes: TagChange[]) { +function applyTagsChanges(tags: Record, changes: TagChange[]) { var retval = _.cloneDeep(tags); const applyDelete = (id: string) => { - Object.values(tags).filter((t: any) => t.parentId === id) - .forEach((child: any) => applyDelete(child.tagId)); + Object.values(tags).filter((t: ManagedTag) => t.strParentId === id) + .forEach((child: ManagedTag) => applyDelete(child.strTagId)); delete retval[id].proposeDelete; } @@ -447,8 +468,8 @@ export function ManageTagsWindowControlled(props: { width="80%" > {tags && tags.length && tags - .sort((a: any, b: any) => a.name.localeCompare(b.name)) - .map((tag: any) => { + .sort((a: ManagedTag, b: ManagedTag) => a.name.localeCompare(b.name)) + .map((tag: ManagedTag) => { return