diff --git a/app/renderer/src/hooks/index.ts b/app/renderer/src/hooks/index.ts index 521aebed..c1c7adac 100644 --- a/app/renderer/src/hooks/index.ts +++ b/app/renderer/src/hooks/index.ts @@ -3,3 +3,4 @@ export * from "./useTargetOutside"; export * from "./useRippleEffect"; export * from "./useNotification"; export * from "./useTime"; +export * from "./storeHooks"; diff --git a/app/renderer/src/routes/Tasks/TaskDetails/index.tsx b/app/renderer/src/routes/Tasks/TaskDetails/index.tsx index 099fff83..7fb25c0d 100644 --- a/app/renderer/src/routes/Tasks/TaskDetails/index.tsx +++ b/app/renderer/src/routes/Tasks/TaskDetails/index.tsx @@ -5,9 +5,8 @@ import React, { useContext, useCallback, } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import { useAppDispatch, useAppSelector } from "hooks"; import { - AppStateTypes, editTaskCard, editTaskCardText, removeTaskCard, @@ -45,11 +44,9 @@ const TaskDetails = React.forwardRef( const descriptionAreaRef = useRef(null); const descriptionFormRef = useRef(null); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); - const tasks = useSelector( - (state: AppStateTypes) => state.tasks.present - ); + const tasks = useAppSelector((state) => state.tasks.present); const { openExternalCallback } = useContext(ConnnectorContext); @@ -90,11 +87,11 @@ const TaskDetails = React.forwardRef( const onEditCardText = useCallback(() => { if (cardTextAreaRef.current && cardTextAreaRef.current.value) { dispatch( - editTaskCardText( + editTaskCardText({ listId, cardId, - cardTextAreaRef.current.value - ) + cardText: cardTextAreaRef.current.value, + }) ); } }, [dispatch, cardId, listId]); @@ -102,14 +99,14 @@ const TaskDetails = React.forwardRef( const onSubmitAction = useCallback( (e: React.FormEvent) => { e.preventDefault(); - dispatch(editTaskCard(listId, cardId, description)); + dispatch(editTaskCard({ listId, cardId, description })); setEditingDescription(false); }, [dispatch, cardId, description, listId] ); const onCardDeleteAction = useCallback(() => { - dispatch(removeTaskCard(listId, cardId)); + dispatch(removeTaskCard({ listId, cardId })); if (onExit) { onExit(); @@ -124,9 +121,9 @@ const TaskDetails = React.forwardRef( const setTaskCardDoneCallback = useCallback( (e) => { if (e.currentTarget.checked) { - dispatch(setTaskCardDone(listId, card?._id)); + dispatch(setTaskCardDone({ listId, cardId: card?._id })); } else { - dispatch(setTaskCardNotDone(listId, card?._id)); + dispatch(setTaskCardNotDone({ listId, cardId: card?._id })); } }, [dispatch, listId, card] diff --git a/app/renderer/src/routes/Tasks/TaskInnerList.tsx b/app/renderer/src/routes/Tasks/TaskInnerList.tsx index 63fafb09..ea7f6a7c 100644 --- a/app/renderer/src/routes/Tasks/TaskInnerList.tsx +++ b/app/renderer/src/routes/Tasks/TaskInnerList.tsx @@ -1,10 +1,10 @@ import React from "react"; -import { TaskTypes } from "store"; +import type { TaskList as TaskListType } from "store"; import TaskList from "./TaskList"; import { StyledTaskSection } from "styles"; type Props = { - tasks: TaskTypes[]; + tasks: TaskListType[]; }; const TaskInnerList: React.FC = ({ tasks }) => { diff --git a/app/renderer/src/routes/Tasks/TaskList.tsx b/app/renderer/src/routes/Tasks/TaskList.tsx index c250691f..a7a07cc9 100644 --- a/app/renderer/src/routes/Tasks/TaskList.tsx +++ b/app/renderer/src/routes/Tasks/TaskList.tsx @@ -1,8 +1,7 @@ import React, { useRef, useState } from "react"; -import { useDispatch } from "react-redux"; +import { useAppDispatch } from "hooks"; import { Droppable, Draggable } from "react-beautiful-dnd"; import { - TaskTypes, addTaskCard, editTaskTitle, editTaskCardText, @@ -11,7 +10,7 @@ import { removeTaskCard, } from "store"; import { StyledTaskSectionItem, StyledCardWrapper } from "styles"; - +import type { TaskList as TaskListType } from "store"; import TaskHeader from "./TaskHeader"; import TaskFormButton from "./TaskFormButton"; import TaskDetails from "./TaskDetails"; @@ -21,7 +20,7 @@ type Props = { priority: boolean; listId: string; title: string; - cards: TaskTypes["cards"]; + cards: TaskListType["cards"]; index: number; }; @@ -38,14 +37,14 @@ const TaskList: React.FC = ({ const [cardId, setCardId] = useState(""); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); - const onCardAdd = (value: string) => { - dispatch(addTaskCard(listId, value)); + const onCardAdd = (cardText: string) => { + dispatch(addTaskCard({ listId, cardText })); }; - const onEditListTitle = (title: string) => { - dispatch(editTaskTitle(listId, title)); + const onEditListTitle = (listTitle: string) => { + dispatch(editTaskTitle({ listId, listTitle })); }; const onRemoveListAction = () => { @@ -81,7 +80,7 @@ const TaskList: React.FC = ({ /> - {cards?.map(({ _id, text, done }, index) => ( + {cards.map(({ _id, text, done }, index) => ( = ({ setShowDetails(true); }} onSaveCardText={(text) => - dispatch(editTaskCardText(listId, _id, text)) + dispatch( + editTaskCardText({ + listId, + cardId: _id, + cardText: text, + }) + ) } onDeleteCard={() => - dispatch(removeTaskCard(listId, _id)) + dispatch( + removeTaskCard({ listId, cardId: _id }) + ) } /> ))} diff --git a/app/renderer/src/routes/Tasks/index.tsx b/app/renderer/src/routes/Tasks/index.tsx index 11aabe4f..6258fb4b 100644 --- a/app/renderer/src/routes/Tasks/index.tsx +++ b/app/renderer/src/routes/Tasks/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import { useAppDispatch, useAppSelector } from "hooks"; import { ActionCreators as UndoActionCreator } from "redux-undo"; import { DragDropContext, @@ -12,15 +12,15 @@ import { StyledTaskWrapper, StyledTaskMain, } from "styles"; -import { AppStateTypes, addTaskList, dragList } from "store"; +import { addTaskList, dragList } from "store"; import TaskFormButton from "./TaskFormButton"; import TaskInnerList from "./TaskInnerList"; export default function Tasks() { - const tasks = useSelector((state: AppStateTypes) => state.tasks); + const tasks = useAppSelector((state) => state.tasks); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const onListAdd = (value: string) => dispatch(addTaskList(value)); @@ -32,14 +32,14 @@ export default function Tasks() { } dispatch( - dragList( - source.droppableId, - destination.droppableId, - source.index, - destination.index, + dragList({ + sourceId: source.droppableId, + destinationId: destination.droppableId, + sourceIndex: source.index, + destinationIndex: destination.index, draggableId, - type - ) + type, + }) ); }; @@ -50,12 +50,14 @@ export default function Tasks() { if (activeElement !== "INPUT" && activeElement !== "TEXTAREA") { if (e.ctrlKey && e.code === "KeyZ") { if (tasks.past.length > 0) { + // @ts-ignore This is a problem with redux-undo types dispatch(UndoActionCreator.undo()); } } if (e.ctrlKey && e.shiftKey && e.code === "KeyZ") { if (tasks.future.length > 0) { + // @ts-ignore This is a problem with redux-undo types dispatch(UndoActionCreator.redo()); } } diff --git a/app/renderer/src/routes/Timer/PriorityCard.tsx b/app/renderer/src/routes/Timer/PriorityCard.tsx index 4514091e..7c202315 100644 --- a/app/renderer/src/routes/Timer/PriorityCard.tsx +++ b/app/renderer/src/routes/Timer/PriorityCard.tsx @@ -1,11 +1,6 @@ import React, { useEffect, useRef } from "react"; -import { useSelector, useDispatch } from "react-redux"; -import { - AppStateTypes, - setTaskCardDone, - skipTaskCard, - removeTaskCard, -} from "store"; +import { useAppDispatch, useAppSelector } from "hooks"; +import { setTaskCardDone, skipTaskCard, removeTaskCard } from "store"; import { StyledPriorityCardContainer, @@ -24,14 +19,11 @@ import { } from "styles"; import { SVG } from "components"; import { useTargetOutside } from "hooks"; -import { isObjectEmpty } from "utils"; const PriorityCard: React.FC = () => { - const tasks = useSelector( - (state: AppStateTypes) => state.tasks.present - ); + const tasks = useAppSelector((state) => state.tasks.present); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const priorityList = tasks.find((list) => list.priority); @@ -48,22 +40,32 @@ const PriorityCard: React.FC = () => { }); const setTaskCardDoneCallback = () => { - if (!isObjectEmpty(priorityCard)) { - dispatch(setTaskCardDone(priorityList?._id, priorityCard?._id)); + if (priorityCard && priorityList) { + dispatch( + setTaskCardDone({ + listId: priorityList?._id, + cardId: priorityCard?._id, + }) + ); } setShowOptions(false); }; const skipTaskCardCallback = () => { - if (!isObjectEmpty(priorityCard)) { + if (priorityList) { dispatch(skipTaskCard(priorityList?._id)); } setShowOptions(false); }; const deleteTaskCardCallback = () => { - if (!isObjectEmpty(priorityCard)) { - dispatch(removeTaskCard(priorityList?._id, priorityCard?._id)); + if (priorityList && priorityCard) { + dispatch( + removeTaskCard({ + listId: priorityList?._id, + cardId: priorityCard?._id, + }) + ); } setShowOptions(false); }; diff --git a/app/renderer/src/store/store.ts b/app/renderer/src/store/store.ts index 5835055b..3fb96d0f 100644 --- a/app/renderer/src/store/store.ts +++ b/app/renderer/src/store/store.ts @@ -5,7 +5,7 @@ import { saveToStorage, getFromStorage } from "utils"; import configReducer from "./config"; import settingReducer from "./settings"; import timerReducer from "./timer"; -import { undoableTasksReducer } from "./tasks"; +import undoableTasksReducer from "./tasks"; import updateReducer from "./update"; export type AppStateTypes = ReturnType; diff --git a/app/renderer/src/store/tasks/actions.ts b/app/renderer/src/store/tasks/actions.ts deleted file mode 100644 index f1d087de..00000000 --- a/app/renderer/src/store/tasks/actions.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { - TaskTypes, - TasksActionTypes, - ADD_TASK_LIST, - CardTypes, - ADD_TASK_CARD, - DRAG_LIST, - EDIT_TASK_TITLE, - EDIT_TASK_CARD_TEXT, - EDIT_TASK_CARD_DESCRIPTION, - REMOVE_TASK_CARD, - REMOVE_TASK_LIST, - SET_TASK_LIST_PRIORITY, - SET_TASK_CARD_DONE, - SKIP_TASK_CARD, - SET_TASK_CARD_NOT_DONE, -} from "./types"; - -export const addTaskList = ( - title: TaskTypes["title"] -): TasksActionTypes => { - return { - type: ADD_TASK_LIST, - payload: title, - }; -}; - -export const removeTaskList = ( - listId: TaskTypes["_id"] -): TasksActionTypes => { - return { - type: REMOVE_TASK_LIST, - payload: listId, - }; -}; - -export const setTaskListPriority = ( - listId: TaskTypes["_id"] -): TasksActionTypes => { - return { - type: SET_TASK_LIST_PRIORITY, - payload: { listId }, - }; -}; - -export const editTaskTitle = ( - listId: TaskTypes["_id"], - listTitle: TaskTypes["title"] -): TasksActionTypes => { - return { - type: EDIT_TASK_TITLE, - payload: { listId, listTitle }, - }; -}; - -export const addTaskCard = ( - listId: TaskTypes["_id"], - cardText: CardTypes["text"] -): TasksActionTypes => { - return { - type: ADD_TASK_CARD, - payload: { listId, cardText }, - }; -}; - -export const editTaskCardText = ( - listId: TaskTypes["_id"], - cardId: CardTypes["_id"], - cardText: CardTypes["text"] -): TasksActionTypes => { - return { - type: EDIT_TASK_CARD_TEXT, - payload: { listId, cardId, cardText }, - }; -}; - -export const editTaskCard = ( - listId: TaskTypes["_id"], - cardId: CardTypes["_id"], - description?: CardTypes["description"] -): TasksActionTypes => { - return { - type: EDIT_TASK_CARD_DESCRIPTION, - payload: { listId, cardId, description }, - }; -}; - -export const removeTaskCard = ( - listId?: TaskTypes["_id"], - cardId?: CardTypes["_id"] -): TasksActionTypes => { - return { - type: REMOVE_TASK_CARD, - payload: { listId, cardId }, - }; -}; - -export const setTaskCardDone = ( - listId?: TaskTypes["_id"], - cardId?: CardTypes["_id"] -): TasksActionTypes => { - return { - type: SET_TASK_CARD_DONE, - payload: { listId, cardId }, - }; -}; - -export const setTaskCardNotDone = ( - listId?: TaskTypes["_id"], - cardId?: CardTypes["_id"] -): TasksActionTypes => { - return { - type: SET_TASK_CARD_NOT_DONE, - payload: { listId, cardId }, - }; -}; - -export const skipTaskCard = ( - listId?: TaskTypes["_id"] -): TasksActionTypes => { - return { - type: SKIP_TASK_CARD, - payload: { listId }, - }; -}; - -export const dragList = ( - sourceId: string, - destinationId: string, - sourceIndex: number, - destinationIndex: number, - draggableId: string, - type: string -): TasksActionTypes => { - return { - type: DRAG_LIST, - payload: { - sourceId, - destinationId, - sourceIndex, - destinationIndex, - draggableId, - type, - }, - }; -}; diff --git a/app/renderer/src/store/tasks/index.ts b/app/renderer/src/store/tasks/index.ts index 6d30c416..957dd8eb 100644 --- a/app/renderer/src/store/tasks/index.ts +++ b/app/renderer/src/store/tasks/index.ts @@ -1,3 +1,303 @@ -export * from "./actions"; -export * from "./reducer"; +import type { PayloadAction } from "@reduxjs/toolkit"; +import { createSlice } from "@reduxjs/toolkit"; +import { getFromStorage } from "utils"; +import type { Task, TaskList, ListPayload } from "./types"; +import undoable from "redux-undo"; +import { + addTaskToList, + createTaskList, + removeTaskFromList, + editTaskList, +} from "./utils/tasklist"; +import { createTask, editTask } from "./utils/task"; export * from "./types"; + +const tasks = + (getFromStorage("state") && getFromStorage("state").tasks) || []; + +const initialState: TaskList[] = tasks; + +const tasksSlice = createSlice({ + name: "tasks", + initialState, + reducers: { + addTaskList: (state, action: ListPayload<"title">) => { + const priority = state.length === 0 ? true : false; + + const title = action.payload.trim().toUpperCase(); + + const taskList = createTaskList({ title, priority }); + + return [...state, taskList]; + }, + + removeTaskList: (state, action: ListPayload<"_id">) => { + return state.filter((list) => list._id !== action.payload); + }, + + setTaskListPriority: (state, action: ListPayload<"_id">) => { + const newState = state.map((list) => { + if (list._id !== action.payload) { + return editTaskList(list, { priority: false }); + } + + return editTaskList(list, { priority: true }); + }); + + return newState; + }, + + editTaskTitle: ( + state, + action: PayloadAction<{ + listId: TaskList["_id"]; + listTitle: TaskList["title"]; + }> + ) => { + return state.map((list) => { + if (list._id === action.payload.listId) { + const title = action.payload.listTitle.trim().toUpperCase(); + + return editTaskList(list, { title }); + } + return list; + }); + }, + + addTaskCard: ( + state, + action: PayloadAction<{ + listId: TaskList["_id"]; + cardText: Task["text"]; + }> + ) => { + const text = action.payload.cardText.trim().capitalize(); + + const newTask = createTask({ text }); + + const newState = state.map((list) => { + if (list._id === action.payload.listId) { + return addTaskToList(list, newTask); + } + return list; + }); + + return newState; + }, + + editTaskCardText: ( + state, + action: PayloadAction<{ + listId: TaskList["_id"]; + cardId: Task["_id"]; + cardText: Task["text"]; + }> + ) => { + const newState = state.map((list) => { + if (list._id !== action.payload.listId) return list; + + const newCards = list.cards.map((card) => { + if (card._id !== action.payload.cardId) return card; + + const text = action.payload.cardText.trim().capitalize(); + + return editTask(card, { text }); + }); + + return { ...list, cards: newCards }; + }); + + return newState; + }, + + editTaskCard: ( + state, + action: PayloadAction<{ + listId: TaskList["_id"]; + cardId: Task["_id"]; + description?: Task["description"]; + }> + ) => { + const newState = state.map((list) => { + if (list._id !== action.payload.listId) return list; + + const newCards = list.cards.map((card) => { + if (card._id !== action.payload.cardId) return card; + + const description = + action.payload.description?.capitalize() || ""; + + return editTask(card, { description }); + }); + + return { ...list, cards: newCards }; + }); + + return newState; + }, + + removeTaskCard: ( + state, + action: PayloadAction<{ + listId: TaskList["_id"]; + cardId: Task["_id"]; + }> + ) => { + const newState = state.map((list) => { + if (list._id !== action.payload.listId) return list; + + return removeTaskFromList(list, action.payload.cardId); + }); + + return newState; + }, + + setTaskCardDone: ( + state, + action: PayloadAction<{ + listId: TaskList["_id"]; + cardId?: Task["_id"]; + }> + ) => { + if (!action.payload.cardId) return state; + + const newState = state.map((list) => { + if (list._id !== action.payload.listId) return list; + + const newCards = list.cards.map((card) => { + if (card._id !== action.payload.cardId) return card; + + return editTask(card, { done: true }); + }); + + return { ...list, cards: newCards }; + }); + + return newState; + }, + + setTaskCardNotDone: ( + state, + action: PayloadAction<{ + listId: TaskList["_id"]; + cardId?: Task["_id"]; + }> + ) => { + if (!action.payload.cardId) return; + + const newState = state.map((list) => { + if (list._id !== action.payload.listId) return list; + + const newCards = list.cards.map((card) => { + if (card._id !== action.payload.cardId) return card; + + return editTask(card, { done: false }); + }); + + return { ...list, cards: newCards }; + }); + + return newState; + }, + + skipTaskCard: (state, action: ListPayload<"_id">) => { + const newState = state.map((list) => { + if (list._id !== action.payload) return list; + + const doneCards = list.cards.filter((card) => card.done); + const notDoneCards = list.cards.filter((card) => !card.done); + + const firstNotDoneCard = notDoneCards.at(0); + + if (!firstNotDoneCard) return list; + + const newNotDoneCards = notDoneCards.filter( + (card) => card._id !== firstNotDoneCard._id + ); + + return { + ...list, + cards: [...newNotDoneCards, firstNotDoneCard, ...doneCards], + }; + }); + + return newState; + }, + + dragList: ( + state, + action: PayloadAction<{ + sourceId: TaskList["_id"]; + destinationId: TaskList["_id"]; + sourceIndex: number; + destinationIndex: number; + draggableId: string; + type: string; + }> + ) => { + const { + sourceId, + destinationId, + sourceIndex, + destinationIndex, + type, + } = action.payload; + + // dragging list around + if (type === "list") { + const list = state.splice(sourceIndex, 1); + + if (list) { + state.splice(destinationIndex, 0, ...list); + } + + return; + } + + // in then same list + if (sourceId === destinationId) { + const list = state.find((list) => sourceId === list._id); + const card = list?.cards.splice(sourceIndex, 1); + + if (card) { + list?.cards.splice(destinationIndex, 0, ...card); + } + } else { + // find the list where drag happened + const listStart = state.find((list) => sourceId === list._id); + + // pull out the card from this list + const card = listStart?.cards.splice(sourceIndex, 1); + + // find the list where drag ended + const listEnd = state.find( + (list) => destinationId === list._id + ); + + // put the card in the new list + if (card) { + listEnd?.cards.splice(destinationIndex, 0, ...card); + } + } + + return; + }, + }, +}); + +export const { + addTaskCard, + addTaskList, + dragList, + editTaskCard, + editTaskCardText, + editTaskTitle, + removeTaskCard, + removeTaskList, + setTaskCardDone, + setTaskCardNotDone, + setTaskListPriority, + skipTaskCard, +} = tasksSlice.actions; + +export default undoable(tasksSlice.reducer); diff --git a/app/renderer/src/store/tasks/reducer.ts b/app/renderer/src/store/tasks/reducer.ts deleted file mode 100644 index 8a6991d0..00000000 --- a/app/renderer/src/store/tasks/reducer.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { v4 as uuid } from "uuid"; -import { getFromStorage } from "utils"; -import { - TaskTypes, - TasksActionTypes, - ADD_TASK_LIST, - ADD_TASK_CARD, - DRAG_LIST, - EDIT_TASK_TITLE, - EDIT_TASK_CARD_TEXT, - EDIT_TASK_CARD_DESCRIPTION, - REMOVE_TASK_CARD, - REMOVE_TASK_LIST, - SET_TASK_LIST_PRIORITY, - CardTypes, - SET_TASK_CARD_DONE, - SKIP_TASK_CARD, - SET_TASK_CARD_NOT_DONE, -} from "./types"; -import undoable from "redux-undo"; - -const tasks = - (getFromStorage("state") && getFromStorage("state").tasks) || []; - -const initialState: TaskTypes[] = tasks; - -const tasksReducer = ( - state = initialState, - action: TasksActionTypes -) => { - switch (action.type) { - case ADD_TASK_LIST: { - const isPriority = state.length === 0 ? true : false; - const newList: TaskTypes = { - _id: uuid(), - title: action.payload.trim().toUpperCase(), - cards: [], - priority: isPriority, - }; - - const newState = [...state, newList]; - - return newState; - } - case REMOVE_TASK_LIST: { - const newState = state.filter((list) => { - return list._id !== action.payload; - }); - - return newState; - } - case EDIT_TASK_TITLE: { - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - return { - ...list, - title: action.payload.listTitle.trim().toUpperCase(), - }; - } - return list; - }); - - return newState; - } - case SET_TASK_LIST_PRIORITY: { - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - return { ...list, priority: true }; - } - - return { ...list, priority: false }; - }); - - newState.sort((a, b) => (a.priority > b.priority ? -1 : 1)); - - return newState; - } - case ADD_TASK_CARD: { - const newCard: CardTypes = { - _id: uuid(), - text: action.payload.cardText.trim().capitalize(), - description: "", - done: false, - }; - - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - return { - ...list, - cards: [...list.cards, newCard], - }; - } - return list; - }); - - return newState; - } - case EDIT_TASK_CARD_TEXT: { - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - const newCards = list.cards.map((card) => { - if (card._id === action.payload.cardId) { - return { - ...card, - text: action.payload.cardText.trim().capitalize(), - }; - } - return card; - }); - return { ...list, cards: newCards }; - } - return list; - }); - - return newState; - } - case EDIT_TASK_CARD_DESCRIPTION: { - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - const newCards = list.cards.map((card) => { - if (card._id === action.payload.cardId) { - return { - ...card, - description: action.payload.description.capitalize(), - }; - } - return card; - }); - return { ...list, cards: newCards }; - } - return list; - }); - - return newState; - } - case REMOVE_TASK_CARD: { - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - const newCards = list.cards.filter( - (card) => card._id !== action.payload.cardId - ); - return { ...list, cards: newCards }; - } - return list; - }); - - return newState; - } - case SET_TASK_CARD_DONE: { - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - const cards = list.cards.map((card) => { - if (card._id === action.payload.cardId) { - return { - ...card, - done: true, - }; - } - return card; - }); - - const firstCard = cards.shift(); - - return { - ...list, - cards: [...cards, firstCard], - }; - } - return list; - }); - - return newState; - } - case SET_TASK_CARD_NOT_DONE: { - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - const cards = list.cards.map((card) => { - if (card._id === action.payload.cardId) { - return { - ...card, - done: false, - }; - } - return card; - }); - - return { ...list, cards }; - } - return list; - }); - - return newState; - } - case SKIP_TASK_CARD: { - const newState = state.map((list) => { - if (list._id === action.payload.listId) { - const notDoneCards = list.cards - .filter((card) => !card.done) - .map((card) => card); - - const firstNotDoneCard = notDoneCards.shift(); - - const doneCards = list.cards - .filter((card) => card.done) - .map((card) => card); - - return { - ...list, - cards: [...notDoneCards, firstNotDoneCard, ...doneCards], - }; - } - return list; - }); - - return newState; - } - case DRAG_LIST: { - const { - sourceId, - destinationId, - sourceIndex, - destinationIndex, - type, - } = action.payload; - - const newState = [...state]; - - // dragging list around - if (type === "list") { - const list = newState.splice(sourceIndex, 1); - - if (list) { - newState.splice(destinationIndex, 0, ...list); - } - - return newState; - } - - // in then same list - if (sourceId === destinationId) { - const list = state.find((list) => sourceId === list._id); - const card = list?.cards.splice(sourceIndex, 1); - - if (card) { - list?.cards.splice(destinationIndex, 0, ...card); - } - } else { - // find the list where drag happened - const listStart = state.find((list) => sourceId === list._id); - - // pull out the card from this list - const card = listStart?.cards.splice(sourceIndex, 1); - - // find the list where drag ended - const listEnd = state.find( - (list) => destinationId === list._id - ); - - // put the card in the new list - if (card) { - listEnd?.cards.splice(destinationIndex, 0, ...card); - } - } - - return newState; - } - default: - return state; - } -}; - -export const undoableTasksReducer = undoable< - TaskTypes[], - TasksActionTypes ->(tasksReducer as any); diff --git a/app/renderer/src/store/tasks/types.ts b/app/renderer/src/store/tasks/types.ts index 3c1d584d..b9e74467 100644 --- a/app/renderer/src/store/tasks/types.ts +++ b/app/renderer/src/store/tasks/types.ts @@ -1,38 +1,20 @@ -const tasks = "[tasks]"; +import type { PayloadAction } from "@reduxjs/toolkit"; -export type CardTypes = { +export type ListPayload = PayloadAction< + TaskList[T] +>; +export type TaskPayload = PayloadAction; + +export type Task = { _id: string; text: string; description: string; done: boolean; }; -export type TaskTypes = { +export type TaskList = { _id: string; title: string; - cards: CardTypes[]; + cards: Task[]; priority: boolean; }; - -export const ADD_TASK_LIST = `${tasks} ADD_TASK_LIST`; -export const REMOVE_TASK_LIST = `${tasks} REMOVE_TASK_LIST`; -export const SET_TASK_LIST_PRIORITY = `${tasks} SET_TASK_LIST_PRIORITY`; - -export const EDIT_TASK_TITLE = `${tasks} EDIT_TASK_TITLE`; - -export const DRAG_LIST = `${tasks} DRAG_LIST`; - -export const ADD_TASK_CARD = `${tasks} ADD_TASK_CARD`; -export const REMOVE_TASK_CARD = `${tasks} REMOVE_TASK_CARD`; - -export const EDIT_TASK_CARD_TEXT = `${tasks} EDIT_TASK_CARD_TEXT`; -export const EDIT_TASK_CARD_DESCRIPTION = `${tasks} EDIT_TASK_CARD_DESCRIPTION`; - -export const SET_TASK_CARD_DONE = `${tasks} SET_TASK_CARD_DONE`; -export const SET_TASK_CARD_NOT_DONE = `${tasks} SET_TASK_CARD_NOT_DONE`; -export const SKIP_TASK_CARD = `${tasks} SKIP_TASK_CARD`; - -export type TasksActionTypes = { - type: typeof SKIP_TASK_CARD; - payload: any; -}; diff --git a/app/renderer/src/store/tasks/utils/task.ts b/app/renderer/src/store/tasks/utils/task.ts new file mode 100644 index 00000000..fc923bc5 --- /dev/null +++ b/app/renderer/src/store/tasks/utils/task.ts @@ -0,0 +1,25 @@ +import { v4 as uuid } from "uuid"; +import type { Task } from "../types"; + +type CreateTaskParams = Pick & + Partial>; +type EditableTaskParams = Partial>; + +export const createTask = ({ + text, + description = "", +}: CreateTaskParams): Task => { + return { + _id: uuid(), + text, + description, + done: false, + }; +}; + +export const editTask = ( + task: Task, + changedFields: EditableTaskParams +): Task => { + return { ...task, ...changedFields }; +}; diff --git a/app/renderer/src/store/tasks/utils/tasklist.ts b/app/renderer/src/store/tasks/utils/tasklist.ts new file mode 100644 index 00000000..b77269f4 --- /dev/null +++ b/app/renderer/src/store/tasks/utils/tasklist.ts @@ -0,0 +1,44 @@ +import { Task, TaskList } from "../types"; +import { v4 as uuid } from "uuid"; + +type CreateTaskListParams = Pick; +type EditableTaskListFields = Partial; + +export const createTaskList = ({ + title, + priority, +}: CreateTaskListParams): TaskList => { + return { + _id: uuid(), + title, + cards: [], + priority, + }; +}; + +export const editTaskList = ( + taskList: TaskList, + newFields: EditableTaskListFields +): TaskList => { + return { ...taskList, ...newFields }; +}; + +export const addTaskToList = ( + taskList: TaskList, + task: Task +): TaskList => { + return { + ...taskList, + cards: [...taskList.cards, task], + }; +}; + +export const removeTaskFromList = ( + taskList: TaskList, + taskId: string +): TaskList => { + return { + ...taskList, + cards: taskList.cards.filter((task) => task._id !== taskId), + }; +}; diff --git a/yarn.lock b/yarn.lock index da65d750..8d76d1d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14641,16 +14641,16 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== +prettier@2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + prettier@^1.14.2: version "1.19.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -prettier@^2.8.8: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"