various feat, fix, debug

- task가 없을 때 보이는 note 화면을 제작
- note folder가 존재하지 않는 경우 자동으로 생성
- task, group이 없을 때 에러가 나던 현상을 해결
-  group에 task가 없을 때, 완료로 표시되던 것을 해결
- 다양한 modal 창에서 enter를 누르면 save되도록 단축키 추가
- activate-view 함수에서 바로 leaf가 튀어나오지 않도록 수정
- open routine view command 추가
- .env 파일을 사용하여 환경 변수를 받을 수 있도록 추가가
This commit is contained in:
sechan100 2025-01-31 17:17:13 +09:00
parent 0f86b2dd62
commit a574beef46
12 changed files with 89 additions and 35 deletions

3
.gitignore vendored
View file

@ -5,6 +5,9 @@
*.iml
.idea
# env
.env
# npm
node_modules

View file

@ -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);

13
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -1,6 +0,0 @@
import { doConfirm } from "@shared/components/modal/confirm-modal"
export const devRunner = () => {
}

View file

@ -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<RoutineNote> => {
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;

View file

@ -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")){

View file

@ -40,7 +40,7 @@ export const BaseTaskGroupFeature = React.memo(({
const [groupMode, setGroupMode] = useState<GroupMode>("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);

View file

@ -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() {

View file

@ -115,19 +115,38 @@ export const RoutineNoteContent = () => {
overflowY: "auto",
}}
>
<TaskDndContext> {note.children.map(el => {
if(isTaskGroup(el)) {
<TaskDndContext>
{(() => {
if(note.children.length === 0) {
return (
<TaskGroupWidget
key={`${el.elementType}-${el.name}`}
group={el}
/>)
} else if(isTask(el)) {
return renderTask(el, null);
} else {
return null;
<div css={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100%",
color: "var(--text-muted)",
fontSize: "1.2em",
}}>
No Tasks.. Add some todos or routines 🤗
</div>
)
}
})}</TaskDndContext>
return note.children.map(el => {
if(isTaskGroup(el)) {
return (
<TaskGroupWidget
key={`${el.elementType}-${el.name}`}
group={el}
/>)
} else if(isTask(el)) {
return renderTask(el, null);
} else {
return null;
}
})
})()}
</TaskDndContext>
</div>
<AddTodoModal />
<StartRoutineModal />

View file

@ -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 (
<div css={{

View file

@ -21,8 +21,7 @@ export const activateView = async (viewTypeName: string, pos = 0) => {
if(!leaf) {
leaf = getLeaf() as WorkspaceLeaf;
await leaf.setViewState({ type: viewTypeName });
await leaf.setViewState({ type: viewTypeName, active: false });
}
app.workspace.revealLeaf(leaf);
}