diff --git a/conda/js/src/components/toolchain/ToolchainEditor.jsx b/conda/js/src/components/toolchain/ToolchainEditor.jsx index af52d038ce5156ca442bdbc8ba00ca1494f765ee..bde16399e208daaa279dbf455802dd1edec58e7d 100644 --- a/conda/js/src/components/toolchain/ToolchainEditor.jsx +++ b/conda/js/src/components/toolchain/ToolchainEditor.jsx @@ -34,6 +34,7 @@ import { getValidToolchainObj as getValidObj } from '@helpers/beat'; import type { BeatObject } from '@helpers/beat'; import { fetchLayout, genModuleApiFuncs } from '@helpers/api'; +import * as Actions from '@store/actions.js'; import * as Selectors from '@store/selectors.js'; import type { FlattenedDatabaseEntry } from '@store/selectors'; @@ -66,6 +67,7 @@ type Props = { analyzerAlgorithms: BeatObject[], // func to save changes on the current tc saveFunc: (BeatObject) => any, + updateFunc: (BeatObject) => any, }; // represents a timeline of state changes @@ -79,8 +81,6 @@ type History = { }; type State = { - // unsaved changes - cache: any, // info for the modal for editing blocks // if the fields are undefined, the modal shouldnt be up modalBlockInfo: { @@ -101,11 +101,11 @@ type State = { }; // generates a new history object whenever the user does an action that changes state -const generateNewHistory = (state: State): History => { +const generateNewHistory = (state: State, data: any): History => { return { past: [ ...state.history.past, - state.cache, + jsonClone(data), ], future: [], }; @@ -120,7 +120,6 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { } state = { - cache: getValidObj(this.props.data), modalBlockInfo: { name: undefined, set: undefined, @@ -136,77 +135,92 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { } // if the toolchain was updated, reset specific state fields - componentWillReceiveProps = (nextProps: Props) => { + UNSAFE_componentWillReceiveProps = (nextProps: Props) => { + /* this.setState({ - cache: getValidObj(nextProps.data), history: generateNewHistory(this.state), }); + */ } - // helper to set the contents (this.state.cache.contents) object + // helper to set the contents (this.props.data.contents) object setContents = (newContents: any) => { this.setState((prevState, props) => ({ - cache: { - ...prevState.cache, - contents: { - ...prevState.cache.contents, - ...newContents, - } - }, - history: generateNewHistory(prevState), + history: generateNewHistory(prevState, props.data), })); + + this.props.updateFunc({ + ...this.props.data, + contents: { + ...this.props.data.contents, + ...newContents, + } + }); } - // helper to set the groups (this.state.cache.extraContents.groups) + // helper to set the groups (this.props.data.extraContents.groups) setGroups = (groups: Group[]) => { this.setState((prevState, props) => ({ - cache: { - ...prevState.cache, - extraContents: { - ...prevState.cache.extraContents, - groups, - } - }, - history: generateNewHistory(prevState), + history: generateNewHistory(prevState, props.data), })); + + this.props.updateFunc({ + ...this.props.data, + extraContents: { + ...this.props.data.extraContents, + groups, + } + }); } // undoes history by saving the current state to the future // and switching to the latest history state undoHistory = () => { - this.setState((prevState) => { - if(prevState.history.past.length === 0) - return prevState; - const newPast = [...prevState.history.past]; - const nextCache = newPast.pop(); - const newFuture = [...prevState.history.future, prevState.cache]; - return { - cache: nextCache, - history: { - past: newPast, - future: newFuture, - }, - }; + if(this.state.history.past.length === 0) + return this.state; + + const newPast = [...this.state.history.past]; + const newData = newPast.pop(); + + const newFuture = [...this.state.history.future, jsonClone(this.props.data)]; + this.setState({ + history: { + past: newPast, + future: newFuture, + }, }); + + if(!newData){ + console.error(`No past state found!`); + return; + } + + this.props.updateFunc(newData); }; // redoes history by putting the current state in the past // and switching to the earliest future state redoHistory = () => { - this.setState((prevState) => { - if(prevState.history.future.length === 0) - return prevState; - const newFuture = [...prevState.history.future]; - const nextCache = newFuture.pop(); - const newPast = [...prevState.history.past, prevState.cache]; - return { - cache: nextCache, - history: { - past: newPast, - future: newFuture, - }, - }; + if(this.state.history.future.length === 0) + return this.state; + + const newFuture = [...this.state.history.future]; + const newData = newFuture.pop(); + + const newPast = [...this.state.history.past, jsonClone(this.props.data)]; + this.setState({ + history: { + past: newPast, + future: newFuture, + }, }); + + if(!newData){ + console.error(`No future state found!`); + return; + } + + this.props.updateFunc(newData); }; // toggles whether the object insert modal is active or not @@ -221,15 +235,15 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // renames a group from an old name to a new one renameGroup = (oldName: string, newName: string) => { - this.setGroups(this.state.cache.extraContents.groups.map(g => g.name === oldName ? {...g, name: newName} : g)); + this.setGroups(this.props.data.extraContents.groups.map(g => g.name === oldName ? {...g, name: newName} : g)); } // finds a block by name from the three block sub-arrays: // blocks (normal blocks), datasets (dataset blocks), analyzers (analyzer blocks) findBlock = (name: string): BlockType => { - const b = this.state.cache.contents.blocks.find(b => b.name === name) || - this.state.cache.contents.datasets.find(b => b.name === name) || - this.state.cache.contents.analyzers.find(b => b.name === name) + const b = this.props.data.contents.blocks.find(b => b.name === name) || + this.props.data.contents.datasets.find(b => b.name === name) || + this.props.data.contents.analyzers.find(b => b.name === name) ; if(!b) throw new Error(`invalid block name: ${ name }`); @@ -240,7 +254,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // creates a connection between a block's output and a block's input with a specific channel createConnections = (connectionData: ConnectionType[]) => { - const rep = this.state.cache.contents.representation; + const rep = this.props.data.contents.representation; // assign empty synchronized_channel vals on blocks that are getting their first connection const toBlockNamesAndChannels = connectionData.map(c => [c.to.split('.')[0], c.channel]); @@ -259,8 +273,8 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { ; }; - const newBlocks = assignBlockChannels(this.state.cache.contents.blocks); - const newAnalyzers = assignBlockChannels(this.state.cache.contents.analyzers); + const newBlocks = assignBlockChannels(this.props.data.contents.blocks); + const newAnalyzers = assignBlockChannels(this.props.data.contents.analyzers); const newRepConns = generateConnectionRepresentations(connectionData); @@ -268,7 +282,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { blocks: newBlocks, analyzers: newAnalyzers, connections: [ - ...this.state.cache.contents.connections, + ...this.props.data.contents.connections, ...connectionData, ], representation: { @@ -283,7 +297,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // updates a blocks location in the graphical editor by providing an x & y offset relative to the current location. updateBlockLocations = (bLocs: {blockName: string, x: number, y: number}[]) => { - const rep = this.state.cache.contents.representation; + const rep = this.props.data.contents.representation; const newReps = bLocs .map(bLoc => ({ [bLoc.blockName]: { ...rep.blocks[bLoc.blockName], col: bLoc.x, row: bLoc.y }})) .reduce((o, bLoc) => ({ ...o, ...bLoc }), {});; @@ -310,7 +324,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { }; // update the block & conn representation with the new names - const rep = {...this.state.cache.contents.representation}; + const rep = {...this.props.data.contents.representation}; rep.blocks = {...rep.blocks}; rep.blocks[newName] = rep.blocks[oldName]; delete rep.blocks[oldName]; @@ -333,10 +347,10 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // update blocks & connections const newContents = { - ...this.state.cache.contents, - [set]: this.state.cache.contents[set].map(s => s.name === oldName ? {...s, name: newName} : s), + ...this.props.data.contents, + [set]: this.props.data.contents[set].map(s => s.name === oldName ? {...s, name: newName} : s), // update the conns, both the names of the blocks and the channel - connections: this.state.cache.contents.connections.map(c => { + connections: this.props.data.contents.connections.map(c => { if(!c.from.includes(oldName) && !c.to.includes(oldName) && c.channel !== oldName) return c; @@ -354,34 +368,34 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // if the changed block is a dataset, update the synchronized_channel on all blocks using its old name if(set === 'datasets'){ - newContents['blocks'] = this.state.cache.contents.blocks + newContents['blocks'] = this.props.data.contents.blocks .map(s => s.synchronized_channel === oldName ? {...s, synchronized_channel: newName} : s) ; - newContents['analyzers'] = this.state.cache.contents.analyzers + newContents['analyzers'] = this.props.data.contents.analyzers .map(s => s.synchronized_channel === oldName ? {...s, synchronized_channel: newName} : s) ; } - const newGroups = this.state.cache.extraContents.groups + const newGroups = this.props.data.extraContents.groups .map(({ name, blocks }) => ({ name, blocks: blocks.map(n => n === oldName ? newName : n) })); - console.log(newName); - this.setState((prevState) => ({ - cache: { - ...prevState.cache, - contents: newContents, - extraContents: { - ...prevState.cache.extraContents, - groups: newGroups, - }, - }, - history: generateNewHistory(prevState), + this.setState((prevState, props) => ({ + history: generateNewHistory(prevState, props.data), modalBlockInfo: { active: true, name: newName, set } })); + + this.props.updateFunc({ + ...this.props.data, + contents: newContents, + extraContents: { + ...this.props.data.extraContents, + groups: newGroups, + }, + }); } // updates a block's input or output name @@ -396,7 +410,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { toSplit[1] = to === combinedName ? newName : toSplit[1]; return [fromSplit.join('.'), toSplit.join('.')]; }; - const rep = {...this.state.cache.contents.representation}; + const rep = {...this.props.data.contents.representation}; rep.connections = Object.entries(rep.connections).map(([name, rep]) => { if(!name.includes(`.${ oldName }`)) @@ -410,8 +424,8 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { .reduce((cs, [name, map]) => ({...cs, [name]: rep}), {}) ; const newContents = { - ...this.state.cache.contents, - [set]: this.state.cache.contents[set].map(s => { + ...this.props.data.contents, + [set]: this.props.data.contents[set].map(s => { if(s.name !== blockName) return s; const newBlock = { @@ -423,7 +437,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { newBlock.outputs = newBlock.outputs.map(str => str === oldName ? newName : str); return newBlock; }), - connections: this.state.cache.contents.connections.map(c => { + connections: this.props.data.contents.connections.map(c => { if(!c.from.includes(`.${ oldName }`) && !c.to.includes(`.${ oldName }`)) return c; const updated = updateConn(c.from, c.to); @@ -449,7 +463,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { newBlock = {}; if(set === 'blocks' || set === 'analyzers'){ newBlock.inputs = []; - newBlock.synchronized_channel = Object.keys(this.state.cache.contents.representation.channel_colors)[0]; + newBlock.synchronized_channel = Object.keys(this.props.data.contents.representation.channel_colors)[0]; } if(set === 'blocks' || set === 'datasets'){ newBlock.outputs = []; @@ -489,13 +503,13 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { const sets = blockData.map(b => b[1]); const rep = { - ...this.state.cache.contents.representation, + ...this.props.data.contents.representation, blocks: { - ...this.state.cache.contents.representation.blocks, + ...this.props.data.contents.representation.blocks, ...newBlocks.reduce((newReps, b) => ({...newReps, [b.block.name]: b.rep}), {}), }, channel_colors: { - ...this.state.cache.contents.representation.channel_colors, + ...this.props.data.contents.representation.channel_colors, ...newBlocks.filter(b => b.channelColor !== null) .reduce((newColors, b) => ({...newColors, [b.block.name]: b.channelColor}), {}), } @@ -503,18 +517,18 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { const newContents = { - blocks: [...this.state.cache.contents.blocks, ...newBlocks.filter((b, i) => sets[i] === 'blocks').map(b => b.block)], - datasets: [...this.state.cache.contents.datasets, ...newBlocks.filter((b, i) => sets[i] === 'datasets').map(b => b.block)], - analyzers: [...this.state.cache.contents.analyzers, ...newBlocks.filter((b, i) => sets[i] === 'analyzers').map(b => b.block)], + blocks: [...this.props.data.contents.blocks, ...newBlocks.filter((b, i) => sets[i] === 'blocks').map(b => b.block)], + datasets: [...this.props.data.contents.datasets, ...newBlocks.filter((b, i) => sets[i] === 'datasets').map(b => b.block)], + analyzers: [...this.props.data.contents.analyzers, ...newBlocks.filter((b, i) => sets[i] === 'analyzers').map(b => b.block)], representation: rep, - connections: this.state.cache.contents.connections, + connections: this.props.data.contents.connections, }; // if the blocks werent copied from an external toolchain, // check for connection info from the copied blocks and create new connections between the new blocks if(!connections){ const copyBlocks = blockData.filter(bd => bd.length > 4); - const newConnections: ConnectionType[] = [...this.state.cache.contents.connections].map(({ from, to, channel }) => { + const newConnections: ConnectionType[] = [...this.props.data.contents.connections].map(({ from, to, channel }) => { const fromBlock = copyBlocks.find(bd => from.startsWith(`${ bd[4].name }.`)); const toBlock = copyBlocks.find(bd => to.startsWith(`${ bd[4].name }.`)); if(!fromBlock || !toBlock) @@ -531,13 +545,13 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { } }) .filter(c => c); - newContents.connections = [...this.state.cache.contents.connections, ...newConnections]; + newContents.connections = [...this.props.data.contents.connections, ...newConnections]; const newRepConns = generateConnectionRepresentations(newConnections); newContents.representation.connections = {...rep.connections, ...newRepConns}; } else { // add the new connections from the copied toolchain const newConnections = connections; - newContents.connections = [...this.state.cache.contents.connections, ...newConnections]; + newContents.connections = [...this.props.data.contents.connections, ...newConnections]; const newRepConns = generateConnectionRepresentations(newConnections); newContents.representation.connections = {...rep.connections, ...newRepConns}; } @@ -548,7 +562,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // add a new input or output to a block addBlockIO = (blockName: string, set: BlockSet, ioType: 'input' | 'output', newName: string) => { const newContents = { - [set]: this.state.cache.contents[set].map(s => { + [set]: this.props.data.contents[set].map(s => { if(s.name !== blockName) return s; const newBlock = { @@ -569,7 +583,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // as well as any connections to/from it // if it was the sole member of a group, deletes the group too deleteBlocks = (names: string[]) => { - const rep = jsonClone(this.state.cache.contents.representation); + const rep = jsonClone(this.props.data.contents.representation); names.forEach(name => { delete rep.blocks[name]; @@ -588,11 +602,11 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { //console.log(Object.keys(rep.connections)); const newContents = { - ...this.state.cache.contents, - blocks: this.state.cache.contents.blocks.filter(s => !names.includes(s.name)), - datasets: this.state.cache.contents.datasets.filter(s => !names.includes(s.name)), - analyzers: this.state.cache.contents.analyzers.filter(s => !names.includes(s.name)), - connections: this.state.cache.contents.connections.filter(c => { + ...this.props.data.contents, + blocks: this.props.data.contents.blocks.filter(s => !names.includes(s.name)), + datasets: this.props.data.contents.datasets.filter(s => !names.includes(s.name)), + analyzers: this.props.data.contents.analyzers.filter(s => !names.includes(s.name)), + connections: this.props.data.contents.connections.filter(c => { if(names.find(blockName => c.to.startsWith(`${ blockName }.`) || c.from.startsWith(`${ blockName }.`))) return false; return true; @@ -600,29 +614,28 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { representation: rep, }; - const newGroups = this.state.cache.extraContents.groups + const newGroups = this.props.data.extraContents.groups .map(({ name, blocks }) => ({ name, blocks: blocks.filter(n => !names.includes(n)) })) .filter(g => g.blocks.length !== 0); this.setState({ - cache: { - ...this.state.cache, - contents: newContents, - extraContents: { - ...this.state.cache.extraContents, - groups: newGroups, - }, - }, - history: generateNewHistory(this.state), + history: generateNewHistory(this.state, this.props.data), }); - this.setContents(newContents); + this.props.updateFunc({ + ...this.props.data, + contents: newContents, + extraContents: { + ...this.props.data.extraContents, + groups: newGroups, + }, + }); } // deletes a connection // looks at the representation object and the connections array deleteConnection = (cn: ConnectionType) => { - const rep = {...this.state.cache.contents.representation}; + const rep = {...this.props.data.contents.representation}; rep.connections = Object.entries(rep.connections).filter(([name, rep]) => { const [from, to] = name.split('/'); @@ -634,7 +647,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { ; const newContents = { - connections: this.state.cache.contents.connections.filter(c => { + connections: this.props.data.contents.connections.filter(c => { if(c.from === cn.from && c.to === cn.to) return false; return true; @@ -648,7 +661,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // deletes an input or output from a block, // as well as any connections connected to it deleteBlockIO = (blockName: string, set: BlockSet, ioType: 'input' | 'output', ioName: string) => { - const rep = {...this.state.cache.contents.representation}; + const rep = {...this.props.data.contents.representation}; const connectionLabel = `${ blockName }.${ ioName }`; @@ -666,8 +679,8 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { .reduce((cs, [name, rep]) => ({...cs, [name]: rep}), {}) ; const newContents = { - ...this.state.cache.contents, - [set]: this.state.cache.contents[set].map(s => { + ...this.props.data.contents, + [set]: this.props.data.contents[set].map(s => { if(s.name !== blockName) return s; const newBlock = { @@ -679,7 +692,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { newBlock.outputs = newBlock.outputs.filter(str => str !== ioName); return newBlock; }), - connections: this.state.cache.contents.connections.filter(c => { + connections: this.props.data.contents.connections.filter(c => { if(ioType === 'input' && c.to === connectionLabel) return false; if(ioType === 'output' && c.from === connectionLabel) @@ -694,10 +707,10 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // changes the block's sync'd channel // also updates all connections from the block updateBlockChannel = (blockName: string, set: BlockSet, channel: string) => { - const queue = [this.state.cache.contents[set].find(b => b.name === blockName)]; + const queue = [this.props.data.contents[set].find(b => b.name === blockName)]; const oldChannel = `${ queue[0].synchronized_channel }`; - const bNames = Object.keys(this.state.cache.contents.representation.blocks); - const conns = this.state.cache.contents.connections; + const bNames = Object.keys(this.props.data.contents.representation.blocks); + const conns = this.props.data.contents.connections; const updatedBlocks: { [string]: BlockType } = {}; const updatedConnections: { [string]: ConnectionType } = {}; while(queue.length > 0){ @@ -732,11 +745,11 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { } const newContents = { - ...this.state.cache.contents, - datasets: this.state.cache.contents.datasets.map(b => updatedBlocks[b.name] || b), - blocks: this.state.cache.contents.blocks.map(b => updatedBlocks[b.name] || b), - analyzers: this.state.cache.contents.analyzers.map(b => updatedBlocks[b.name] || b), - connections: this.state.cache.contents.connections.map(c => updatedConnections[connectionToId(c)] || c), + ...this.props.data.contents, + datasets: this.props.data.contents.datasets.map(b => updatedBlocks[b.name] || b), + blocks: this.props.data.contents.blocks.map(b => updatedBlocks[b.name] || b), + analyzers: this.props.data.contents.analyzers.map(b => updatedBlocks[b.name] || b), + connections: this.props.data.contents.connections.map(c => updatedConnections[connectionToId(c)] || c), }; this.setContents(newContents); @@ -744,9 +757,9 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // gets possible channels the block can sync to getPossibleChannels = (blockName: string) => { - const relConns = this.state.cache.contents.connections.filter(c => c.to.split('.')[0] === blockName) || []; + const relConns = this.props.data.contents.connections.filter(c => c.to.split('.')[0] === blockName) || []; const possibleChannels = Array.from(new Set(relConns.map(c => c.channel))) - .reduce((o, channel) => ({...o, [channel]: this.state.cache.contents.representation.channel_colors[channel]}), {}); + .reduce((o, channel) => ({...o, [channel]: this.props.data.contents.representation.channel_colors[channel]}), {}); return possibleChannels; } @@ -756,7 +769,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // handles the left click on the svg's background // shows a menu to add/paste blocks at the click location handleSvgContextMenu = (e: any, data: { x: number, y: number, selectBlocks: (string[]) => any, clicked: string }) => { - const {blocks, datasets, analyzers, representation} = this.state.cache.contents; + const {blocks, datasets, analyzers, representation} = this.props.data.contents; const { x, y, selectBlocks, clicked } = data; let newBlockName = ''; const usedNames = this.getUsedNames(); @@ -847,7 +860,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // handles a right click on a group handleGroupContextMenu = (e: any, data: { group: string, selectBlocks: (BlockType[]) => any, clicked: string }) => { const {group, clicked, selectBlocks} = data; - const groups = this.state.cache.extraContents.groups; + const groups = this.props.data.extraContents.groups; const g = groups.find(g => g.name === group); switch(clicked){ case 'delete': @@ -873,14 +886,14 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { // gets all the used names in the toolchain: // group names & block names getUsedNames = (): string[] => [ - ...this.state.cache.extraContents.groups.map(g => g.name), - ...Object.keys(this.state.cache.contents.representation.blocks) + ...this.props.data.extraContents.groups.map(g => g.name), + ...Object.keys(this.props.data.contents.representation.blocks) ]; // handles a right click on an area selection handleAreaSelectContextMenu = (e: any, data: { selection: BlockType[], clicked: string }) => { const {selection, clicked} = data; - const groups = this.state.cache.extraContents.groups; + const groups = this.props.data.extraContents.groups; switch(clicked){ case 'createGroup': const newGroupName = generateNewKey('group', this.getUsedNames()); @@ -927,10 +940,9 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { autoLayout = async () => { // first save the tc to the BEAT prefix const serverSaveFunc = genModuleApiFuncs('toolchain').put; - await serverSaveFunc(this.state.cache); + await serverSaveFunc(this.props.data); // then fetch the new layout - const json = await fetchLayout(this.state.cache.name); - console.log(json); + const json = await fetchLayout(this.props.data.name); // transform the received layout into objs for updateBlockLocations const posRaw = json.objects @@ -972,12 +984,12 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { <Row> <GraphicalEditor interactable - repData={this.state.cache.contents.representation} - blocks={this.state.cache.contents.blocks} - datasets={this.state.cache.contents.datasets} - analyzers={this.state.cache.contents.analyzers} - connections={this.state.cache.contents.connections} - groups={this.state.cache.extraContents.groups} + repData={this.props.data.contents.representation} + blocks={this.props.data.contents.blocks} + datasets={this.props.data.contents.datasets} + analyzers={this.props.data.contents.analyzers} + connections={this.props.data.contents.connections} + groups={this.props.data.extraContents.groups} handleBlockClick={this.handleBlockClick} updateBlockLocations={this.updateBlockLocations} createConnection={(from: string, to: string, channel: string) => this.createConnections([{ from, to, channel }])} @@ -1149,7 +1161,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { const possibleChannels = active ? this.getPossibleChannels(name) : {}; return ( <EditModal - data={this.state.cache.contents[set].find(d => d.name === name)} + data={this.props.data.contents[set].find(d => d.name === name)} active={active} toggle={() => { this.setState({ @@ -1159,7 +1171,7 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { } }); }} - blockNames={Object.keys(this.state.cache.contents.representation.blocks) || []} + blockNames={Object.keys(this.props.data.contents.representation.blocks) || []} updateBlockName={(newName: string) => { this.updateBlockName(set, name, newName); }} @@ -1234,9 +1246,9 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { className='mx-auto' outline color='secondary' - onClick={() => this.props.saveFunc(this.state.cache)} + onClick={() => this.props.saveFunc(this.props.data)} > - Save Changes (Changes are <ValidSchemaBadge entity='toolchain' obj={this.state.cache} />) + Save Changes (Changes are <ValidSchemaBadge entity='toolchain' obj={this.props.data} />) </Button> </div> <Form onSubmit={(e) => e.preventDefault()}> @@ -1247,8 +1259,8 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { type='text' className='tcDescription' placeholder='Toolchain description...' - value={this.state.cache.contents['description']} - onChange={e => this.setContents({ ...this.state.cache.contents, 'description': e.target.value})} + value={this.props.data.contents['description']} + onChange={e => this.setContents({ ...this.props.data.contents, 'description': e.target.value})} /> </FormGroup> </FormGroup> @@ -1262,14 +1274,24 @@ export class ToolchainEditor extends React.PureComponent<Props, State> { } const mapStateToProps = (state, ownProps) => { + const tcs = Selectors.toolchainGet(state); const obj = { - toolchains: Selectors.toolchainGet(state), + toolchains: tcs, databases: Selectors.databaseGet(state), sets: Selectors.flattenedDatabases(state), normalAlgorithms: Selectors.normalBlocks(state), analyzerAlgorithms: Selectors.analyzerBlocks(state), + data: tcs[ownProps.index] || getValidObj() }; return obj; }; -export default connect(mapStateToProps)(ToolchainEditor); +const mapDispatchToProps = (dispatch, ownProps) => ({ + // replace the obj in the Redux store with the new object + updateFunc: (obj) => { + console.log(`dispatching for ${ obj.name }`); + dispatch(Actions[`toolchainUpdate`](obj.name, obj)); + }, +}); + +export default connect(mapStateToProps, mapDispatchToProps)(ToolchainEditor);