diff --git a/web/src/App/TopNavbar.tsx b/web/src/App/TopNavbar.tsx index f3ca37b..c24e666 100644 --- a/web/src/App/TopNavbar.tsx +++ b/web/src/App/TopNavbar.tsx @@ -137,11 +137,11 @@ const ProjectFinder = () => { return ( <> {projectTeams.map(team => ( - + {team.name} {team.projects.map((project, idx) => ( - + diff --git a/web/src/Projects/Project/Details/index.tsx b/web/src/Projects/Project/Details/index.tsx index 1816eed..3690205 100644 --- a/web/src/Projects/Project/Details/index.tsx +++ b/web/src/Projects/Project/Details/index.tsx @@ -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) => { + 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 = ({ 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( + 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( + 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( + 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( + 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( + 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 } }); diff --git a/web/src/Projects/Project/index.tsx b/web/src/Projects/Project/index.tsx index 80cdd86..ef22601 100644 --- a/web/src/Projects/Project/index.tsx +++ b/web/src/Projects/Project/index.tsx @@ -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 = (cache: T) => T; -function updateApolloCache(client: DataProxy, document: DocumentNode, update: UpdateCacheFn, variables?: object) { - let queryArgs: DataProxy.Query; - 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 = ({ 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( + 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( + 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) => { - 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( + 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( + 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( + 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( + 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( + 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( + 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 }, + ); }, }) } diff --git a/web/src/Projects/index.tsx b/web/src/Projects/index.tsx index 293dd7b..7926eb1 100644 --- a/web/src/Projects/index.tsx +++ b/web/src/Projects/index.tsx @@ -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(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(client, GetProjectsDocument, cache => + produce(cache, draftCache => { + draftCache.teams.push({ ...createData.data.createTeam }); + }), + ); }, }); if (loading) { diff --git a/web/src/Teams/index.tsx b/web/src/Teams/index.tsx index d7cd199..1a8f708 100644 --- a/web/src/Teams/index.tsx +++ b/web/src/Teams/index.tsx @@ -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 = ({ 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(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 ( diff --git a/web/src/shared/components/TopNavbar/index.tsx b/web/src/shared/components/TopNavbar/index.tsx index 8cd593d..2c568b7 100644 --- a/web/src/shared/components/TopNavbar/index.tsx +++ b/web/src/shared/components/TopNavbar/index.tsx @@ -198,7 +198,11 @@ const NavBar: React.FC = ({ {menuType && menuType.map((name, idx) => { console.log(`${name} : ${idx} === ${currentTab}`); - return {name}; + return ( + + {name} + + ); })} )} diff --git a/web/src/shared/utils/cache.ts b/web/src/shared/utils/cache.ts new file mode 100644 index 0000000..eb130b6 --- /dev/null +++ b/web/src/shared/utils/cache.ts @@ -0,0 +1,33 @@ +import { DataProxy } from '@apollo/client'; +import { DocumentNode } from 'graphql'; + +type UpdateCacheFn = (cache: T) => T; + +export function updateApolloCache( + client: DataProxy, + document: DocumentNode, + update: UpdateCacheFn, + variables?: object, +) { + let queryArgs: DataProxy.Query; + 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;