From c7d5e20f9b681117d009aedda57a0f8c680f2976 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Wed, 16 Dec 2020 16:02:49 -0500 Subject: [PATCH 1/3] signals mostly working --- .../JoinProjectModal/JoinProjectModal.js | 1 + src/index.js | 1 + src/projects/goals/actions.js | 4 +- src/routes/Dashboard/Dashboard.js | 5 +- src/signalsHandlers.js | 169 +++++++++++------- 5 files changed, 109 insertions(+), 71 deletions(-) diff --git a/src/components/JoinProjectModal/JoinProjectModal.js b/src/components/JoinProjectModal/JoinProjectModal.js index 8060838..e603af5 100644 --- a/src/components/JoinProjectModal/JoinProjectModal.js +++ b/src/components/JoinProjectModal/JoinProjectModal.js @@ -27,6 +27,7 @@ export default function JoinProjectModal({ setValidatingSecret(true) try { const projectExists = await onJoinProject(projectSecret) + console.log(projectExists) if (!projectExists) { setInvalidText('project does not exist or peers could not be found') } else { diff --git a/src/index.js b/src/index.js index a0d2d61..972b6fc 100644 --- a/src/index.js +++ b/src/index.js @@ -48,6 +48,7 @@ let store = createStore( /* preloadedState, */ composeEnhancers(applyMiddleware(...middleware)) ) +// initialize the appWs with the signals handler getAppWs(signalsHandlers(store)).then(async client => { const profilesInfo = await client.appInfo({ installed_app_id: PROFILES_APP_ID }) const [cellId, _] = profilesInfo.cell_data.find( diff --git a/src/projects/goals/actions.js b/src/projects/goals/actions.js index 615c0ae..fffda39 100644 --- a/src/projects/goals/actions.js +++ b/src/projects/goals/actions.js @@ -24,10 +24,12 @@ const [ ] = createCrudActionCreators(PROJECTS_ZOME_NAME, 'goal') export { + // standard crud createGoal, - createGoalWithEdge, fetchGoals, updateGoal, archiveGoal, + // non-standard + createGoalWithEdge, archiveGoalFully, } \ No newline at end of file diff --git a/src/routes/Dashboard/Dashboard.js b/src/routes/Dashboard/Dashboard.js index a152334..04221c5 100644 --- a/src/routes/Dashboard/Dashboard.js +++ b/src/routes/Dashboard/Dashboard.js @@ -234,7 +234,6 @@ async function joinProject(passphrase, dispatch) { payload: null, provenance: getAgentPubKey(), // FIXME: this will need correcting after holochain changes this }) - console.log(projectMeta) return true } catch (e) { console.log(e) @@ -272,9 +271,7 @@ function mapDispatchToProps(dispatch) { } await createProject(passphrase, projectMeta, dispatch) }, - joinProject: async passphrase => { - await joinProject(passphrase, dispatch) - }, + joinProject: passphrase => joinProject(passphrase, dispatch), } } diff --git a/src/signalsHandlers.js b/src/signalsHandlers.js index fd2c999..33b130a 100644 --- a/src/signalsHandlers.js +++ b/src/signalsHandlers.js @@ -6,19 +6,15 @@ the same events types, because their payload signatures match, and the reducers handle them the same way */ -import { createEdge, archiveEdge } from './projects/edges/actions' -import { createGoal, archiveGoalFully } from './projects/goals/actions' -import { createGoalVote, archiveGoalVote } from './projects/goal-votes/actions' -import { - createGoalMember, - archiveGoalMember, -} from './projects/goal-members/actions' -import { - createGoalComment, - archiveGoalComment, -} from './projects/goal-comments/actions' +import * as edgeActions from './projects/edges/actions' +import * as goalActions from './projects/goals/actions' +import * as goalVoteActions from './projects/goal-votes/actions' +import * as goalMemberActions from './projects/goal-members/actions' +import * as goalCommentActions from './projects/goal-comments/actions' +import * as entryPointActions from './projects/entry-points/actions' import { setMember } from './projects/members/actions' import { setAgent } from './agents/actions' +import { cellIdToString } from 'connoropolous-hc-redux-middleware' // We directly use the 'success' type, since these actions // have already succeeded on another machine, and we're just reflecting them locally @@ -26,81 +22,122 @@ function createSignalAction(holochainAction, cellId, payload) { return { type: holochainAction.success().type, payload, + meta: { + cellIdString: cellIdToString(cellId), + }, } } +// all possible values of `payload.action` off a signal +const ActionType = { + Create: 'create', + Update: 'update', + Delete: 'delete', +} + +// all possible values of `payload.entry_type` off a signal +const SignalType = { + // Profiles Zome + Agent: 'agent', + // Projects Zome + Edge: 'edge', + EntryPoint: 'entry_point', + Goal: 'goal', + // custom signal type for a goal_with_edge + // this is because it's important to the UI to receive both + // the new goal, and the edge, at the same moment + GoalWithEdge: 'goal_with_edge', + // custom signal type for goal_fully_archived + // this is because it's important to the UI to receive + // both the archived goal, and everything connected to it that + // has archived at the same time + ArchiveGoalFully: 'archive_goal_fully', + GoalComment: 'goal_comment', + GoalMember: 'goal_member', + GoalVote: 'goal_vote', + Member: 'member', + ProjectMeta: 'project_meta', +} + +const crudActionSets = { + Edge: edgeActions, + Goal: goalActions, + GoalVote: goalVoteActions, + GoalMember: goalMemberActions, + GoalComment: goalCommentActions, + EntryPoint: entryPointActions, +} +const crudTypes = { + edge: 'Edge', + goal: 'Goal', + goal_vote: 'GoalVote', + goal_member: 'GoalMember', + goal_comment: 'GoalComment', + entry_point: 'EntryPoint', +} + +// entryTypeName = CamelCase +const pickCrudAction = (entryTypeName, actionType) => { + // these all follow a pattern, on account of the fact that they used + // createCrudActionCreators to set themselves up + let actionPrefix + switch (actionType) { + case ActionType.Create: + actionPrefix = 'create' + break + case ActionType.Update: + actionPrefix = 'update' + break + case ActionType.Delete: + actionPrefix = 'archive' + break + default: + throw new Error('unknown actionType') + } + const actionSet = crudActionSets[entryTypeName] + // such as `createGoalComment` + const actionName = `${actionPrefix}${entryTypeName}` + return actionSet[actionName] +} + export default store => signal => { console.log(signal) - return + const { cellId, payload } = signal.data - const cellId = rawSignal.instance_id - const signalContent = rawSignal.signal - let signalArgs - try { - signalArgs = JSON.parse(signalContent.arguments) - } catch (e) { - console.log(e, signalContent) + // switch to CamelCasing if defined + const crudType = crudTypes[payload.entry_type] + if (crudType) { + const action = pickCrudAction(crudType, payload.action) + store.dispatch(createSignalAction(action, cellId, payload)) + // we captured the action for this signal, so early exit return } - switch (signalContent.name) { - case 'new_agent': - const { agent } = signalArgs + + // otherwise use non-crud actions + switch (payload.entry_type) { + // profiles zome + case SignalType.Agent: // this one is different than the rest on purpose // there's no "local action" equivalent - store.dispatch(setAgent(agent)) + store.dispatch(setAgent(payload)) break - case 'new_member': - const { member } = signalArgs + // projects zome + case SignalType.Member: // this one is different than the rest on purpose // there's no "local action" equivalent - store.dispatch(setMember(cellId, member)) - break - case 'goal_maybe_with_edge': - const { goal } = signalArgs - store.dispatch(createSignalAction(createGoal, cellId, goal)) - break - case 'goal_archived': - const { archived } = signalArgs - store.dispatch(createSignalAction(archiveGoalFully, cellId, archived)) - break - // covers create and update cases - case 'edge': - const { edge } = signalArgs - store.dispatch(createSignalAction(createEdge, cellId, edge)) + store.dispatch(setMember(cellIdToString(cellId), payload.data)) break - case 'edge_archived': + case SignalType.GoalWithEdge: store.dispatch( - createSignalAction(archiveEdge, cellId, signalArgs.address) + createSignalAction(goalActions.createGoalWithEdge, cellId, payload.data) ) break - case 'goal_comment': - const { goalComment } = signalArgs - store.dispatch(createSignalAction(createGoalComment, cellId, goalComment)) - break - case 'goal_comment_archived': - store.dispatch( - createSignalAction(archiveGoalComment, cellId, signalArgs.address) - ) - break - case 'goal_member': - const { goalMember } = signalArgs - store.dispatch(createSignalAction(createGoalMember, cellId, goalMember)) - break - case 'goal_member_archived': - store.dispatch( - createSignalAction(archiveGoalMember, cellId, signalArgs.address) - ) - break - case 'goal_vote': - const { goalVote } = signalArgs - store.dispatch(createSignalAction(createGoalVote, cellId, goalVote)) - break - case 'goal_vote_archived': + case SignalType.ArchiveGoalFully: store.dispatch( - createSignalAction(archiveGoalVote, cellId, signalArgs.address) + createSignalAction(goalActions.archiveGoalFully, cellId, payload.data) ) break default: - console.log('unrecognised signal type received: ', signalContent.name) + console.log('unrecognised entry_type received: ', payload.entry_type) } } From 8a60c4d9752b0a35925e82eab0bcc311927c5964 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Thu, 17 Dec 2020 12:15:00 -0500 Subject: [PATCH 2/3] fix-signals and projects --- src/cells/actions.js | 10 + src/cells/reducer.js | 7 +- src/drawing/index.js | 432 +++++++++++----------- src/projects/members/actions.js | 4 +- src/projects/members/reducer.js | 12 +- src/projects/project-meta/reducer.js | 19 +- src/routes/Dashboard/Dashboard.js | 37 +- src/routes/ProjectView/MapView/MapView.js | 6 +- src/signalsHandlers.js | 2 +- 9 files changed, 286 insertions(+), 243 deletions(-) diff --git a/src/cells/actions.js b/src/cells/actions.js index bfa9e19..e839320 100644 --- a/src/cells/actions.js +++ b/src/cells/actions.js @@ -7,6 +7,7 @@ const SET_PROFILES_CELL_ID = 'SET_PROFILES_CELL_ID' const SET_PROJECTS_CELL_IDS = 'SET_PROJECTS_CELL_IDS' +const JOIN_PROJECT_CELL_ID = 'JOIN_PROJECT_CELL_ID' /* action creator functions */ @@ -24,9 +25,18 @@ const setProjectsCellIds = cellIds => { } } +const joinProjectCellId = cellId => { + return { + type: JOIN_PROJECT_CELL_ID, + payload: cellId, + } +} + export { SET_PROFILES_CELL_ID, SET_PROJECTS_CELL_IDS, + JOIN_PROJECT_CELL_ID, setProfilesCellId, setProjectsCellIds, + joinProjectCellId } diff --git a/src/cells/reducer.js b/src/cells/reducer.js index 8b7510e..2c92950 100644 --- a/src/cells/reducer.js +++ b/src/cells/reducer.js @@ -2,7 +2,7 @@ import _ from 'lodash' import { createProjectMeta, } from '../projects/project-meta/actions' -import { SET_PROFILES_CELL_ID, SET_PROJECTS_CELL_IDS } from './actions' +import { SET_PROFILES_CELL_ID, SET_PROJECTS_CELL_IDS, JOIN_PROJECT_CELL_ID } from './actions' const defaultState = { profiles: null, @@ -27,6 +27,11 @@ export default function (state = defaultState, action) { ...state, projects: [...state.projects, meta.cellIdString], } + case JOIN_PROJECT_CELL_ID: + return { + ...state, + projects: [...state.projects, payload], + } default: return state } diff --git a/src/drawing/index.js b/src/drawing/index.js index 70856da..b277f2f 100644 --- a/src/drawing/index.js +++ b/src/drawing/index.js @@ -69,241 +69,249 @@ function render(store, canvas) { const edges = state.projects.edges[projectId] const goalMembers = state.projects.goalMembers[projectId] const entryPoints = state.projects.entryPoints[projectId] - if (!goals || !edges || !goalMembers || !entryPoints) return - // converts the goals object to an array - const goalsAsArray = Object.keys(goals).map(address => goals[address]) - // convert the edges object to an array - const edgesAsArray = Object.keys(edges).map(address => edges[address]) + // draw things relating to the project, if the project has fully loaded + if (goals && edges && goalMembers && entryPoints) { + // converts the goals object to an array + const goalsAsArray = Object.keys(goals).map(address => goals[address]) + // convert the edges object to an array + const edgesAsArray = Object.keys(edges).map(address => edges[address]) - const coordinates = layoutFormula(state.ui.screensize.width, state) + const coordinates = layoutFormula(state.ui.screensize.width, state) - const activeEntryPointsObjects = activeEntryPoints.map( - entryPointAddress => entryPoints[entryPointAddress] - ) - drawEntryPoints( - ctx, - activeEntryPointsObjects, - goals, - edgesAsArray, - coordinates - ) - - // render each edge to the canvas, basing it off the rendering coordinates of the parent and child nodes - edgesAsArray.forEach(function (edge) { - const childCoords = coordinates[edge.child_address] - const parentCoords = coordinates[edge.parent_address] - const parentGoalText = goals[edge.parent_address] - ? goals[edge.parent_address].content - : '' - if (childCoords && parentCoords) { - const [edge1port, edge2port] = calculateEdgeCoordsByGoalCoords( - childCoords, - parentCoords, - parentGoalText, - ctx - ) - const isHovered = state.ui.hover.hoveredEdge === edge.address - const isSelected = state.ui.selection.selectedEdges.includes(edge.address) - drawEdge(edge1port, edge2port, ctx, isHovered, isSelected) - } - }) - - // create layers behind and in front of the editing highlight overlay - const unselectedGoals = goalsAsArray.filter(goal => { - return ( - state.ui.selection.selectedGoals.indexOf(goal.address) === -1 && - state.ui.goalForm.editAddress !== goal.address - ) - }) - const selectedGoals = goalsAsArray.filter(goal => { - return ( - state.ui.selection.selectedGoals.indexOf(goal.address) > -1 && - state.ui.goalForm.editAddress !== goal.address + const activeEntryPointsObjects = activeEntryPoints.map( + entryPointAddress => entryPoints[entryPointAddress] ) - }) - - // render each unselected goal to the canvas - unselectedGoals.forEach(goal => { - // use the set of coordinates at the same index - // in the coordinates array - const isHovered = state.ui.hover.hoveredGoal === goal.address - const isSelected = false - const isEditing = false - const membersOfGoal = Object.keys(goalMembers) - .map(address => goalMembers[address]) - .filter(goalMember => goalMember.goal_address === goal.address) - .map(goalMember => state.agents[goalMember.agent_address]) - drawGoalCard( - goal, - membersOfGoal, - coordinates[goal.address], - isEditing, - '', - isSelected, - isHovered, - ctx - ) - }) - if ( - state.ui.keyboard.shiftKeyDown && - state.ui.mouse.mousedown && - state.ui.mouse.coordinate.x !== 0 - ) { - drawSelectBox( - state.ui.mouse.coordinate, - state.ui.mouse.size, - canvas.getContext('2d') - ) - } - // draw the editing highlight overlay - /* if shift key not held down and there are more than 1 Goals selected */ - if ( - state.ui.goalForm.editAddress || - (state.ui.selection.selectedGoals.length > 1 && - !state.ui.keyboard.shiftKeyDown) - ) { - // counteract the translation - ctx.save() - ctx.setTransform(1, 0, 0, 1, 0, 0) - drawOverlay( + drawEntryPoints( ctx, - 0, - 0, - state.ui.screensize.width, - state.ui.screensize.height - ) - ctx.restore() - } - - // render each selected goal to the canvas - selectedGoals.forEach(goal => { - // use the set of coordinates at the same index - // in the coordinates array - const isHovered = state.ui.hover.hoveredGoal === goal.address - const isSelected = true - const isEditing = false - const membersOfGoal = Object.keys(goalMembers) - .map(address => goalMembers[address]) - .filter(goalMember => goalMember.goal_address === goal.address) - .map(goalMember => state.agents[goalMember.agent_address]) - drawGoalCard( - goal, - membersOfGoal, - coordinates[goal.address], - isEditing, - '', - isSelected, - isHovered, - ctx + activeEntryPointsObjects, + goals, + edgesAsArray, + coordinates ) - }) - // render the edge that is pending to be created to the open goal form - if (state.ui.goalForm.isOpen) { - if (state.ui.goalForm.parentAddress) { - const parentCoords = coordinates[state.ui.goalForm.parentAddress] - const newGoalCoords = { - x: state.ui.goalForm.xLoc, - y: state.ui.goalForm.yLoc, - } - const parentGoalText = state.projects.goals[ - state.ui.goalForm.parentAddress - ] - ? goals[state.ui.goalForm.parentAddress].content + // render each edge to the canvas, basing it off the rendering coordinates of the parent and child nodes + edgesAsArray.forEach(function (edge) { + const childCoords = coordinates[edge.child_address] + const parentCoords = coordinates[edge.parent_address] + const parentGoalText = goals[edge.parent_address] + ? goals[edge.parent_address].content : '' - const [edge1port, edge2port] = calculateEdgeCoordsByGoalCoords( - newGoalCoords, - parentCoords, - parentGoalText, + if (childCoords && parentCoords) { + const [edge1port, edge2port] = calculateEdgeCoordsByGoalCoords( + childCoords, + parentCoords, + parentGoalText, + ctx + ) + const isHovered = state.ui.hover.hoveredEdge === edge.address + const isSelected = state.ui.selection.selectedEdges.includes( + edge.address + ) + drawEdge(edge1port, edge2port, ctx, isHovered, isSelected) + } + }) + + // create layers behind and in front of the editing highlight overlay + const unselectedGoals = goalsAsArray.filter(goal => { + return ( + state.ui.selection.selectedGoals.indexOf(goal.address) === -1 && + state.ui.goalForm.editAddress !== goal.address + ) + }) + const selectedGoals = goalsAsArray.filter(goal => { + return ( + state.ui.selection.selectedGoals.indexOf(goal.address) > -1 && + state.ui.goalForm.editAddress !== goal.address + ) + }) + + // render each unselected goal to the canvas + unselectedGoals.forEach(goal => { + // use the set of coordinates at the same index + // in the coordinates array + const isHovered = state.ui.hover.hoveredGoal === goal.address + const isSelected = false + const isEditing = false + const membersOfGoal = Object.keys(goalMembers) + .map(address => goalMembers[address]) + .filter(goalMember => goalMember.goal_address === goal.address) + .map(goalMember => state.agents[goalMember.agent_address]) + drawGoalCard( + goal, + membersOfGoal, + coordinates[goal.address], + isEditing, + '', + isSelected, + isHovered, ctx ) - drawEdge(edge1port, edge2port, ctx) + }) + if ( + state.ui.keyboard.shiftKeyDown && + state.ui.mouse.mousedown && + state.ui.mouse.coordinate.x !== 0 + ) { + drawSelectBox( + state.ui.mouse.coordinate, + state.ui.mouse.size, + canvas.getContext('2d') + ) + } + // draw the editing highlight overlay + /* if shift key not held down and there are more than 1 Goals selected */ + if ( + state.ui.goalForm.editAddress || + (state.ui.selection.selectedGoals.length > 1 && + !state.ui.keyboard.shiftKeyDown) + ) { + // counteract the translation + ctx.save() + ctx.setTransform(1, 0, 0, 1, 0, 0) + drawOverlay( + ctx, + 0, + 0, + state.ui.screensize.width, + state.ui.screensize.height + ) + ctx.restore() } - } - - // render the edge that is pending to be created between existing Goals - if (state.ui.edgeConnector.fromAddress) { - const { fromAddress, relation, toAddress } = state.ui.edgeConnector - const { liveCoordinate } = state.ui.mouse - const fromCoords = coordinates[fromAddress] - const fromContent = goals[fromAddress].content - const [ - fromAsChildCoord, - fromAsParentCoord, - ] = calculateEdgeCoordsByGoalCoords( - fromCoords, - fromCoords, - fromContent, - ctx - ) - // if there's a goal this is pending - // as being "to", then we will be drawing the edge to its correct - // upper or lower port - // the opposite of whichever the "from" port is connected to - let toCoords, toContent, toAsChildCoord, toAsParentCoord - if (toAddress) { - toCoords = coordinates[toAddress] - toContent = goals[toAddress].content - ;[toAsChildCoord, toAsParentCoord] = calculateEdgeCoordsByGoalCoords( - toCoords, - toCoords, - toContent, + // render each selected goal to the canvas + selectedGoals.forEach(goal => { + // use the set of coordinates at the same index + // in the coordinates array + const isHovered = state.ui.hover.hoveredGoal === goal.address + const isSelected = true + const isEditing = false + const membersOfGoal = Object.keys(goalMembers) + .map(address => goalMembers[address]) + .filter(goalMember => goalMember.goal_address === goal.address) + .map(goalMember => state.agents[goalMember.agent_address]) + drawGoalCard( + goal, + membersOfGoal, + coordinates[goal.address], + isEditing, + '', + isSelected, + isHovered, ctx ) + }) + + // render the edge that is pending to be created to the open goal form + if (state.ui.goalForm.isOpen) { + if (state.ui.goalForm.parentAddress) { + const parentCoords = coordinates[state.ui.goalForm.parentAddress] + const newGoalCoords = { + x: state.ui.goalForm.xLoc, + y: state.ui.goalForm.yLoc, + } + const parentGoalText = state.projects.goals[ + state.ui.goalForm.parentAddress + ] + ? goals[state.ui.goalForm.parentAddress].content + : '' + const [edge1port, edge2port] = calculateEdgeCoordsByGoalCoords( + newGoalCoords, + parentCoords, + parentGoalText, + ctx + ) + drawEdge(edge1port, edge2port, ctx) + } } - // in drawEdge, it draws at exactly the two coordinates given, - // so we could pass them in either order/position - const fromEdgeCoord = - relation === RELATION_AS_PARENT ? fromAsParentCoord : fromAsChildCoord + // render the edge that is pending to be created between existing Goals + if (state.ui.edgeConnector.fromAddress) { + const { fromAddress, relation, toAddress } = state.ui.edgeConnector + const { liveCoordinate } = state.ui.mouse + const fromCoords = coordinates[fromAddress] + const fromContent = goals[fromAddress].content + const [ + fromAsChildCoord, + fromAsParentCoord, + ] = calculateEdgeCoordsByGoalCoords( + fromCoords, + fromCoords, + fromContent, + ctx + ) - // use the current mouse coordinate position, liveCoordinate, by default - let toEdgeCoord = liveCoordinate + // if there's a goal this is pending + // as being "to", then we will be drawing the edge to its correct + // upper or lower port + // the opposite of whichever the "from" port is connected to + let toCoords, toContent, toAsChildCoord, toAsParentCoord + if (toAddress) { + toCoords = coordinates[toAddress] + toContent = goals[toAddress].content + ;[toAsChildCoord, toAsParentCoord] = calculateEdgeCoordsByGoalCoords( + toCoords, + toCoords, + toContent, + ctx + ) + } - // use the coordinates relating to a Goal which it is pending that - // this edge will connect the "from" Goal "to" - if (toAddress) { - toEdgeCoord = - relation === RELATION_AS_PARENT ? toAsChildCoord : toAsParentCoord - } + // in drawEdge, it draws at exactly the two coordinates given, + // so we could pass them in either order/position + const fromEdgeCoord = + relation === RELATION_AS_PARENT ? fromAsParentCoord : fromAsChildCoord + + // use the current mouse coordinate position, liveCoordinate, by default + let toEdgeCoord = liveCoordinate + + // use the coordinates relating to a Goal which it is pending that + // this edge will connect the "from" Goal "to" + if (toAddress) { + toEdgeCoord = + relation === RELATION_AS_PARENT ? toAsChildCoord : toAsParentCoord + } - if (relation === RELATION_AS_CHILD) { - fromEdgeCoord.y = fromEdgeCoord.y - CONNECTOR_VERTICAL_SPACING - // only modify if we're dealing with an actual goal being connected to - if (toAddress) toEdgeCoord.y = toEdgeCoord.y + CONNECTOR_VERTICAL_SPACING - } else if (relation === RELATION_AS_PARENT) { - fromEdgeCoord.y = fromEdgeCoord.y + CONNECTOR_VERTICAL_SPACING - // only modify if we're dealing with an actual goal being connected to - if (toAddress) toEdgeCoord.y = toEdgeCoord.y - CONNECTOR_VERTICAL_SPACING + if (relation === RELATION_AS_CHILD) { + fromEdgeCoord.y = fromEdgeCoord.y - CONNECTOR_VERTICAL_SPACING + // only modify if we're dealing with an actual goal being connected to + if (toAddress) + toEdgeCoord.y = toEdgeCoord.y + CONNECTOR_VERTICAL_SPACING + } else if (relation === RELATION_AS_PARENT) { + fromEdgeCoord.y = fromEdgeCoord.y + CONNECTOR_VERTICAL_SPACING + // only modify if we're dealing with an actual goal being connected to + if (toAddress) + toEdgeCoord.y = toEdgeCoord.y - CONNECTOR_VERTICAL_SPACING + } + + drawEdge(fromEdgeCoord, toEdgeCoord, ctx) } - drawEdge(fromEdgeCoord, toEdgeCoord, ctx) + // draw the editing goal in front of the overlay as well + if (state.ui.goalForm.editAddress) { + // editing an existing Goal + const editingGoal = goals[state.ui.goalForm.editAddress] + const isEditing = true + const editText = state.ui.goalForm.content + const membersOfGoal = Object.keys(goalMembers) + .map(address => goalMembers[address]) + .filter(goalMember => goalMember.goal_address === editingGoal.address) + .map(goalMember => state.agents[goalMember.agent_address]) + drawGoalCard( + editingGoal, + membersOfGoal, + coordinates[editingGoal.address], + isEditing, + editText, + false, + false, + ctx + ) + } } - // draw the editing goal in front of the overlay as well - if (state.ui.goalForm.editAddress) { - // editing an existing Goal - const editingGoal = goals[state.ui.goalForm.editAddress] - const isEditing = true - const editText = state.ui.goalForm.content - const membersOfGoal = Object.keys(goalMembers) - .map(address => goalMembers[address]) - .filter(goalMember => goalMember.goal_address === editingGoal.address) - .map(goalMember => state.agents[goalMember.agent_address]) - drawGoalCard( - editingGoal, - membersOfGoal, - coordinates[editingGoal.address], - isEditing, - editText, - false, - false, - ctx - ) - } else if (state.ui.goalForm.isOpen) { - // creating a new Goal + // creating a new Goal + if (!state.ui.goalForm.editAddress && state.ui.goalForm.isOpen) { const isHovered = false const isSelected = false const isEditing = true diff --git a/src/projects/members/actions.js b/src/projects/members/actions.js index 908a73b..bc7aa9c 100644 --- a/src/projects/members/actions.js +++ b/src/projects/members/actions.js @@ -15,11 +15,11 @@ const FETCH_MEMBERS = 'fetch_members' /* action creator functions */ -const setMember = (cellId, member) => { +const setMember = (cellIdString, member) => { return { type: SET_MEMBER, payload: { - cellId, + cellIdString, member, }, } diff --git a/src/projects/members/reducer.js b/src/projects/members/reducer.js index 02123a3..98e559e 100644 --- a/src/projects/members/reducer.js +++ b/src/projects/members/reducer.js @@ -8,22 +8,22 @@ const defaultState = {} export default function (state = defaultState, action) { const { payload, type } = action - let cellId + let cellIdString switch (type) { // FETCH_MEMBERS case fetchMembers.success().type: - cellId = action.meta.cellIdString + cellIdString = action.meta.cellIdString return { ...state, - [cellId]: _.keyBy(payload, 'address'), + [cellIdString]: _.keyBy(payload, 'address'), } // SET_MEMBER case SET_MEMBER: - cellId = payload.cellId + cellIdString = payload.cellIdString return { ...state, - [cellId]: { - ...state[cellId], + [cellIdString]: { + ...state[cellIdString], [payload.member.address]: payload.member, }, } diff --git a/src/projects/project-meta/reducer.js b/src/projects/project-meta/reducer.js index 42aa687..2fa4a9d 100644 --- a/src/projects/project-meta/reducer.js +++ b/src/projects/project-meta/reducer.js @@ -11,19 +11,24 @@ import { const defaultState = {} export default function (state = defaultState, action) { - const { - payload, - type, - } = action - + const { payload, type } = action switch (type) { + // act optimistically, because holochain is taking + // 18 seconds for this call to respond + case 'acorn_projects/create_project_meta': + return { + ...state, + [action.meta.cellIdString]: { + ...payload, + address: 'temp' + Math.random(), + }, + } case createProjectMeta.success().type: case fetchProjectMeta.success().type: case updateProjectMeta.success().type: - let { cellIdString } = action.meta return { ...state, - [cellIdString]: { + [action.meta.cellIdString]: { ...payload.entry, address: payload.address, }, diff --git a/src/routes/Dashboard/Dashboard.js b/src/routes/Dashboard/Dashboard.js index 04221c5..bc635a2 100644 --- a/src/routes/Dashboard/Dashboard.js +++ b/src/routes/Dashboard/Dashboard.js @@ -17,7 +17,7 @@ import { PROJECTS_DNA_PATH, PROJECTS_ZOME_NAME } from '../../holochainConfig' import { passphraseToUuid } from '../../secrets' import { getAdminWs, getAppWs, getAgentPubKey } from '../../hcWebsockets' import { fetchEntryPoints } from '../../projects/entry-points/actions' -import { fetchMembers } from '../../projects/members/actions' +import { fetchMembers, setMember } from '../../projects/members/actions' import { createProjectMeta, fetchProjectMeta, @@ -25,8 +25,7 @@ import { import selectEntryPoints from '../../projects/entry-points/select' import DashboardListProject from './DashboardListProject' -import { getProjectCellIdStrings } from '../../projectAppIds' -import { setProjectsCellIds } from '../../cells/actions' +import { joinProjectCellId } from '../../cells/actions' function Dashboard({ agentAddress, @@ -206,12 +205,21 @@ async function installProjectApp(passphrase) { return installedApp } -async function createProject(passphrase, projectMeta, dispatch) { +async function createProject(passphrase, projectMeta, agentAddress, dispatch) { const installedApp = await installProjectApp(passphrase) const cellIdString = cellIdToString(installedApp.cell_data[0][0]) + // because we are acting optimistically, + // because holochain is taking 18 s to respond to this first call + // we will directly set ourselves as a member of this cell + await dispatch( + setMember(cellIdString, { address: agentAddress }) + ) + const b1 = Date.now() await dispatch( createProjectMeta.create({ cellIdString, payload: projectMeta }) ) + const b2 = Date.now() + console.log('duration in MS over createProject ', b2 - b1) } async function joinProject(passphrase, dispatch) { @@ -222,11 +230,12 @@ async function joinProject(passphrase, dispatch) { // remove the instance again immediately const installedApp = await installProjectApp(passphrase) const cellId = installedApp.cell_data[0][0] + const cellIdString = cellIdToString(installedApp.cell_data[0][0]) const appWs = await getAppWs() // wait 10 seconds for initial sync - await new Promise((resolve) => setTimeout(resolve, 10000)) + await new Promise(resolve => setTimeout(resolve, 10000)) try { - const projectMeta = await appWs.callZome({ + await appWs.callZome({ cap: null, cell_id: cellId, zome_name: PROJECTS_ZOME_NAME, @@ -234,15 +243,19 @@ async function joinProject(passphrase, dispatch) { payload: null, provenance: getAgentPubKey(), // FIXME: this will need correcting after holochain changes this }) + await dispatch(joinProjectCellId(cellIdString)) return true } catch (e) { - console.log(e) // deactivate app const adminWs = await getAdminWs() - await adminWs.deactivateApp({ installed_app_id: installedApp.installed_app_id }) - if (e.type === 'error' - && e.data.type === 'ribosome_error' - && e.data.data.includes('no project meta exists')) { + await adminWs.deactivateApp({ + installed_app_id: installedApp.installed_app_id, + }) + if ( + e.type === 'error' && + e.data.type === 'ribosome_error' && + e.data.data.includes('no project meta exists') + ) { return false } else { throw e @@ -269,7 +282,7 @@ function mapDispatchToProps(dispatch) { creator_address: agentAddress, created_at: Date.now(), } - await createProject(passphrase, projectMeta, dispatch) + await createProject(passphrase, projectMeta, agentAddress, dispatch) }, joinProject: passphrase => joinProject(passphrase, dispatch), } diff --git a/src/routes/ProjectView/MapView/MapView.js b/src/routes/ProjectView/MapView/MapView.js index 54109a7..e3edbe7 100644 --- a/src/routes/ProjectView/MapView/MapView.js +++ b/src/routes/ProjectView/MapView/MapView.js @@ -130,8 +130,10 @@ function mapStateToProps(state) { // TODO: make this also based on whether the user has just registered (created their profile) showEmptyState: !!state.agentAddress && - state.projects.goals[projectId] && - Object.values(state.projects.goals[projectId]).length === 0, + ((state.projects.goals[projectId] && + Object.values(state.projects.goals[projectId]).length === 0) || + // project is loading + !state.projects.goals[projectId]), } } diff --git a/src/signalsHandlers.js b/src/signalsHandlers.js index 33b130a..b6a78b0 100644 --- a/src/signalsHandlers.js +++ b/src/signalsHandlers.js @@ -108,7 +108,7 @@ export default store => signal => { const crudType = crudTypes[payload.entry_type] if (crudType) { const action = pickCrudAction(crudType, payload.action) - store.dispatch(createSignalAction(action, cellId, payload)) + store.dispatch(createSignalAction(action, cellId, payload.data)) // we captured the action for this signal, so early exit return } From 5ac6cf6fc383323eb0bec5317e80f686dc7ad456 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Thu, 17 Dec 2020 12:18:50 -0500 Subject: [PATCH 3/3] Update src/components/JoinProjectModal/JoinProjectModal.js --- src/components/JoinProjectModal/JoinProjectModal.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/JoinProjectModal/JoinProjectModal.js b/src/components/JoinProjectModal/JoinProjectModal.js index e603af5..8060838 100644 --- a/src/components/JoinProjectModal/JoinProjectModal.js +++ b/src/components/JoinProjectModal/JoinProjectModal.js @@ -27,7 +27,6 @@ export default function JoinProjectModal({ setValidatingSecret(true) try { const projectExists = await onJoinProject(projectSecret) - console.log(projectExists) if (!projectExists) { setInvalidText('project does not exist or peers could not be found') } else {