Skip to content

Commit

Permalink
functioning network sync
Browse files Browse the repository at this point in the history
  • Loading branch information
jthrilly committed Dec 8, 2023
1 parent ce6d850 commit fab4b55
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 119 deletions.
36 changes: 26 additions & 10 deletions app/(interview)/interview/_components/InterviewShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@ import {
SET_SERVER_SESSION,
type SetServerSessionAction,
} from '~/lib/interviewer/ducks/modules/setServerSession';
import { getStageIndex } from '~/lib/interviewer/selectors/session';
import {
getActiveSession,
getStageIndex,
} from '~/lib/interviewer/selectors/session';
import { api } from '~/trpc/client';
import { useQueryState } from 'next-usequerystate';
import usePrevious from '~/hooks/usePrevious';
import { isEqual } from 'lodash';

// The job of ServerSync is to listen to actions in the redux store, and to sync
// data with the server.
const ServerSync = ({ interviewId }: { interviewId: string }) => {
const [init, setInit] = useState(false);
// Current stage
const currentStage = useSelector(getStageIndex);
const prevCurrentStage = usePrevious(currentStage);
const { mutate: updateStage } = api.interview.sync.currentStep.useMutation();
const currentSession = useSelector(getActiveSession);
const prevCurrentSession = usePrevious(currentSession);
const { mutate: syncSessionWithServer } = api.interview.sync.useMutation();

useEffect(() => {
if (!init) {
Expand All @@ -31,14 +35,26 @@ const ServerSync = ({ interviewId }: { interviewId: string }) => {
}

if (
prevCurrentStage !== null &&
currentStage !== null &&
currentStage !== prevCurrentStage
isEqual(currentSession, prevCurrentSession) ||
!currentSession ||
!prevCurrentSession
) {
console.log(`⬆️ Syncing stage index (${currentStage}) to server...`);
updateStage({ interviewId, currentStep: currentStage });
return;
}
}, [currentStage, prevCurrentStage, updateStage, interviewId, init]);

console.log(`⬆️ Syncing session with server...`);
syncSessionWithServer({
id: interviewId,
network: currentSession.network,
currentStep: currentSession.currentStep,
});
}, [
currentSession,
prevCurrentSession,
interviewId,
syncSessionWithServer,
init,
]);

return null;
};
Expand Down
22 changes: 1 addition & 21 deletions lib/interviewer/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ const useNavigationHelpers = (
} = useSelector(getNavigationInfo);

const prevStageIndex = usePrevious(currentStep);
console.log('prevStageIndex', prevStageIndex);
const prevCurrentStage = usePrevious(currentStage);
console.log('prevCurrentStage', prevCurrentStage);

const calculateNextStage = useCallback(() => {
const nextStage = Object.keys(skipMap).find(
Expand Down Expand Up @@ -98,25 +95,8 @@ const useNavigationHelpers = (
};

useEffect(() => {
if (prevStageIndex === null) {
return;
}

if (currentStage === null) {
return;
}

dispatch(sessionActions.updateStage(currentStage));
}, [currentStage, dispatch, prevStageIndex, currentStep]);

// If currentStage is null, this is the first run. We need to set it based on
// the sessions current stage index.
// useEffect(() => {
// if (currentStage === null) {
// console.log('current stage is null, setting to', currentStep);
// setCurrentStage(currentStep);
// }
// }, [currentStage, setCurrentStage, currentStep]);
}, [currentStage, dispatch]);

return {
progress,
Expand Down
16 changes: 3 additions & 13 deletions lib/interviewer/components/NodeList.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,7 @@ const NodeList = (props) => {
const [stagger, setStagger] = useState(true);
const instanceId = useRef(v4());

// useEffect(() => {
// // When items or sorting changes, change the instance id to trigger
// // the animation.
// instanceId.current = v4();

// const sorter = createSorter(sortOrder);
// const sortedNodes = sorter(initialItems);

// setItems(sortedNodes);
// }, [initialItems, sortOrder]);

const isSource = !!find(items, [entityPrimaryKeyProperty, meta.entityPrimaryKeyProperty ?? null]);
const isValidTarget = !isSource && willAccept;
Expand All @@ -66,14 +57,13 @@ const NodeList = (props) => {
{initialItems.map((node, index) => {
const isDraggable =
!(externalData && disableDragNew) && !(disableDragNew && node.stageId !== stageId);

return (
<motion.div
layout
onClick={() => onItemClick(node)}
key={`${instanceId.current}-${node.entityPrimaryKeyProperty}`}
initial={{ opacity: 0, y: '50%', scale: 0.5 }}
animate={{ opacity: 1, y: 0, scale: 1, transition: { delay: stagger ? index * 0.1 : 0 } }}
key={`${instanceId.current}-${node[entityPrimaryKeyProperty]}`}
initial={{ opacity: 0, y: '3rem' }}
animate={{ opacity: 1, y: 0, scale: 1, transition: { delay: stagger ? index * 0.05 : 0 } }}
exit={{ opacity: 0, scale: 0, }}
>
<EnhancedNode
Expand Down
3 changes: 0 additions & 3 deletions lib/interviewer/containers/ProtocolScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ const onComplete = () => {

const ProtocolScreen = () => {
const currentStage = useSelector(getCurrentStage);
const currentStep = useSelector(getStageIndex);

console.log('protocol screen current stage', currentStep);

if (!currentStage) {
return null;
Expand Down
126 changes: 126 additions & 0 deletions lib/interviewer/ducks/modules/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { createSlice, createAction } from '@reduxjs/toolkit';
import {
entityAttributesProperty,
entityPrimaryKeyProperty,
} from '@codaco/shared-consts';
import { reject, find, isMatch, omit, keys, get } from 'lodash';
import { v4 as uuid } from 'uuid';
import { SET_SERVER_SESSION } from './setServerSession';

// Property names passed to user worker scripts
export const primaryKeyPropertyForWorker = 'networkCanvasId';
export const nodeTypePropertyForWorker = 'networkCanvasType';

// Initial network model structure
export const initialState = {
ego: {
[entityPrimaryKeyProperty]: uuid(),
[entityAttributesProperty]: {},
},
nodes: [],
edges: [],
};

// Action creators
const addNode = createAction('ADD_NODE');
const updateEgo = createAction('UPDATE_EGO');
const batchAddNodes = createAction('BATCH_ADD_NODES');
const toggleNodeAttributes = createAction('TOGGLE_NODE_ATTRIBUTES');
const updateNode = createAction('UPDATE_NODE');
const removeNode = createAction('REMOVE_NODE');
const addNodeToPrompt = createAction('ADD_NODE_TO_PROMPT');
const removeNodeFromPrompt = createAction('REMOVE_NODE_FROM_PROMPT');
const addEdge = createAction('ADD_EDGE');
const updateEdge = createAction('UPDATE_EDGE');
const toggleEdge = createAction('TOGGLE_EDGE');
const removeEdge = createAction('REMOVE_EDGE');
const addSession = createAction('ADD_SESSION');

// Reducer
const networkReducer = createSlice({
name: 'network',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(SET_SERVER_SESSION, (state, action) => {
if (!action.payload.session.network) {
return state;
}
return action.session.network;
})
.addCase(addNode, (state, action) => {
// Handle ADD_NODE logic here
})
.addCase(updateEgo, (state, action) => {
// Handle UPDATE_EGO logic here
})
.addCase(batchAddNodes, (state, action) => {
// Handle BATCH_ADD_NODES logic here
})
.addCase(toggleNodeAttributes, (state, action) => {
// Handle TOGGLE_NODE_ATTRIBUTES logic here
})
.addCase(updateNode, (state, action) => {
// Handle UPDATE_NODE logic here
})
.addCase(removeNode, (state, action) => {
// Handle REMOVE_NODE logic here
})
.addCase(addNodeToPrompt, (state, action) => {
// Handle ADD_NODE_TO_PROMPT logic here
})
.addCase(removeNodeFromPrompt, (state, action) => {
// Handle REMOVE_NODE_FROM_PROMPT logic here
})
.addCase(addEdge, (state, action) => {
// Handle ADD_EDGE logic here
})
.addCase(updateEdge, (state, action) => {
// Handle UPDATE_EDGE logic here
})
.addCase(toggleEdge, (state, action) => {
// Handle TOGGLE_EDGE logic here
})
.addCase(removeEdge, (state, action) => {
// Handle REMOVE_EDGE logic here
})
.addCase(addSession, (state, action) => {
// Handle ADD_SESSION logic here
});
},
});

export const { reducer: networkReducer, actions } = networkSlice;

const {
addNode,
updateEgo,
batchAddNodes,
toggleNodeAttributes,
updateNode,
removeNode,
addNodeToPrompt,
removeNodeFromPrompt,
addEdge,
updateEdge,
toggleEdge,
removeEdge,
addSession,
} = actions;

export {
addNode,
updateEgo,
batchAddNodes,
toggleNodeAttributes,
updateNode,
removeNode,
addNodeToPrompt,
removeNodeFromPrompt,
addEdge,
updateEdge,
toggleEdge,
removeEdge,
addSession,
};
23 changes: 6 additions & 17 deletions lib/interviewer/selectors/session.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
import type { Stage, NcNetwork } from '@codaco/shared-consts';
import type { Stage } from '@codaco/shared-consts';
import { createDeepEqualSelector } from './utils';
import { getProtocolStages } from './protocol';
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from '../store';
import type { RootState, Session } from '../store';

export type SessionState = Record<string, unknown>;

export type Session = {
protocolUid: string;
promptIndex: number;
currentStep: number;
caseId: string;
network: NcNetwork;
startedAt: Date;
updatedAt: Date;
finishedAt: Date;
exportedAt: Date;
stages?: SessionState;
};

export type SessionsState = Record<string, Session>;

export const getActiveSessionId = (state: RootState) => state.activeSessionId;
Expand All @@ -40,8 +27,8 @@ export const getLastActiveSession = createSelector(getSessions, (sessions) => {
const session = sessions[sessionId]!;
if (
!lastSessionId ||
(session.updatedAt &&
session.updatedAt > sessions[lastSessionId]!.updatedAt)
(session.lastUpdated &&
session.lastUpdated > sessions[lastSessionId]!.lastUpdated)
) {
return sessionId;
}
Expand Down Expand Up @@ -125,6 +112,8 @@ export const getSessionProgress = createSelector(
getPromptIndex,
getPromptCount,
(currentStep, stageCount, promptIndex, promptCount) => {
if (currentStep === null) return 0;

const stageProgress = currentStep / (stageCount - 1);
const stageWorth = 1 / stageCount; // The amount of progress each stage is worth

Expand Down
3 changes: 2 additions & 1 deletion lib/interviewer/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ export const store = configureStore({
});

export type Session = {
id: string;
protocolUid: string;
promptIndex: number;
currentStep: number;
caseId: string;
network: NcNetwork;
startedAt: Date;
updatedAt: Date;
lastUpdated: Date;
finishedAt: Date;
exportedAt: Date;
};
Expand Down
Loading

0 comments on commit fab4b55

Please sign in to comment.