diff --git a/package-lock.json b/package-lock.json index 8e28d22..d0a7c76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,12 @@ "license": "MIT", "dependencies": { "@popperjs/core": "^2.11.8", + "clsx": "^2.1.1", "i": "^0.3.7", "npm": "^10.8.3", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "zustand": "^5.0.0-rc.2" }, "devDependencies": { "@types/lodash": "^4.17.7", @@ -579,13 +581,13 @@ "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "dev": true + "devOptional": true }, "node_modules/@types/react": { "version": "18.3.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.8.tgz", "integrity": "sha512-syBUrW3/XpnW4WJ41Pft+I+aPoDVbrBVQGEnbD7NijDGlVC+8gV/XKRY+7vMDlfPpbwYt0l1vd/Sj8bJGMbs9Q==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -951,6 +953,14 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -997,7 +1007,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "devOptional": true }, "node_modules/debug": { "version": "4.3.6", @@ -4652,6 +4662,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "5.0.0-rc.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.0-rc.2.tgz", + "integrity": "sha512-o2Nwuvnk8vQBX7CcHL8WfFkZNJdxB/VKeWw0tNglw8p4cypsZ3tRT7rTRTDNeUPFS0qaMBRSKe+fVwL5xpcE3A==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index b35b4bb..c5b3f61 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,11 @@ }, "dependencies": { "@popperjs/core": "^2.11.8", + "clsx": "^2.1.1", "i": "^0.3.7", "npm": "^10.8.3", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "zustand": "^5.0.0-rc.2" } } diff --git a/src/entities/archive.ts b/src/entities/archive.ts index b518403..c88cbd4 100644 --- a/src/entities/archive.ts +++ b/src/entities/archive.ts @@ -5,6 +5,7 @@ import { Day } from "lib/day"; import { routineManager, Routine } from "./routine"; import { FileNotFoundError } from "lib/file/errors"; import { parseProperties } from "lib/file/utils"; +import moment from "moment"; export interface Task { @@ -24,11 +25,10 @@ export interface RoutineNote { export const routineNoteArchiver = { /** - * 오늘에 해당하는 RoutineNote를 archive에서 가져온다. + * day에 해당하는 RoutineNote를 archive에서 가져온다. * 만약 archive에 존재하지 않는다면 생성하고 저장한 뒤에 반환한다. */ - async getTodayRoutineNote(): Promise { - const day = Day.now(); + async getRoutineNote(day: Day): Promise { try { const file = archiveDAO.getRoutineNoteFile(day); return archiveDAO.parseFile(file); @@ -101,9 +101,8 @@ const archiveDAO = { return { name, checked }; }); - console.log(tasks) return { - day: new Day(props.day), + day: new Day(moment(props.day)), tasks }; }, @@ -161,7 +160,7 @@ const createNewRoutineNote = async (day: Day): Promise => { * routine으로부터 Task를 파생시킨다. * @param routine * @param day - * @returns RoutineTask | null 만약 routine이 day에 수행되어야하는 루틴이 아니라면 null을 반환한다. + * @returns RoutineTask | null 만약 routine이 day에 수행어야하는 루틴이 아니라면 null을 반환한다. */ const deriveRoutineTask = (routine: Routine, day: Day): RoutineTask | null => { if (!routine.properties.dayOfWeeks.contains(day.getDayOfWeek())) return null; diff --git a/src/features/routine-note/days.tsx b/src/features/routine-note/days.tsx new file mode 100644 index 0000000..11e0732 --- /dev/null +++ b/src/features/routine-note/days.tsx @@ -0,0 +1,33 @@ +import { Day } from "lib/day"; +import { useRoutineNote } from "./use-routine-note"; +import moment from "moment"; +import { ButtonComponent } from "obsidian"; +import clsx from "clsx"; + + +export const DaysMenu = () => { + const routineNote = useRoutineNote(s=>s.routineNote); + const setRoutineNote = useRoutineNote(s=>s.setRoutineNote); + + + const today = Day.now(); + return ( +
+
+ {today.getCurrentWeek().map((day, idx) => { + return ( + + ) + })} +
+
+ ) +} \ No newline at end of file diff --git a/src/features/routine-note/index.tsx b/src/features/routine-note/index.tsx index 72cfe5c..fa506b0 100644 --- a/src/features/routine-note/index.tsx +++ b/src/features/routine-note/index.tsx @@ -1,43 +1,32 @@ -import { RoutineNote, routineNoteArchiver, RoutineTask } from "entities/archive"; -import { RoutineComponent } from "./routine"; -import React, { useCallback } from "react"; +import { RoutineNote, routineNoteArchiver } from "entities/archive"; +import React, { createContext, useContext, useEffect, useRef, useState } from "react"; import "./style.css"; -import { routineManager } from "entities/routine"; - +import { NoteView } from "./note"; +import { DaysMenu } from "./days"; +import { Day } from "lib/day"; +import { useRoutineNote, UseRoutineNoteContext } from "./use-routine-note"; +// eslint-disable-next-line @typescript-eslint/no-empty-interface interface Props { - routineNote: RoutineNote } -export const RoutineNoteView = ({ routineNote }: Props) => { +export const RoutineNoteView = () => { + const todayRoutineNoteRef = useRef({ + day: Day.now(), + tasks: [], + }); - // 루틴 체크, 혹은 체크해제시 콜백 - const onCheckChange = useCallback((routine: RoutineTask, checked: boolean) => { - // 개별루틴 업데이트 - routineManager.updateAchievement({ - routineName: routine.name, - day: routineNote.day, - checked + useEffect(() => { + routineNoteArchiver.getRoutineNote(Day.now()) + .then(note => { + todayRoutineNoteRef.current = note; }); - - // 루틴노트 업데이트 - routineNoteArchiver.updateTaskCheck(routineNote, routine.name, checked); - }, [routineNote]); - + }, []); return ( -
-
-

Daily Routine

- {routineNote.day.getAsUserCustomFormat()} -
- {routineNote.tasks.map((task, idx) => { - return ( -
- -
- ) - })} -
- ); -} + + + + + ); +} \ No newline at end of file diff --git a/src/features/routine-note/note.tsx b/src/features/routine-note/note.tsx new file mode 100644 index 0000000..a5a2747 --- /dev/null +++ b/src/features/routine-note/note.tsx @@ -0,0 +1,43 @@ +import { RoutineNote, RoutineTask, routineNoteArchiver } from "entities/archive"; +import { routineManager } from "entities/routine"; +import { useCallback } from "react"; +import { RoutineComponent } from "./routine"; +import { useRoutineNote } from "./use-routine-note"; + + + +export const NoteView = () => { + const routineNote = useRoutineNote(s=>s.routineNote); + console.log(routineNote); + + // 루틴 체크, 혹은 체크해제시 콜백 + const onCheckChange = useCallback((routine: RoutineTask, checked: boolean) => { + // 개별루틴 업데이트 + routineManager.updateAchievement({ + routineName: routine.name, + day: routineNote.day, + checked + }); + + + // 루틴노트 업데이트 + routineNoteArchiver.updateTaskCheck(routineNote, routine.name, checked); + }, [routineNote]); + + + return ( +
+
+

Daily Routine

+ {routineNote.day.getAsUserCustomFormat()} +
+ {routineNote.tasks.map((task, idx) => { + return ( +
+ +
+ ) + })} +
+ ); +} \ No newline at end of file diff --git a/src/features/routine-note/routine.tsx b/src/features/routine-note/routine.tsx index 0602ba6..1ef4673 100644 --- a/src/features/routine-note/routine.tsx +++ b/src/features/routine-note/routine.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useState } from "react" import { RoutineTask } from "entities/archive"; @@ -8,6 +8,7 @@ interface Props { onCheckChange: (routine: RoutineTask, checked: boolean) => void; } export const RoutineComponent = ({ task: routine, onCheckChange }: Props) => { + const [checked, setChecked] = useState(routine.checked); const id = `routine-${routine.name}` @@ -16,7 +17,10 @@ export const RoutineComponent = ({ task: routine, onCheckChange }: Props) => { {/* 전체적으로 감싸주는 label */}