import React , { useContext , useEffect , useReducer , useRef , useCallback } from 'react' ; import { useHistory } from 'react-router-dom' ; import { Board as BoardType , TaskGroup as TaskGroupType , Task as TaskType , } from 'ProjectPlanner/types' ; import { api } from 'ProjectPlanner/api' ; type Props = { boardId : number ; } ; type BoardContextType = { board : BoardType | null ; taskGroups : TaskGroupType [ ] | null ; tasks : TaskType [ ] | null ; loading : boolean ; updateBoard : ( updates : Partial < BoardType > ) => void ; createTaskGroup : ( ) => void ; updateTaskGroup : ( taskGroupId : number , updates : Partial < TaskGroupType > ) => void ; updateTaskGroups : ( taskGroups : TaskGroupType [ ] ) => void ; removeTaskGroup : ( taskGroupId : number ) => void ; getTask : ( taskId : number ) => TaskType | undefined ; updateTask : ( taskId : number , task : TaskType ) => void ; createTask : ( taskGroupId : number ) => void ; removeTask : ( taskId : number ) => void ; } ; const BoardContext = React . createContext < BoardContextType > ( null ! ) ; type State = { current : 'loading' | 'success' | 'error' ; errorMessage : null | string ; board : null | BoardType ; taskGroups : null | TaskGroupType [ ] ; tasks : null | TaskType [ ] ; } ; type Actions = { type : 'LOAD_DATA' ; payload : Partial < State > ; } | { type : 'UPDATE_BOARD' ; updates : Partial < BoardType > ; } | { type : 'CREATE_TASK_GROUP' ; taskGroup : TaskGroupType ; } | { type : 'REMOVE_TASK_GROUP' ; taskGroupId : number ; } | { type : 'UPDATE_TASK_GROUP' ; taskGroupId : number ; updates : Partial < TaskGroupType > ; } | { type : 'UPDATE_TASK_GROUPS' ; taskGroups : TaskGroupType [ ] ; } | { type : 'CREATE_TASK' ; taskGroupId : number ; task : TaskType ; } | { type : 'UPDATE_TASK' ; taskId : number ; task : TaskType ; } | { type : 'REMOVE_TASK' ; taskId : number ; } ; export const BoardProvider = ( { boardId , children } ) => { const history = useHistory ( ) ; const [ state , dispatch ] = useReducer ( ( state , action ) => { switch ( action . type ) { case 'LOAD_DATA' : { return { ... state , ... action . payload } ; } case 'UPDATE_BOARD' : { if ( ! state . board ) return state ; return { ... state , board : { ... state . board , ... action . updates } } ; } case 'CREATE_TASK_GROUP' : { if ( ! state . taskGroups ) return state ; return { ... state , taskGroups : state . taskGroups . concat ( action . taskGroup ) } ; } case 'REMOVE_TASK_GROUP' : { if ( ! state . taskGroups || ! state . tasks ) return state ; const relatedTaskIds = state . taskGroups . find ( ( t ) => t . id === action . taskGroupId ) ?. taskIds || [ ] ; return { ... state , taskGroups : state . taskGroups . filter ( ( t ) => t . id !== action . taskGroupId ) , tasks : state . tasks . filter ( ( t ) => ! relatedTaskIds . includes ( t . id ) ) , } ; } case 'UPDATE_TASK_GROUP' : { const { taskGroups } = state ; if ( ! taskGroups ) return state ; const i = taskGroups . findIndex ( ( t ) => t . id === action . taskGroupId ) ; return { ... state , taskGroups : [ ... taskGroups . slice ( 0 , i ) , { ... taskGroups [ i ] , ... action . updates } , ... taskGroups . slice ( i + 1 , taskGroups . length ) , ] , } ; } case 'UPDATE_TASK_GROUPS' : { return { ... state , taskGroups : action . taskGroups } ; } case 'CREATE_TASK' : { const { taskGroups , tasks } = state ; if ( ! taskGroups || ! tasks ) return state ; const i = taskGroups . findIndex ( ( t ) => t . id === action . taskGroupId ) ; const taskGroup = taskGroups [ i ] ; return { ... state , tasks : tasks . concat ( action . task ) , taskGroups : [ ... taskGroups . slice ( 0 , i ) , { ... taskGroup , taskIds : taskGroup . taskIds . concat ( action . task . id ) } , ... taskGroups . slice ( i + 1 , taskGroups . length ) , ] , } ; } case 'UPDATE_TASK' : { const { tasks } = state ; if ( ! tasks ) return state ; const i = tasks . findIndex ( ( t ) => t . id === action . taskId ) ; return { ... state , tasks : [ ... tasks . slice ( 0 , i ) , action . task , ... tasks . slice ( i + 1 , tasks . length ) ] , } ; } case 'REMOVE_TASK' : { const { taskGroups , tasks } = state ; if ( ! taskGroups || ! tasks ) return state ; return { ... state , tasks : tasks . filter ( ( task ) => task . id !== action . taskId ) , taskGroups : taskGroups . map ( ( taskGroup ) => { return { ... taskGroup , taskIds : taskGroup . taskIds . filter ( ( id ) => id !== action . taskId ) , } ; } ) , } ; } default : return state ; } } , { current : 'loading' , errorMessage : null , board : null , tasks : null , taskGroups : null , } ) ; const { current : status , board , taskGroups , tasks } = state ; const loading = status === 'loading' ; useEffect ( ( ) => { api . boards . getBoard ( boardId ) . then ( ( data ) => { const { taskGroups , tasks , ... board } = data ; if ( ! board . id ) { history . push ( '/boards' ) ; return ; } dispatch ( { type : 'LOAD_DATA' , payload : { taskGroups , tasks , board , current : 'success' } } ) ; } ) ; } , [ boardId , history ] ) ; const context = { board , taskGroups , tasks , loading , updateBoard : ( updates ) => { if ( ! board ) return ; api . boards . updateBoard ( boardId , updates ) . then ( ( ) => { dispatch ( { type : 'UPDATE_BOARD' , updates } ) ; } ) ; } , createTaskGroup : ( ) => { if ( ! taskGroups ) return ; api . boards . createTaskGroup ( boardId ) . then ( ( taskGroup ) => { dispatch ( { type : 'CREATE_TASK_GROUP' , taskGroup } ) ; } ) ; } , updateTaskGroup : ( taskGroupId , updates ) => { if ( ! taskGroups ) return ; api . boards . updateTaskGroup ( taskGroupId , updates ) . then ( ( ) => { dispatch ( { type : 'UPDATE_TASK_GROUP' , taskGroupId , updates } ) ; } ) ; } , updateTaskGroups : ( taskGroups ) => { dispatch ( { type : 'UPDATE_TASK_GROUPS' , taskGroups } ) ; api . boards . updateTaskGroups ( taskGroups ) ; } , removeTaskGroup : ( taskGroupId ) => { api . boards . removeTaskGroup ( taskGroupId ) . then ( ( ) => { dispatch ( { type : 'REMOVE_TASK_GROUP' , taskGroupId } ) ; } ) ; } , getTask : ( taskId ) => { return tasks ?. find ( ( t ) => t . id === taskId ) ; } , createTask : ( taskGroupId ) => { api . boards . createTask ( boardId , taskGroupId ) . then ( ( task ) => { dispatch ( { type : 'CREATE_TASK' , taskGroupId , task } ) ; } ) ; } , updateTask : useCallback ( ( taskId , task ) => { api . boards . updateTask ( taskId , task ) . then ( ( ) => { dispatch ( { type : 'UPDATE_TASK' , taskId , task } ) ; } ) ; } , [ ] ) , removeTask : async ( taskId ) => { api . boards . removeTask ( boardId , taskId ) . then ( ( ) => { dispatch ( { type : 'REMOVE_TASK' , taskId } ) ; } ) ; } , } ; return < BoardContext . Provider children = { children } value = { context } / > ; } ; export const useBoardContext = ( ) => { return useContext ( BoardContext ) ; } ;