mirror of
https://github.com/sechan100/daily-routine-2.git
synced 2026-07-22 05:37:51 +00:00
debug: routine이 두번 생성되는 버그를 해결
- 한국어 입력의 경우 isComposing 속성으로 인해서 enter 이벤트가 두번 발생해서 routine 생성 이벤트가 2번 발생함
This commit is contained in:
parent
e3b567538a
commit
04a04b71bf
5 changed files with 54 additions and 15 deletions
|
|
@ -8,8 +8,9 @@ import { RoutineGroupEntity } from "../domain/routine-group";
|
|||
|
||||
|
||||
const parse = async (file: TFile): Promise<RoutineGroup> => {
|
||||
const fm = await fileAccessor.loadFrontMatter(file);
|
||||
const name = file.basename.startsWith(GROUP_PREFIX) ? file.basename.slice(GROUP_PREFIX.length) : file.basename;
|
||||
const properties = RoutineGroupEntity.validateGroupProperties(fileAccessor.loadFrontMatter(file));
|
||||
const properties = RoutineGroupEntity.validateGroupProperties(fm);
|
||||
if(properties.isErr()) throw new Error(`[RoutineGroup '${name}' Parse Error] ${properties.error}`);
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,16 @@ import dedent from "dedent";
|
|||
import { Routine } from "../domain/routine-type";
|
||||
|
||||
|
||||
/**
|
||||
* File을 받아서 Routine 객체로 변환한다.
|
||||
*
|
||||
* 루틴을 저장소에서 읽어와 변환하는 로직은 모두 해당 함수를 사용한다.
|
||||
* @param file
|
||||
* @returns
|
||||
*/
|
||||
const parse = async (file: TFile): Promise<Routine> => {
|
||||
const result = RoutineEntity.validateRoutineProperties(fileAccessor.loadFrontMatter(file));
|
||||
const fm = await fileAccessor.loadFrontMatter(file);
|
||||
const result = RoutineEntity.validateRoutineProperties(fm);
|
||||
if(result.isErr()){
|
||||
new Notice(`Routine '${file.basename}' frontmatter error: ${result.error}`);
|
||||
throw new Error(`[Routine '${file.basename}' Parse Error] ${result.error}`);
|
||||
|
|
@ -32,7 +40,7 @@ export interface RoutineQuery {
|
|||
load(routineName: string): Promise<Routine>;
|
||||
}
|
||||
export interface RoutineRepository extends RoutineQuery {
|
||||
persist(entity: Routine): Promise<boolean>;
|
||||
create(entity: Routine): Promise<boolean>;
|
||||
delete(routineName: string): Promise<void>;
|
||||
changeName(originalName: string, newName: string): Promise<void>;
|
||||
update(routine: Routine): Promise<Routine>;
|
||||
|
|
@ -62,7 +70,7 @@ export const routineRepository: RoutineRepository = {
|
|||
return await parse(file);
|
||||
},
|
||||
|
||||
async persist(routine: Routine){
|
||||
async create(routine: Routine){
|
||||
const path = ROUTINE_PATH(routine.name);
|
||||
const file = fileAccessor.loadFile(path);
|
||||
if(!file){
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { useCallback, useEffect, useMemo } from 'react';
|
|||
import { ModalApi } from './create-modal';
|
||||
import { TextEditComponent } from "@shared/components/TextEditComponent"
|
||||
import { Button } from "@shared/components/Button"
|
||||
import { plugin } from "@shared/utils/plugin-service-locator";
|
||||
|
||||
|
||||
type ModalProps = {
|
||||
|
|
@ -173,14 +174,22 @@ export const SaveBtn = ({
|
|||
name,
|
||||
}: SaveBtnProps) => {
|
||||
|
||||
const handleEnterKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
if(e.key === "Enter" && !disabled) onSaveBtnClick?.();
|
||||
}, [disabled, onSaveBtnClick]);
|
||||
|
||||
/**
|
||||
* Enter Key로 Save Button을 트리거하는 이벤트를 등록
|
||||
*/
|
||||
useEffect(() => {
|
||||
|
||||
const handleEnterKeyDown = ({ key, isComposing}: KeyboardEvent) => {
|
||||
if(isComposing) return;
|
||||
|
||||
if(key === "Enter" && !disabled){
|
||||
onSaveBtnClick?.();
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', handleEnterKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleEnterKeyDown);
|
||||
}, [handleEnterKeyDown]);
|
||||
}, [disabled, onSaveBtnClick]);
|
||||
|
||||
return (
|
||||
<div css={{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { plugin } from "@shared/utils/plugin-service-locator";
|
||||
import { TFile, TFolder } from "obsidian";
|
||||
|
||||
import { TFile, TFolder, getFrontMatterInfo, parseYaml } from "obsidian";
|
||||
|
||||
|
||||
export interface FileAccessor {
|
||||
|
|
@ -58,7 +57,7 @@ export interface FileAccessor {
|
|||
*/
|
||||
writeFrontMatter: (file: TFile, frontMatterModifier: (frontmatter: any) => any) => Promise<void>;
|
||||
|
||||
loadFrontMatter: (file: TFile) => object | null;
|
||||
loadFrontMatter: (file: TFile) => Promise<object>;
|
||||
}
|
||||
|
||||
export const fileAccessor: FileAccessor = {
|
||||
|
|
@ -116,8 +115,30 @@ export const fileAccessor: FileAccessor = {
|
|||
});
|
||||
},
|
||||
|
||||
loadFrontMatter: (file: TFile) => {
|
||||
/**
|
||||
* 1차 시도는 metadataCache에서 frontmatter를 가져오고,
|
||||
* 실패하면 파일을 직접 읽어서 frontmatter를 파싱한다.
|
||||
* 그래도 안되면 에러를 발생시킨다.
|
||||
* @param file
|
||||
* @returns
|
||||
*/
|
||||
loadFrontMatter: async (file: TFile): Promise<object> => {
|
||||
const metadataCache = plugin().app.metadataCache.getFileCache(file);
|
||||
return metadataCache?.frontmatter ?? null;
|
||||
if(metadataCache && metadataCache.frontmatter){
|
||||
return metadataCache.frontmatter;
|
||||
} else {
|
||||
const content = await fileAccessor.readFileAsReadonly(file);
|
||||
return parseFrontmatterFromContent(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const parseFrontmatterFromContent = (fileContent: string): object => {
|
||||
const fmInfo = getFrontMatterInfo(fileContent);
|
||||
if(!fmInfo.exists){
|
||||
return {};
|
||||
}
|
||||
const yaml = fmInfo.frontmatter.replace('---', '').trim()
|
||||
return parseYaml(yaml);
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ export const useStartRoutineModal = createModal(({ modal }: StartRoutineModalPro
|
|||
const [routine, dispatch] = useReducer<RoutineReducer>(routineReducer, createNewRoutine());
|
||||
|
||||
const onSaveBtnClick = useCallback(async () => {
|
||||
await routineRepository.persist(routine);
|
||||
await routineRepository.create(routine);
|
||||
mergeNotes();
|
||||
modal.close();
|
||||
new Notice(`Routine '${routine.name}' started! 🎉`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue