chore: create helper function for interacting with apollo cache

This commit is contained in:
Jordan Knott 2020-06-23 16:55:17 -05:00
parent 57382de9d0
commit a589fbe399
7 changed files with 258 additions and 307 deletions

View File

@ -137,11 +137,11 @@ const ProjectFinder = () => {
return (
<>
{projectTeams.map(team => (
<TeamContainer>
<TeamContainer key={team.id}>
<TeamTitle>{team.name}</TeamTitle>
<TeamProjects>
{team.projects.map((project, idx) => (
<TeamProjectContainer>
<TeamProjectContainer key={project.id}>
<TeamProjectLink to={`/projects/${project.id}`}>
<TeamProjectBackground color={colors[idx % 5]} />
<TeamProjectAvatar color={colors[idx % 5]} />

View File

@ -18,6 +18,7 @@ import {
useUpdateTaskChecklistItemNameMutation,
useCreateTaskChecklistItemMutation,
FindTaskDocument,
FindTaskQuery,
} from 'shared/generated/graphql';
import UserIDContext from 'App/context';
import MiniProfile from 'shared/components/MiniProfile';
@ -27,9 +28,10 @@ import styled from 'styled-components';
import Button from 'shared/components/Button';
import Input from 'shared/components/Input';
import { useForm } from 'react-hook-form';
import updateApolloCache from 'shared/utils/cache';
const calculateChecklistBadge = (draftState: any) => {
const total = draftState.checklists.reduce((prev: any, next: any) => {
const calculateChecklistBadge = (checklists: Array<TaskChecklist>) => {
const total = checklists.reduce((prev: any, next: any) => {
return (
prev +
next.items.reduce((innerPrev: any, _item: any) => {
@ -37,7 +39,7 @@ const calculateChecklistBadge = (draftState: any) => {
}, 0)
);
}, 0);
const complete = draftState.checklists.reduce(
const complete = checklists.reduce(
(prev: any, next: any) =>
prev +
next.items.reduce((innerPrev: any, item: any) => {
@ -131,171 +133,104 @@ const Details: React.FC<DetailsProps> = ({
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
const [setTaskChecklistItemComplete] = useSetTaskChecklistItemCompleteMutation({
update: client => {
const cacheData: any = client.readQuery({
query: FindTaskDocument,
variables: { taskID },
});
const newData = produce(cacheData.findTask, (draftState: any) => {
const { complete, total } = calculateChecklistBadge(draftState);
if (!draftState.badges) {
draftState.badges = {
checklist: {},
};
}
draftState.badges.checklist = {
__typename: 'ChecklistBadge',
complete,
total,
};
});
client.writeQuery({
query: FindTaskDocument,
variables: { taskID },
data: {
findTask: newData,
},
});
updateApolloCache<FindTaskQuery>(
client,
FindTaskDocument,
cache =>
produce(cache, draftCache => {
const { complete, total } = calculateChecklistBadge(draftCache.findTask.checklists);
draftCache.findTask.badges.checklist = {
__typename: 'ChecklistBadge',
complete,
total,
};
}),
{ taskID },
);
},
});
const [deleteTaskChecklist] = useDeleteTaskChecklistMutation({
update: (client, deleteData) => {
const cacheData: any = client.readQuery({
query: FindTaskDocument,
variables: { taskID },
});
console.log(deleteData);
const newData = produce(cacheData.findTask, (draftState: any) => {
draftState.checklists = cacheData.findTask.checklists.filter(
(checklist: any) => checklist.id !== deleteData.data.deleteTaskChecklist.taskChecklist.id,
);
const { complete, total } = calculateChecklistBadge(draftState);
if (!draftState.badges) {
draftState.badges = {
checklist: {},
};
}
draftState.badges.checklist = {
__typename: 'ChecklistBadge',
complete,
total,
};
if (complete === 0 && total === 0) {
draftState.badges.checklist = null;
}
});
client.writeQuery({
query: FindTaskDocument,
variables: { taskID },
data: {
findTask: newData,
},
});
updateApolloCache<FindTaskQuery>(
client,
FindTaskDocument,
cache =>
produce(cache, draftCache => {
const { checklists } = cache.findTask;
const item = deleteData.deleteTaskChecklist;
draftCache.findTask.checklists = checklists.filter(c => c.id !== item.taskChecklist.id);
const { complete, total } = calculateChecklistBadge(draftCache.findTask.checklists);
draftCache.findTask.badges.checklist = {
__typename: 'ChecklistBadge',
complete,
total,
};
if (complete === 0 && total === 0) {
draftCache.findTask.badges.checklist = null;
}
}),
{ taskID },
);
},
});
const [updateTaskChecklistItemName] = useUpdateTaskChecklistItemNameMutation();
const [createTaskChecklist] = useCreateTaskChecklistMutation({
update: (client, createData) => {
const cacheData: any = client.readQuery({
query: FindTaskDocument,
variables: { taskID },
});
console.log(createData);
const newData = {
findTask: {
...cacheData.findTask,
checklists: [...cacheData.findTask.checklists, { ...createData.data.createTaskChecklist }],
},
};
client.writeQuery({
query: FindTaskDocument,
variables: { taskID },
data: newData,
});
updateApolloCache<FindTaskQuery>(
client,
FindTaskDocument,
cache =>
produce(cache, draftCache => {
const item = createData.data.createTaskChecklist;
draftCache.findTask.checklists.push({ ...item });
}),
{ taskID },
);
},
});
const [updateTaskChecklistName] = useUpdateTaskChecklistNameMutation();
const [deleteTaskChecklistItem] = useDeleteTaskChecklistItemMutation({
update: (client, deleteData) => {
const cacheData: any = client.readQuery({
query: FindTaskDocument,
variables: { taskID },
});
console.log(deleteData);
const newData = produce(cacheData.findTask, (draftState: any) => {
const idx = draftState.checklists.findIndex(
(checklist: TaskChecklist) =>
checklist.id === deleteData.data.deleteTaskChecklistItem.taskChecklistItem.taskChecklistID,
);
console.log(`idx ${idx}`);
if (idx !== -1) {
draftState.checklists[idx].items = cacheData.findTask.checklists[idx].items.filter(
(item: any) => item.id !== deleteData.data.deleteTaskChecklistItem.taskChecklistItem.id,
);
const { complete, total } = calculateChecklistBadge(draftState);
if (!draftState.badges) {
draftState.badges = {
checklist: {},
updateApolloCache<FindTaskQuery>(
client,
FindTaskDocument,
cache =>
produce(cache, draftCache => {
const item = deleteData.data.deleteTaskChecklistItem.taskChecklistItem;
draftCache.findTask.checklists = cache.findTask.checklists.filter(c => item.id !== c.id);
const { complete, total } = calculateChecklistBadge(draftCache.findTask.checklists);
draftCache.findTask.badges.checklist = {
__typename: 'ChecklistBadge',
complete,
total,
};
}
draftState.badges.checklist = {
__typename: 'ChecklistBadge',
complete,
total,
};
}
});
client.writeQuery({
query: FindTaskDocument,
variables: { taskID },
data: {
findTask: newData,
},
});
}),
{ taskID },
);
},
});
const [createTaskChecklistItem] = useCreateTaskChecklistItemMutation({
update: (client, newTaskItem) => {
const cacheData: any = client.readQuery({
query: FindTaskDocument,
variables: { taskID },
});
console.log(cacheData);
console.log(newTaskItem);
const newData = produce(cacheData.findTask, (draftState: any) => {
const idx = draftState.checklists.findIndex(
(checklist: TaskChecklist) => checklist.id === newTaskItem.data.createTaskChecklistItem.taskChecklistID,
);
if (idx !== -1) {
draftState.checklists[idx].items = [
...cacheData.findTask.checklists[idx].items,
{ ...newTaskItem.data.createTaskChecklistItem },
];
console.log(draftState.checklists.map((item: any) => item.items));
const { complete, total } = calculateChecklistBadge(draftState);
if (!draftState.badges) {
draftState.badges = {
checklist: {},
};
}
draftState.badges.checklist = {
__typename: 'ChecklistBadge',
complete,
total,
};
console.log(draftState.badges.checklist);
}
});
console.log(newData);
client.writeQuery({
query: FindTaskDocument,
variables: { taskID },
data: {
findTask: newData,
},
});
updateApolloCache<FindTaskQuery>(
client,
FindTaskDocument,
cache =>
produce(cache, draftCache => {
const item = newTaskItem.data.createTaskChecklistItem;
const { checklists } = cache.findTask;
const idx = checklists.findIndex(c => c.id === item.taskChecklistID);
if (idx !== -1) {
draftCache.findTask.checklists[idx].items.push({ ...item });
const { complete, total } = calculateChecklistBadge(draftCache.findTask.checklists);
draftCache.findTask.badges.checklist = {
__typename: 'ChecklistBadge',
complete,
total,
};
}
}),
{ taskID },
);
},
});
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });

View File

@ -1,8 +1,9 @@
// LOC830
import React, { useState, useRef, useContext, useEffect } from 'react';
import { MENU_TYPES } from 'shared/components/TopNavbar';
import updateApolloCache from 'shared/utils/cache';
import GlobalTopNavbar, { ProjectPopup } from 'App/TopNavbar';
import styled from 'styled-components/macro';
import { DataProxy } from '@apollo/client';
import { Bolt, ToggleOn, Tags, CheckCircle, Sort, Filter } from 'shared/icons';
import { usePopup, Popup } from 'shared/components/PopupMenu';
import { useParams, Route, useRouteMatch, useHistory, RouteComponentProps } from 'react-router-dom';
@ -45,33 +46,9 @@ import produce from 'immer';
import MiniProfile from 'shared/components/MiniProfile';
import Details from './Details';
import { useApolloClient } from '@apollo/react-hooks';
import { DocumentNode } from 'graphql';
import UserIDContext from 'App/context';
import DueDateManager from 'shared/components/DueDateManager';
type UpdateCacheFn<T> = (cache: T) => T;
function updateApolloCache<T>(client: DataProxy, document: DocumentNode, update: UpdateCacheFn<T>, variables?: object) {
let queryArgs: DataProxy.Query<any>;
if (variables) {
queryArgs = {
query: document,
variables,
};
} else {
queryArgs = {
query: document,
};
}
const cache: T | null = client.readQuery(queryArgs);
if (cache) {
const newCache = update(cache);
client.writeQuery({
...queryArgs,
data: newCache,
});
}
}
const getCacheData = (client: any, projectID: string) => {
const cacheData: FindProjectQuery = client.readQuery({
query: FindProjectDocument,
@ -140,25 +117,33 @@ const LabelManagerEditor: React.FC<LabelManagerEditorProps> = ({
const [currentLabel, setCurrentLabel] = useState('');
const [createProjectLabel] = useCreateProjectLabelMutation({
update: (client, newLabelData) => {
const cacheData = getCacheData(client, projectID);
const newData = {
...cacheData.findProject,
labels: [...cacheData.findProject.labels, { ...newLabelData.data.createProjectLabel }],
};
writeCacheData(client, projectID, cacheData, newData);
updateApolloCache<FindProjectQuery>(
client,
FindProjectDocument,
cache =>
produce(cache, draftCache => {
draftCache.findProject.labels.push({ ...newLabelData.data.createProjectLabel });
}),
{
projectId: projectID,
},
);
},
});
const [updateProjectLabel] = useUpdateProjectLabelMutation();
const [deleteProjectLabel] = useDeleteProjectLabelMutation({
update: (client, newLabelData) => {
const cacheData = getCacheData(client, projectID);
const newData = {
...cacheData.findProject,
labels: cacheData.findProject.labels.filter(
(label: any) => label.id !== newLabelData.data.deleteProjectLabel.id,
),
};
writeCacheData(client, projectID, cacheData, newData);
updateApolloCache<FindProjectQuery>(
client,
FindProjectDocument,
cache =>
produce(cache, draftCache => {
draftCache.findProject.labels = cache.findProject.labels.filter(
label => label.id !== newLabelData.data.deleteProjectLabel.id,
);
}),
{ projectId: projectID },
);
},
});
const labels = labelsRef.current ? labelsRef.current : [];
@ -286,27 +271,29 @@ const Project = () => {
const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
const [updateTaskLocation] = useUpdateTaskLocationMutation({
update: (client, newTask) => {
const cacheData = getCacheData(client, projectID);
console.log(cacheData);
console.log(newTask);
const newTaskGroups = produce(cacheData.findProject.taskGroups, (draftState: Array<TaskGroup>) => {
const { previousTaskGroupID, task } = newTask.data.updateTaskLocation;
if (previousTaskGroupID !== task.taskGroup.id) {
const oldTaskGroupIdx = draftState.findIndex((t: TaskGroup) => t.id === previousTaskGroupID);
const newTaskGroupIdx = draftState.findIndex((t: TaskGroup) => t.id === task.taskGroup.id);
if (oldTaskGroupIdx !== -1 && newTaskGroupIdx !== -1) {
draftState[oldTaskGroupIdx].tasks = draftState[oldTaskGroupIdx].tasks.filter((t: Task) => t.id !== task.id);
draftState[newTaskGroupIdx].tasks = [...draftState[newTaskGroupIdx].tasks, { ...task }];
}
}
});
const newData = {
...cacheData.findProject,
taskGroups: newTaskGroups,
};
writeCacheData(client, projectID, cacheData, newData);
updateApolloCache<FindProjectQuery>(
client,
FindProjectDocument,
cache =>
produce(cache, draftCache => {
const { previousTaskGroupID, task } = newTask.data.updateTaskLocation;
if (previousTaskGroupID !== task.taskGroup.id) {
const { taskGroups } = cache.findProject;
const oldTaskGroupIdx = taskGroups.findIndex((t: TaskGroup) => t.id === previousTaskGroupID);
const newTaskGroupIdx = taskGroups.findIndex((t: TaskGroup) => t.id === task.taskGroup.id);
if (oldTaskGroupIdx !== -1 && newTaskGroupIdx !== -1) {
draftCache.findProject.taskGroups[oldTaskGroupIdx].tasks = taskGroups[oldTaskGroupIdx].tasks.filter(
(t: Task) => t.id !== task.id,
);
draftCache.findProject.taskGroups[newTaskGroupIdx].tasks = [
...taskGroups[newTaskGroupIdx].tasks,
{ ...task },
];
}
}
}),
{ projectId: projectID },
);
},
});
const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation({});
@ -314,15 +301,17 @@ const Project = () => {
const [deleteTaskGroup] = useDeleteTaskGroupMutation({
onCompleted: deletedTaskGroupData => {},
update: (client, deletedTaskGroupData) => {
const cacheData = getCacheData(client, projectID);
const newData = {
...cacheData.findProject,
taskGroups: cacheData.findProject.taskGroups.filter(
(taskGroup: any) => taskGroup.id !== deletedTaskGroupData.data.deleteTaskGroup.taskGroup.id,
),
};
writeCacheData(client, projectID, cacheData, newData);
updateApolloCache<FindProjectQuery>(
client,
FindProjectDocument,
cache =>
produce(cache, draftCache => {
draftCache.findProject.taskGroups = draftCache.findProject.taskGroups.filter(
(taskGroup: TaskGroup) => taskGroup.id !== deletedTaskGroupData.data.deleteTaskGroup.taskGroup.id,
);
}),
{ projectId: projectID },
);
},
});
@ -346,21 +335,19 @@ const Project = () => {
const [createTask] = useCreateTaskMutation({
onCompleted: newTaskData => {},
update: (client, newTaskData) => {
const cacheData = getCacheData(client, projectID);
const newTaskGroups = produce(cacheData.findProject.taskGroups, draftState => {
const targetIndex = draftState.findIndex(
(taskGroup: any) => taskGroup.id === newTaskData.data.createTask.taskGroup.id,
);
draftState[targetIndex] = {
...draftState[targetIndex],
tasks: [...draftState[targetIndex].tasks, { ...newTaskData.data.createTask }],
};
});
const newData = {
...cacheData.findProject,
taskGroups: newTaskGroups,
};
writeCacheData(client, projectID, cacheData, newData);
updateApolloCache<FindProjectQuery>(
client,
FindProjectDocument,
cache =>
produce(cache, draftCache => {
const { taskGroups } = cache.findProject;
const idx = taskGroups.findIndex(taskGroup => taskGroup.id === newTaskData.data.createTask.taskGroup.id);
if (idx !== -1) {
draftCache.findProject.taskGroups[idx].tasks.push({ ...newTaskData.data.createTask });
}
}),
{ projectId: projectID },
);
},
});
@ -444,12 +431,20 @@ const Project = () => {
const onListDrop = (droppedColumn: TaskGroup) => {
console.log(`list drop ${droppedColumn.id}`);
const cacheData = getCacheData(client, projectID);
const newData = produce(cacheData, (draftState: any) => {
const taskGroupIdx = cacheData.findProject.taskGroups.findIndex((t: any) => t.id === droppedColumn.id);
cacheData.findProject.taskGroups[taskGroupIdx].position = droppedColumn.position;
});
writeCacheData(client, projectID, cacheData, newData);
updateApolloCache<FindProjectQuery>(
client,
FindProjectDocument,
cache =>
produce(cache, draftCache => {
const taskGroupIdx = cache.findProject.taskGroups.findIndex(t => t.id === droppedColumn.id);
if (taskGroupIdx !== -1) {
draftCache.findProject.taskGroups[taskGroupIdx].position = droppedColumn.position;
}
}),
{
projectId: projectID,
},
);
updateTaskGroupLocation({
variables: { taskGroupID: droppedColumn.id, position: droppedColumn.position },
optimisticResponse: {
@ -481,12 +476,15 @@ const Project = () => {
const [updateProjectName] = useUpdateProjectNameMutation({
update: (client, newName) => {
const cacheData = getCacheData(client, projectID);
const newData = {
...cacheData.findProject,
name: newName.data.updateProjectName.name,
};
writeCacheData(client, projectID, cacheData, newData);
updateApolloCache<FindProjectQuery>(
client,
FindProjectDocument,
cache =>
produce(cache, draftCache => {
draftCache.findProject.name = newName.data.updateProjectName.name;
}),
{ projectId: projectID },
);
},
});
@ -739,17 +737,18 @@ const Project = () => {
deleteTask({
variables: { taskID: cardId },
update: () => {
const cacheData = getCacheData(client, projectID);
const newData = {
...cacheData.findProject,
taskGroups: cacheData.findProject.taskGroups.map((taskGroup: any) => {
return {
...taskGroup,
tasks: taskGroup.tasks.filter((t: any) => t.id !== cardId),
};
}),
};
writeCacheData(client, projectID, cacheData, newData);
updateApolloCache<FindProjectQuery>(
client,
FindProjectDocument,
cache =>
produce(cache, draftCache => {
draftCache.findProject.taskGroups = cache.findProject.taskGroups.map(taskGroup => ({
...taskGroup,
tasks: taskGroup.tasks.filter(t => t.id !== cardId),
}));
}),
{ projectId: projectID },
);
},
})
}

View File

@ -6,6 +6,7 @@ import {
useGetProjectsQuery,
useCreateProjectMutation,
GetProjectsDocument,
GetProjectsQuery,
} from 'shared/generated/graphql';
import ProjectGridItem, { AddProjectItem } from 'shared/components/ProjectGridItem';
@ -17,6 +18,8 @@ import Button from 'shared/components/Button';
import { usePopup, Popup } from 'shared/components/PopupMenu';
import { useForm } from 'react-hook-form';
import Input from 'shared/components/Input';
import updateApolloCache from 'shared/utils/cache';
import produce from 'immer';
const CreateTeamButton = styled(Button)`
width: 100%;
@ -205,22 +208,11 @@ const Projects = () => {
}, []);
const [createProject] = useCreateProjectMutation({
update: (client, newProject) => {
const cacheData: any = client.readQuery({
query: GetProjectsDocument,
});
console.log(cacheData);
console.log(newProject);
const newData = {
...cacheData,
projects: [...cacheData.projects, { ...newProject.data.createProject }],
};
console.log(newData);
client.writeQuery({
query: GetProjectsDocument,
data: newData,
});
updateApolloCache<GetProjectsQuery>(client, GetProjectsDocument, cache =>
produce(cache, draftCache => {
draftCache.projects.push({ ...newProject.data.createProject });
}),
);
},
});
@ -228,17 +220,11 @@ const Projects = () => {
const { userID, setUserID } = useContext(UserIDContext);
const [createTeam] = useCreateTeamMutation({
update: (client, createData) => {
const cacheData: any = client.readQuery({
query: GetProjectsDocument,
});
const newData = {
...cacheData,
teams: [...cacheData.teams, { ...createData.data.createTeam }],
};
client.writeQuery({
query: GetProjectsDocument,
data: newData,
});
updateApolloCache<GetProjectsQuery>(client, GetProjectsDocument, cache =>
produce(cache, draftCache => {
draftCache.teams.push({ ...createData.data.createTeam });
}),
);
},
});
if (loading) {

View File

@ -2,7 +2,13 @@ import React, { useState, useContext, useEffect } from 'react';
import styled from 'styled-components/macro';
import { MENU_TYPES } from 'shared/components/TopNavbar';
import GlobalTopNavbar from 'App/TopNavbar';
import { useGetTeamQuery, useDeleteTeamMutation, GetProjectsDocument } from 'shared/generated/graphql';
import updateApolloCache from 'shared/utils/cache';
import {
useGetTeamQuery,
useDeleteTeamMutation,
GetProjectsDocument,
GetProjectsQuery,
} from 'shared/generated/graphql';
import { useParams, useHistory } from 'react-router';
import { usePopup, Popup } from 'shared/components/PopupMenu';
import { History } from 'history';
@ -116,26 +122,14 @@ export const TeamPopup: React.FC<TeamPopupProps> = ({ history, name, teamID }) =
const { hidePopup, setTab } = usePopup();
const [deleteTeam] = useDeleteTeamMutation({
update: (client, deleteData) => {
const cacheData: any = client.readQuery({
query: GetProjectsDocument,
});
console.log(cacheData);
console.log(deleteData);
const newData = produce(cacheData, (draftState: any) => {
draftState.teams = draftState.teams.filter((team: any) => team.id !== deleteData.data.deleteTeam.team.id);
draftState.projects = draftState.projects.filter(
(project: any) => project.team.id !== deleteData.data.deleteTeam.team.id,
);
});
client.writeQuery({
query: GetProjectsDocument,
data: {
...newData,
},
});
updateApolloCache<GetProjectsQuery>(client, GetProjectsDocument, cache =>
produce(cache, draftCache => {
draftCache.teams = cache.teams.filter((team: any) => team.id !== deleteData.data.deleteTeam.team.id);
draftCache.projects = cache.projects.filter(
(project: any) => project.team.id !== deleteData.data.deleteTeam.team.id,
);
}),
);
},
});
return (

View File

@ -198,7 +198,11 @@ const NavBar: React.FC<NavBarProps> = ({
{menuType &&
menuType.map((name, idx) => {
console.log(`${name} : ${idx} === ${currentTab}`);
return <ProjectTab active={currentTab === idx}>{name}</ProjectTab>;
return (
<ProjectTab key={idx} active={currentTab === idx}>
{name}
</ProjectTab>
);
})}
</ProjectTabs>
)}

View File

@ -0,0 +1,33 @@
import { DataProxy } from '@apollo/client';
import { DocumentNode } from 'graphql';
type UpdateCacheFn<T> = (cache: T) => T;
export function updateApolloCache<T>(
client: DataProxy,
document: DocumentNode,
update: UpdateCacheFn<T>,
variables?: object,
) {
let queryArgs: DataProxy.Query<any>;
if (variables) {
queryArgs = {
query: document,
variables,
};
} else {
queryArgs = {
query: document,
};
}
const cache: T | null = client.readQuery(queryArgs);
if (cache) {
const newCache = update(cache);
client.writeQuery({
...queryArgs,
data: newCache,
});
}
}
export default updateApolloCache;