diff --git a/.gitignore b/.gitignore index cf928ae..04da5d7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ *.iml .idea +# env +.env + # npm node_modules diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 7f1be78..87c07d2 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -5,6 +5,7 @@ import fs from "node:fs"; import path from "path"; import { dirname } from 'path'; import { fileURLToPath } from 'url'; +import dotenv from 'dotenv'; const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD @@ -104,9 +105,8 @@ async function copyPluginFilesTo_iCloud() { 'styles.css', ]; - const destinationDir = '/Users/sechan/Library/Mobile Documents/iCloud~md~obsidian/Documents/main/.obsidian/plugins/daily-routine-2/'; - // const destinationDir = '/Users/sechan/Library/Mobile Documents/iCloud~md~obsidian/Documents/cloud-test/.obsidian/plugins/daily-routine-2/'; - + dotenv.config(); + const destinationDir = process.env.ICLOUD_DIRECTORY; // 현재 파일의 URL을 파일 경로로 변환 const __filename = fileURLToPath(import.meta.url); diff --git a/package-lock.json b/package-lock.json index 60e9474..c40f8e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@popperjs/core": "^2.11.8", "clsx": "^2.1.1", "dedent": "^1.5.3", + "dotenv": "^16.4.7", "eslint-plugin-react": "^7.37.1", "eslint-plugin-react-hooks": "^4.6.2", "lodash": "^4.17.21", @@ -3374,6 +3375,18 @@ "node": ">=12" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", diff --git a/package.json b/package.json index 6e93a65..4357f43 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@popperjs/core": "^2.11.8", "clsx": "^2.1.1", "dedent": "^1.5.3", + "dotenv": "^16.4.7", "eslint-plugin-react": "^7.37.1", "eslint-plugin-react-hooks": "^4.6.2", "lodash": "^4.17.21", diff --git a/src/dev-runner.tsx b/src/dev-runner.tsx deleted file mode 100644 index 111652d..0000000 --- a/src/dev-runner.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { doConfirm } from "@shared/components/modal/confirm-modal" - -export const devRunner = () => { - - -} \ No newline at end of file diff --git a/src/entities/note/repository/note-repository.ts b/src/entities/note/repository/note-repository.ts index ea5f466..3aa0109 100644 --- a/src/entities/note/repository/note-repository.ts +++ b/src/entities/note/repository/note-repository.ts @@ -1,7 +1,7 @@ import { ensureArchive } from "@entities/archives"; import { fileAccessor } from "@shared/file/file-accessor"; import { Day } from "@shared/period/day"; -import { TAbstractFile, TFile } from "obsidian"; +import { Notice, TAbstractFile, TFile } from "obsidian"; import { DR_SETTING } from "@app/settings/setting-provider"; import { doConfirm } from "@shared/components/modal/confirm-modal"; import { RoutineNote } from "../domain/note.type"; @@ -16,7 +16,12 @@ const parseNote = async (day: Day, file: TFile): Promise => { export const ROUTINE_NOTE_FILE = (day: Day): TFile | null => { const path = ROUTINE_ARCHIVE_PATH(day); - return fileAccessor.loadFile(path); + try { + return fileAccessor.loadFile(path); + } catch (e) { + new Notice(`Error while loading note file. Please check the path: ${path}`); + return null; + } }; export const ROUTINE_ARCHIVE_PATH = (day: Day) => { @@ -86,7 +91,12 @@ export const noteRepository: NoteRepository = { const file = ROUTINE_NOTE_FILE(day); if(!file){ const path = ROUTINE_ARCHIVE_PATH(day); - await fileAccessor.createFile(path, serializeRoutineNote(routineNote)); + try { + await fileAccessor.createFile(path, serializeRoutineNote(routineNote)); + } catch (e) { + await fileAccessor.createFolder(DR_SETTING.noteFolderPath()); + await fileAccessor.createFile(path, serializeRoutineNote(routineNote)); + } return true; } else { return false; diff --git a/src/entities/note/repository/serializer.ts b/src/entities/note/repository/serializer.ts index 38b1f60..e582bb9 100644 --- a/src/entities/note/repository/serializer.ts +++ b/src/entities/note/repository/serializer.ts @@ -76,9 +76,15 @@ const parseTask = (line: string): Task => { } export const parseRoutineNote = (day: Day, content: string): RoutineNote => { + const formatCheckRegex = /# Tasks\s*.*/; + if(!formatCheckRegex.test(content)) throw new Error('invalid-note-format'); + const regex = /##\s+.*(?:\n(?!##|$).*)*/g; const blocks = content.match(regex); - if(!blocks) throw new Error('no task blocks found'); + if(!blocks) return { + day, + children: [] + }; const root: NoteElement[] = blocks.flatMap(b => { if(b.startsWith("## UNGROUPED")){ diff --git a/src/features/task-el/ui/BaseTaskGroupFeature.tsx b/src/features/task-el/ui/BaseTaskGroupFeature.tsx index addcb82..f4e3f15 100644 --- a/src/features/task-el/ui/BaseTaskGroupFeature.tsx +++ b/src/features/task-el/ui/BaseTaskGroupFeature.tsx @@ -40,7 +40,7 @@ export const BaseTaskGroupFeature = React.memo(({ const [groupMode, setGroupMode] = useState("idle"); const bgColor = useLeaf(s=>s.leafBgColor); const { note } = useRoutineNote(); - const isAllSubTasksChecked = group.children.every(t => t.state !== "un-checked") + const isAllSubTasksChecked = group.children.length !== 0 && group.children.every(t => t.state !== "un-checked") const [open, _setOpen] = useState(group.isOpen); diff --git a/src/main.ts b/src/main.ts index f447fdd..da246ab 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,7 +5,7 @@ import { DailyRoutinePluginSettings, DailyRoutineSettingTab, DEFAULT_SETTINGS } import { DailyRoutineObsidianView } from './app'; import { activateView } from '@shared/view/activate-view'; import { updateMomentConfig } from '@app/settings/moment-config'; -import { devRunner } from './dev-runner'; +import { DAILY_ROUTINE_ICON_NAME } from '@app/ui/daily-routine-icon'; export default class DailyRoutinePlugin extends Plugin { @@ -31,16 +31,16 @@ export default class DailyRoutinePlugin extends Plugin { ); this.addCommand({ - id: "daily-routine-dev-only-mobile-view-toggle", - name: "Toggle Mobile View", + id: "daily-routine-open-routine-view", + icon: DAILY_ROUTINE_ICON_NAME, + name: "Open Routine View", callback: () => { - // @ts-ignore - this.app.emulateMobile(!this.app.isMobile); + activateView(DailyRoutineObsidianView.VIEW_TYPE, 1); } }); + // this.app.emulateMobile(!this.app.isMobile); - devRunner(); - setTimeout(() => activateView(DailyRoutineObsidianView.VIEW_TYPE, 1), 500); + setTimeout(() => activateView(DailyRoutineObsidianView.VIEW_TYPE, 1), 0); } onunload() { diff --git a/src/pages/routine-note/Content.tsx b/src/pages/routine-note/Content.tsx index 72361dd..86dc31d 100644 --- a/src/pages/routine-note/Content.tsx +++ b/src/pages/routine-note/Content.tsx @@ -115,19 +115,38 @@ export const RoutineNoteContent = () => { overflowY: "auto", }} > - {note.children.map(el => { - if(isTaskGroup(el)) { + + {(() => { + if(note.children.length === 0) { return ( - ) - } else if(isTask(el)) { - return renderTask(el, null); - } else { - return null; +
+ No Tasks.. Add some todos or routines 🤗 +
+ ) } - })}
+ + return note.children.map(el => { + if(isTaskGroup(el)) { + return ( + ) + } else if(isTask(el)) { + return renderTask(el, null); + } else { + return null; + } + }) + })()} +
diff --git a/src/shared/components/modal/styled.tsx b/src/shared/components/modal/styled.tsx index f7b70a8..d22dcda 100644 --- a/src/shared/components/modal/styled.tsx +++ b/src/shared/components/modal/styled.tsx @@ -4,7 +4,7 @@ /** @jsxImportSource @emotion/react */ import { ToggleComponent } from "@shared/components/ToggleComponent"; import { TEXT_CSS } from '../text-style'; -import { useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { ModalApi } from './create-modal'; import { TextEditComponent } from "@shared/components/TextEditComponent" import { Button } from "@shared/components/Button" @@ -172,6 +172,15 @@ export const SaveBtn = ({ onSaveBtnClick, name, }: SaveBtnProps) => { + + const handleEnterKeyDown = useCallback((e: KeyboardEvent) => { + if(e.key === "Enter" && !disabled) onSaveBtnClick?.(); + }, [disabled, onSaveBtnClick]); + + useEffect(() => { + document.addEventListener('keydown', handleEnterKeyDown, true); + return () => document.removeEventListener('keydown', handleEnterKeyDown); + }, [handleEnterKeyDown]); return (
{ if(!leaf) { leaf = getLeaf() as WorkspaceLeaf; - await leaf.setViewState({ type: viewTypeName }); + await leaf.setViewState({ type: viewTypeName, active: false }); } - app.workspace.revealLeaf(leaf); } \ No newline at end of file