Fixed some more bugs.

editsong
Sander Vocke 4 years ago
parent b27176ae66
commit 0a9bec1c87
  1. 10
      client/src/components/windows/manage_links/BatchLinkDialog.tsx
  2. 21
      client/src/components/windows/manage_links/LinksStatusWidget.tsx
  3. 103
      client/src/components/windows/manage_tags/ManageTagsWindow.tsx
  4. 2
      client/src/lib/backend/queries.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 {

@ -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<number> = queryStoreCount(s, ResourceType.Track);
let tracksPromise: Promise<number> = queryStoreCount(s, ResourceType.Track);
let albumsPromise: Promise<number> = queryStoreCount(s, ResourceType.Album);
let artistsPromise: Promise<number> = 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<string, Counts>) => {
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: {
<td><Typography>{lin.albums} / {tot.albums}</Typography></td>
</tr>
<tr>
<td><Typography>Linked songs:</Typography></td>
<td><Box minWidth="200px"><LinearProgress variant="determinate" color="secondary" value={perc.songs} /></Box></td>
<td><Typography>{lin.songs} / {tot.songs}</Typography></td>
<td><Typography>Linked tracks:</Typography></td>
<td><Box minWidth="200px"><LinearProgress variant="determinate" color="secondary" value={perc.tracks} /></Box></td>
<td><Typography>{lin.tracks} / {tot.tracks}</Typography></td>
</tr>
<tr><td colSpan={4}>&nbsp;</td></tr>
</>

@ -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<string, any> | null,
fetchedTags: Record<string, ManagedTag> | 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<string, any>, 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<string, ManagedTag>, 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<Record<string, ManagedTag>> {
return (async () => {
var retval: Record<string, any> = {};
const tags: any = await queryTags(
var retval: Record<string, ManagedTag> = {};
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 | number[]>(null);
const [expanded, setExpanded] = useState<boolean>(true);
@ -163,9 +178,9 @@ export function SingleTag(props: {
{props.prependElems}
<TagChip transparent={tag.proposeDelete} label={tagLabel} />
</Box>
{hasChildren && expanded && tag.children
.sort((a: any, b: any) => a.name.localeCompare(b.name))
.map((child: any) => <SingleTag
{expanded && tag.children && tag.children
.sort((a: ManagedTag, b: ManagedTag) => a.name.localeCompare(b.name))
.map((child: ManagedTag) => <SingleTag
tag={child}
prependElems={[...props.prependElems,
<TagChip transparent={true} label={tagLabel} />,
@ -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<string, any>, changes: TagChange[]) {
var retval: Record<string, any> = _.cloneDeep(tags);
function annotateTagsWithChanges(tags: Record<string, ManagedTag>, changes: TagChange[])
: Record<string, ManagedTag> {
var retval: Record<string, ManagedTag> = _.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<string, any>, 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<string, any>, changes: TagChange[]
return retval;
}
function applyTagsChanges(tags: Record<string, any>, changes: TagChange[]) {
function applyTagsChanges(tags: Record<string, ManagedTag>, 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 <SingleTag
tag={tag}
prependElems={[]}

@ -31,8 +31,6 @@ export async function queryItems(
responseType: responseType,
};
console.log(q);
const requestOpts = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },

Loading…
Cancel
Save