|
|
|
@ -19,7 +19,8 @@ export enum TagChangeType { |
|
|
|
|
export interface TagChange { |
|
|
|
|
type: TagChangeType, |
|
|
|
|
id: number, // MuDBase ID. If not in database yet, negative IDs will be used until submitted.
|
|
|
|
|
parent?: number, // MuDBase ID. If not in database yet, negative IDs will be used until submitted.
|
|
|
|
|
parent?: number | null, // MuDBase ID. If not in database yet, negative IDs will be used until submitted.
|
|
|
|
|
// null refers to the tags root.
|
|
|
|
|
name?: string, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -51,10 +52,12 @@ export function ManageTagsWindowReducer(state: ManageTagsWindowState, action: an |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function organiseTags(allTags: Record<number, any>, fromId: number | null): any[] { |
|
|
|
|
const base = Object.values(allTags).filter((tag: any) => |
|
|
|
|
(fromId === null && !tag.parentId) || |
|
|
|
|
(tag.parentId && tag.parentId === fromId) |
|
|
|
|
); |
|
|
|
|
const base = Object.values(allTags).filter((tag: any) => { |
|
|
|
|
var par: any = ("proposedParent" in tag) ? tag.proposedParent : tag.parentId; |
|
|
|
|
|
|
|
|
|
return (fromId === null && !par) || |
|
|
|
|
(par && par === fromId) |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return base.map((tag: any) => { |
|
|
|
|
return { |
|
|
|
@ -102,6 +105,7 @@ export function SingleTag(props: { |
|
|
|
|
prependElems: any[], |
|
|
|
|
dispatch: (action: any) => void, |
|
|
|
|
state: ManageTagsWindowState, |
|
|
|
|
changedTags: any[], |
|
|
|
|
}) { |
|
|
|
|
const tag = props.tag; |
|
|
|
|
const hasChildren = 'children' in tag && tag.children.length > 0; |
|
|
|
@ -153,6 +157,7 @@ export function SingleTag(props: { |
|
|
|
|
<Typography variant="h5">/</Typography>]} |
|
|
|
|
dispatch={props.dispatch} |
|
|
|
|
state={props.state} |
|
|
|
|
changedTags={props.changedTags} |
|
|
|
|
/>)} |
|
|
|
|
<ManageTagMenu |
|
|
|
|
position={menuPos} |
|
|
|
@ -183,13 +188,27 @@ export function SingleTag(props: { |
|
|
|
|
] |
|
|
|
|
}) |
|
|
|
|
}} |
|
|
|
|
onMove={(to: number | null) => { |
|
|
|
|
props.dispatch({ |
|
|
|
|
type: ManageTagsWindowActions.SetPendingChanges, |
|
|
|
|
value: [ |
|
|
|
|
...props.state.pendingChanges, |
|
|
|
|
{ |
|
|
|
|
type: TagChangeType.MoveTo, |
|
|
|
|
id: tag.tagId, |
|
|
|
|
parent: to, |
|
|
|
|
} |
|
|
|
|
] |
|
|
|
|
}) |
|
|
|
|
}} |
|
|
|
|
tag={tag} |
|
|
|
|
changedTags={props.changedTags} |
|
|
|
|
/> |
|
|
|
|
</> |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function addTagChanges(tags: Record<number, any>, changes: TagChange[]) { |
|
|
|
|
var retval = tags; |
|
|
|
|
function annotateTagsWithChanges(tags: Record<number, any>, changes: TagChange[]) { |
|
|
|
|
var retval = _.cloneDeep(tags); |
|
|
|
|
|
|
|
|
|
const applyDelete = (id: number) => { |
|
|
|
|
retval[id].proposeDelete = true; |
|
|
|
@ -205,6 +224,37 @@ function addTagChanges(tags: Record<number, any>, changes: TagChange[]) { |
|
|
|
|
case TagChangeType.Delete: |
|
|
|
|
applyDelete(change.id); |
|
|
|
|
break; |
|
|
|
|
case TagChangeType.MoveTo: |
|
|
|
|
retval[change.id].proposedParent = change.parent; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new Error("Unimplemented tag change") |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
return retval; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function applyTagsChanges(tags: Record<number, any>, changes: TagChange[]) { |
|
|
|
|
var retval = _.cloneDeep(tags); |
|
|
|
|
|
|
|
|
|
const applyDelete = (id: number) => { |
|
|
|
|
Object.values(tags).filter((t: any) => t.parentId === id) |
|
|
|
|
.forEach((child: any) => applyDelete(child.tagId)); |
|
|
|
|
delete retval[id].proposeDelete; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
changes.forEach((change: TagChange) => { |
|
|
|
|
switch (change.type) { |
|
|
|
|
case TagChangeType.Rename: |
|
|
|
|
retval[change.id].name = change.name; |
|
|
|
|
break; |
|
|
|
|
case TagChangeType.Delete: |
|
|
|
|
applyDelete(change.id); |
|
|
|
|
break; |
|
|
|
|
case TagChangeType.MoveTo: |
|
|
|
|
retval[change.id].parentId = change.parent; |
|
|
|
|
if (change.parent === null) { delete retval[change.id].parentId; } |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new Error("Unimplemented tag change") |
|
|
|
|
} |
|
|
|
@ -234,7 +284,10 @@ export default function ManageTagsWindow(props: IProps) { |
|
|
|
|
})(); |
|
|
|
|
}, [props.state.fetchedTags]); |
|
|
|
|
|
|
|
|
|
const tagsWithChanges = addTagChanges(props.state.fetchedTags || {}, props.state.pendingChanges) |
|
|
|
|
const tagsWithChanges = annotateTagsWithChanges(props.state.fetchedTags || {}, props.state.pendingChanges) |
|
|
|
|
const changedTags = organiseTags( |
|
|
|
|
applyTagsChanges(props.state.fetchedTags || {}, props.state.pendingChanges), |
|
|
|
|
null); |
|
|
|
|
const tags = organiseTags(tagsWithChanges, null); |
|
|
|
|
|
|
|
|
|
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
|
|
|
@ -263,6 +316,7 @@ export default function ManageTagsWindow(props: IProps) { |
|
|
|
|
prependElems={[]} |
|
|
|
|
dispatch={props.dispatch} |
|
|
|
|
state={props.state} |
|
|
|
|
changedTags={changedTags} |
|
|
|
|
/>; |
|
|
|
|
})} |
|
|
|
|
</Box> |
|
|
|
|