Bug fix: Duplicate key props for folderless sequences

folders
Rick Carlino 2019-12-05 15:52:01 -06:00
parent b190fe3609
commit 435273994a
8 changed files with 121 additions and 99 deletions

View File

@ -1011,5 +1011,11 @@ export enum Actions {
SET_CONSISTENCY = "SET_CONSISTENCY",
PING_START = "PING_START",
PING_OK = "PING_OK",
PING_NO = "PING_NO"
PING_NO = "PING_NO",
// Sequence Folders
FOLDER_TOGGLE = "FOLDER_TOGGLE",
FOLDER_TOGGLE_ALL = "FOLDER_TOGGLE_ALL",
FOLDER_TOGGLE_EDIT = "FOLDER_EDIT",
}

View File

@ -2,12 +2,10 @@ import { FolderNode } from "../constants";
import { ingest } from "../data_transfer";
import {
collapseAll,
expandAll,
findFolder,
setFolderColor,
toggleFolderOpenState
} from "../actions";
import { times, sample } from "lodash";
import { sample } from "lodash";
import { cloneAndClimb, climb } from "../climb";
// Folder structure used in tests:
@ -81,13 +79,6 @@ describe("expand/collapse all", () => {
node.open = !sample([true, false]);
});
it("expands all folders", async () => {
const open = await expandAll(halfOpen);
climb(open, (node) => {
expect(node.open).toBe(true);
});
});
it("collapses all folders", async () => {
const closed = await collapseAll(halfOpen);
climb(closed, (node) => {
@ -95,23 +86,3 @@ describe("expand/collapse all", () => {
});
});
});
describe("toggleFolderOpenState", () => {
it("toggles the `open` value of a folder", () => {
times(3, async () => {
const node = randomNode();
if (!node) {
throw new Error("Impossible");
}
const { id } = node;
const before = findFolder(GRAPH, id);
const nextGraph = await toggleFolderOpenState(GRAPH, id);
const after = findFolder(nextGraph, id);
if (before && after) {
expect(after.open).toEqual(!before.open);
} else {
fail("Could not find ID.");
}
});
});
});

View File

@ -9,6 +9,7 @@ import { initSave, destroy, edit, save } from "../api/crud";
import { Folder } from "farmbot/dist/resources/api_resources";
import { DeepPartial } from "redux";
import { findFolderById } from "../resources/selectors_by_id";
import { Actions } from "../constants";
type TreePromise = Promise<Tree>;
@ -23,23 +24,6 @@ export const findFolder = (tree: Tree, id: number) => {
return result;
};
export const toggleFolderOpenState =
(tree: Tree, id: number): TreePromise => {
return Promise.resolve(cloneAndClimb(tree, (node, halt) => {
if (node.id === id) {
node.open = !node.open;
halt();
}
}));
};
export const expandAll =
(tree: Tree): TreePromise => {
return Promise.resolve(cloneAndClimb(tree, (node) => {
node.open = true;
}));
};
export const collapseAll = (tree: Tree): TreePromise => {
return Promise.resolve(cloneAndClimb(tree, (node) => {
node.open = false;
@ -86,7 +70,7 @@ export const createFolder = (config: DeepPartial<Folder> = {}) => {
export const deleteFolder = (id: number) => {
const { index } = store.getState().resources;
const folder = findFolderById(index, id)
const folder = findFolderById(index, id);
const action = destroy(folder.uuid);
// tslint:disable-next-line:no-any
return store.dispatch(action as any) as ReturnType<typeof action>;
@ -96,3 +80,15 @@ export const moveFolderItem = (_: Tree) => Promise.reject("WIP");
export const moveFolder = (_: Tree) => Promise.reject("WIP");
export const searchSequencesAndFolders = (_: Tree) => Promise.reject("WIP");
export const searchByNameOrFolder = (_: Tree) => Promise.reject("WIP");
export const toggleFolderOpenState = (id: number) => Promise
.resolve(store.dispatch({ type: Actions.FOLDER_TOGGLE, payload: { id } }));
export const toggleFolderEditState = (id: number) => Promise
.resolve(store.dispatch({
type: Actions.FOLDER_TOGGLE_EDIT,
payload: { id }
}));
export const toggleAll = (payload: boolean) => Promise
.resolve(store.dispatch({ type: Actions.FOLDER_TOGGLE_ALL, payload }));

View File

@ -13,8 +13,8 @@ interface FolderUI {
* Not going to optimize prematurely -RC */
content: string[];
color: Color;
open?: boolean;
editing?: boolean;
open: boolean;
editing: boolean;
}
/** A top-level directory */

View File

@ -46,14 +46,20 @@ export const ingest: IngestFn = ({ folders, localMetaAttributes }) => {
...x,
kind: "terminal",
content: (localMetaAttributes[x.id] || {}).sequences || [],
children: []
open: true,
editing: false,
children: [],
...(localMetaAttributes[x.id] || {})
});
const medial = (x: FolderNode): FolderNodeMedial => ({
...x,
kind: "medial",
open: true,
editing: false,
children: childrenOf(x.id).map(terminal),
content: (localMetaAttributes[x.id] || {}).sequences || []
content: (localMetaAttributes[x.id] || {}).sequences || [],
...(localMetaAttributes[x.id] || {})
});
childrenOf(-1).map((root) => {
@ -61,8 +67,11 @@ export const ingest: IngestFn = ({ folders, localMetaAttributes }) => {
return output.folders.push({
...root,
kind: "initial",
open: true,
editing: false,
children,
content: (localMetaAttributes[root.id] || {}).sequences || []
content: (localMetaAttributes[root.id] || {}).sequences || [],
...(localMetaAttributes[root.id] || {})
});
});

View File

@ -3,7 +3,7 @@ import { Page, Col, Row, BlurableInput } from "../ui";
import { FolderUnion, RootFolderNode } from "./constants";
import { Everything } from "../interfaces";
import { connect } from "react-redux";
import { createFolder, deleteFolder, setFolderName } from "./actions";
import { createFolder, deleteFolder, setFolderName, toggleFolderOpenState } from "./actions";
import { TaggedSequence } from "farmbot";
import { selectAllSequences } from "../resources/selectors";
@ -24,8 +24,11 @@ const FolderNode = ({ node, sequences }: FolderNodeProps) => {
const subfolderBtn = <a onClick={creates}>📁</a>;
const deleteBtn = <a onClick={deletes}>🗑</a>;
const toggleBtn = <a onClick={() => { }}> {node.open ? "" : ""} </a>;
const toggleBtn = <a onClick={() => toggleFolderOpenState(node.id)}>
{node.open ? "" : ""}
</a>;
const editBtn = <a onClick={() => alert("TODO")}></a>;
const inputBox = <BlurableInput
value={node.name}
onCommit={({ currentTarget }) => {
@ -33,7 +36,7 @@ const FolderNode = ({ node, sequences }: FolderNodeProps) => {
}} />;
const names = node
.content
.map(x => <li key={"Z" + node.id}>*{sequences[x].body.name}</li>);
.map(x => <li key={"Z" + node.id + sequences[x].uuid}>*{sequences[x].body.name}</li>);
const children = <ul> {names} </ul>;
const stuff: { jsx: JSX.Element[], margin: number } =
@ -59,12 +62,12 @@ const FolderNode = ({ node, sequences }: FolderNodeProps) => {
}
return <div style={{ marginLeft: `${stuff.margin}px` }}>
{toggleBtn}
{subfolderBtn}
{node.kind !== "terminal" && subfolderBtn}
{deleteBtn}
{editBtn}
{inputBox}
{children}
{stuff.jsx}
{!!node.open && children}
{!!node.open && stuff.jsx}
</div>;
};
@ -86,12 +89,8 @@ export class RawFolders extends React.Component<Props, State> {
<Col xs={12} sm={6} smOffset={3}>
<Row>
<input placeholder={"Search"} disabled={true} />
<button onClick={() => createFolder()}>
Folder
</button>
<button>
Sequence
</button>
<button onClick={() => createFolder()}>Folder</button>
<button>Sequence</button>
</Row>
</Col>
<Col xs={12} sm={6} smOffset={3}>

View File

@ -8,7 +8,8 @@ import {
initResourceReducer,
afterEach,
beforeEach,
folderIndexer
folderIndexer,
reindexFolders
} from "./reducer_support";
import { TaggedResource, SpecialStatus } from "farmbot";
import { Actions } from "../constants";
@ -23,6 +24,7 @@ import { farmwareState } from "../farmware/reducer";
import { initialState as regimenState } from "../regimens/reducer";
import { initialState as sequenceState } from "../sequences/reducer";
import { initialState as alertState } from "../messages/reducer";
import { climb } from "../folders/climb";
export const emptyState = (): RestResources => {
return {
@ -157,4 +159,40 @@ export let resourceReducer =
payload: resource
});
}, s);
});
})
.add<{ id: number }>(Actions.FOLDER_TOGGLE, (s, { payload }) => {
console.log("WOOSH X 1");
const { localMetaAttributes } = s.index.sequenceFolders;
const record = localMetaAttributes[parseInt("" + payload.id)];
record.open = !record.open;
climb(s.index.sequenceFolders.folders, (node, halt) => {
if (node.id == payload.id) {
node.open = !node.open;
halt();
}
});
reindexFolders(s.index);
return s;
})
// .add<boolean>(Actions.FOLDER_TOGGLE_ALL, (s, { payload }) => {
// const { localMetaAttributes } = s.index.sequenceFolders;
// Object.keys(localMetaAttributes).map((x) => {
// localMetaAttributes[parseInt("" + x)].open = payload;
// });
// reindexFolders(s.index);
// return s;
// })
// .add<{ id: number }>(Actions.FOLDER_TOGGLE_EDIT, (s, { payload }) => {
// const { localMetaAttributes } = s.index.sequenceFolders;
// Object.keys(localMetaAttributes).map((x) => {
// if (x == ("" + payload.id)) {
// const record = localMetaAttributes[parseInt("" + x)];
// record.editing = !record.editing;
// }
// });
// return s;
// })
;

View File

@ -49,39 +49,42 @@ type IndexDirection =
type IndexerCallback = (self: TaggedResource, index: ResourceIndex) => void;
export interface Indexer extends Record<IndexDirection, IndexerCallback> { }
export const reindexFolders = (i: ResourceIndex) => {
const folders = betterCompact(selectAllFolders(i)
.map((x): FolderNode | undefined => {
const { body } = x;
if (typeof body.id === "number") {
const fn: FolderNode = { id: body.id, ...body };
return fn;
}
}));
const oldMeta = i.sequenceFolders.localMetaAttributes;
const localMetaAttributes: Record<number, FolderMeta> = {};
folders.map(x => {
localMetaAttributes[x.id] = {
...(oldMeta[x.id] || {}),
sequences: [], // Clobber and re-init
};
});
selectAllSequences(i).map((s) => {
const { folder_id } = s.body;
if (folder_id) {
(localMetaAttributes[folder_id]?.sequences || []).push(s.uuid);
}
});
i.sequenceFolders = {
folders: ingest({ folders, localMetaAttributes }),
localMetaAttributes
};
};
export const folderIndexer: IndexerCallback = (r, i) => {
if (r.kind === "Folder" || r.kind === "Sequence") {
const folders = betterCompact(selectAllFolders(i)
.map((x): FolderNode | undefined => {
const { body } = x;
if (typeof body.id === "number") {
const fn: FolderNode = { id: body.id, ...body };
return fn;
}
}));
const oldMeta = i.sequenceFolders.localMetaAttributes;
const localMetaAttributes: Record<number, FolderMeta> = {};
folders.map(x => {
localMetaAttributes[x.id] = {
editing: false,
open: false,
...(oldMeta[x.id] || {}),
sequences: [], // Clobber and re-init
};
});
selectAllSequences(i).map((s) => {
const { folder_id } = s.body;
if (folder_id) {
(localMetaAttributes[folder_id]?.sequences || []).push(s.uuid);
}
});
i.sequenceFolders = {
folders: ingest({ folders, localMetaAttributes }),
localMetaAttributes
};
reindexFolders(i);
}
};