Skip to content

Commit

Permalink
Merge pull request #116 from danielo515/fix-safer-note-source
Browse files Browse the repository at this point in the history
fix(input): if folder does not exist, the form does not fail
  • Loading branch information
danielo515 authored Nov 4, 2023
2 parents 229db20 + 8567b8a commit 9737e99
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 108 deletions.
2 changes: 1 addition & 1 deletion src/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MigrationError } from "./core/formDefinitionSchema";
import FormResult from "./core/FormResult";
import { exampleModalDefinition } from "./exampleModalDefinition";
import ModalFormPlugin from "./main";
import { ModalFormError } from "./utils/Error";
import { ModalFormError } from "./utils/ModalFormError";
import { FormModal } from "./FormModal";
import { log_error, log_notice } from "./utils/Log";

Expand Down
43 changes: 31 additions & 12 deletions src/FormModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { FileSuggest } from "./suggesters/suggestFile";
import { DataviewSuggest } from "./suggesters/suggestFromDataview";
import { SvelteComponent } from "svelte";
import { executeSandboxedDvQuery, sandboxedDvQuery } from "./suggesters/SafeDataviewQuery";
import { pipe } from "fp-ts/lib/function";
import { A, E } from "@std";
import { log_error } from "./utils/Log";

export type SubmitFn = (formResult: FormResult) => void;

Expand Down Expand Up @@ -136,7 +139,14 @@ export class FormModal extends Modal {
const options = source == 'fixed'
? fieldInput.multi_select_options
: source == 'notes'
? get_tfiles_from_folder(fieldInput.folder, this.app).map((file) => file.basename)
? pipe(
get_tfiles_from_folder(fieldInput.folder, this.app),
E.map(A.map((file) => file.basename)),
E.getOrElse((err) => {
log_error(err)
return [] as string[];
})
)
: executeSandboxedDvQuery(sandboxedDvQuery(fieldInput.query), this.app)
this.svelteComponents.push(new MultiSelect({
target: fieldBase.controlEl,
Expand Down Expand Up @@ -182,18 +192,27 @@ export class FormModal extends Modal {
case "notes":
return fieldBase.addDropdown((element) => {
const files = get_tfiles_from_folder(fieldInput.folder, this.app);
const options = files.reduce(
(
acc: Record<string, string>,
option
) => {
acc[option.basename] =
option.basename;
return acc;
},
{}
pipe(
files,
E.map((files) => files.reduce(
(
acc: Record<string, string>,
option
) => {
acc[option.basename] =
option.basename;
return acc;
},
{}
)),
E.mapLeft((err) => {
log_error(err);
return err;
}),
E.map((options) => {
element.addOptions(options)
})
);
element.addOptions(options);
this.formResult[definition.name] = element.getValue();
element.onChange(async (value) => {
this.formResult[definition.name] =
Expand Down
2 changes: 1 addition & 1 deletion src/core/FormResult.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { objectSelect } from './objectSelect';
import { stringifyYaml } from "obsidian";
import { log_error } from "../utils/Log";
import { ModalFormError } from "../utils/Error";
import { ModalFormError } from "../utils/ModalFormError";

type ResultStatus = "ok" | "cancelled";

Expand Down
2 changes: 2 additions & 0 deletions src/core/formDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ export type EditableFormDefinition = {
label?: string;
description: string;
input: EditableInput;
folder?: string;
options?: { value: string; label: string }[];
}[];
};

Expand Down
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ModalFormSettingTab } from "src/ModalFormSettingTab";
import { API } from "src/API";
import { EDIT_FORM_VIEW, EditFormView } from "src/views/EditFormView";
import { MANAGE_FORMS_VIEW, ManageFormsView } from "src/views/ManageFormsView";
import { ModalFormError } from "src/utils/Error";
import { ModalFormError } from "src/utils/ModalFormError";
import { type FormDefinition } from "src/core/formDefinition";
import { formNeedsMigration, migrateToLatest, MigrationError, InvalidData } from "./core/formDefinitionSchema";
import { parseSettings, type ModalFormSettings, type OpenPosition, getDefaultSettings } from "src/core/settings";
Expand Down
3 changes: 2 additions & 1 deletion src/std/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { pipe as p, flow as f } from "fp-ts/function";
import { partitionMap, findFirst, findFirstMap, partition, map as mapArr, filter } from "fp-ts/Array";
import { map as mapO, getOrElse as getOrElseOpt, some, none } from 'fp-ts/Option'
import { isLeft, isRight, tryCatchK, map, getOrElse, right, left, mapLeft, Either, bimap, tryCatch, flatMap } from "fp-ts/Either";
import { isLeft, isRight, tryCatchK, map, getOrElse, fromNullable, right, left, mapLeft, Either, bimap, tryCatch, flatMap } from "fp-ts/Either";
import { BaseSchema, Output, ValiError, parse as parseV } from "valibot";
import { Semigroup, concatAll } from "fp-ts/Semigroup";
import { NonEmptyArray } from "fp-ts/NonEmptyArray";
Expand Down Expand Up @@ -30,6 +30,7 @@ export const E = {
mapLeft,
bimap,
flatMap,
fromNullable,
}

export const O = {
Expand Down
2 changes: 1 addition & 1 deletion src/suggesters/SafeDataviewQuery.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { E, Either, flow } from "@std";
import { pipe } from "fp-ts/lib/function";
import { App } from "obsidian";
import { ModalFormError } from "src/utils/Error";
import { ModalFormError } from "src/utils/ModalFormError";
import { log_error } from "src/utils/Log";

type DataviewQuery = (dv: unknown, pages: unknown) => unknown;
Expand Down
12 changes: 5 additions & 7 deletions src/suggesters/suggestFile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AbstractInputSuggest, App, TAbstractFile, TFile } from "obsidian";
import { get_tfiles_from_folder } from "../utils/files";
import { tryCatch } from "../utils/Error";
import { E } from "@std";

// Instead of hardcoding the logic in separate and almost identical classes,
// we move this little logic parts into an interface and we can use the samme
Expand All @@ -10,6 +10,7 @@ export interface FileStrategy {
selectSuggestion(file: TFile): string;
}


export class FileSuggest extends AbstractInputSuggest<TFile> {
constructor(
public app: App,
Expand All @@ -21,17 +22,14 @@ export class FileSuggest extends AbstractInputSuggest<TFile> {
}

getSuggestions(input_str: string): TFile[] {
const all_files = tryCatch(
() => get_tfiles_from_folder(this.folder, this.app),
"The folder does not exist"
);
if (!all_files) {
const all_files = get_tfiles_from_folder(this.folder, this.app)
if (E.isLeft(all_files)) {
return [];
}

const lower_input_str = input_str.toLowerCase();

return all_files.filter((file: TAbstractFile) => {
return all_files.right.filter((file: TAbstractFile) => {
return (
file instanceof TFile &&
file.extension === "md" &&
Expand Down
45 changes: 0 additions & 45 deletions src/utils/Error.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/utils/Log.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Notice } from "obsidian";
import { ModalFormError } from "./Error";
import { ModalFormError } from "./ModalFormError";


export function log_notice(title: string, msg: string | DocumentFragment): void {
Expand Down
7 changes: 7 additions & 0 deletions src/utils/ModalFormError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class ModalFormError extends Error {
constructor(msg: string, public console_msg?: string) {
super(msg);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
102 changes: 65 additions & 37 deletions src/utils/files.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,75 @@
import { App, TAbstractFile, TFile, TFolder, Vault, normalizePath } from "obsidian";
import { ModalFormError } from "./Error";

export function resolve_tfolder(folder_str: string, app: App): TFolder {
folder_str = normalizePath(folder_str);

const folder = app.vault.getAbstractFileByPath(folder_str);
if (!folder) {
throw new ModalFormError(`Folder "${folder_str}" doesn't exist`);
}
if (!(folder instanceof TFolder)) {
throw new ModalFormError(`${folder_str} is a file, not a folder`);
}

return folder;
import { E, Either, pipe } from "@std";
export class FolderDoesNotExistError extends Error {
static readonly tag = "FolderDoesNotExistError";
}

export function resolve_tfile(file_str: string, app: App): TFile {
file_str = normalizePath(file_str);

const file = app.vault.getAbstractFileByPath(file_str);
if (!file) {
throw new ModalFormError(`File "${file_str}" doesn't exist`);
}
if (!(file instanceof TFile)) {
throw new ModalFormError(`${file_str} is a folder, not a file`);
}
export class NotAFolderError extends Error {
static readonly tag = "NotAFolderError";
constructor(public file: TAbstractFile) {
super(`File ${file.path} is not a folder`);
}
}

return file;
export class FileDoesNotExistError extends Error {
static readonly tag = "FileDoesNotExistError";
static of(file: string) {
return new FileDoesNotExistError(`File "${file}" doesn't exist`);
}
}
export class NotAFileError extends Error {
static readonly tag = "NotAFileError";
constructor(public file: TAbstractFile) {
super(`File ${file.path} is not a file`);
}
}

export function get_tfiles_from_folder(folder_str: string, app: App): Array<TFile> {
const folder = resolve_tfolder(folder_str, app);
type FolderError = FolderDoesNotExistError | NotAFolderError;

const files: Array<TFile> = [];
Vault.recurseChildren(folder, (file: TAbstractFile) => {
if (file instanceof TFile) {
files.push(file);
}
});
export function resolve_tfolder(folder_str: string, app: App): Either<FolderError, TFolder> {
return pipe(
normalizePath(folder_str),
(path) => app.vault.getAbstractFileByPath(path),
E.fromNullable(new FolderDoesNotExistError(`Folder "${folder_str}" doesn't exist`)),
E.flatMap((file) => {
if (!(file instanceof TFolder)) {
return E.left(new NotAFolderError(file));
}
return E.right(file);
})
);
}

files.sort((a, b) => {
return a.basename.localeCompare(b.basename);
});
export function resolve_tfile(file_str: string, app: App): Either<FileDoesNotExistError | NotAFileError, TFile> {
return pipe(
normalizePath(file_str),
(path) => app.vault.getAbstractFileByPath(path),
E.fromNullable(FileDoesNotExistError.of(file_str)),
E.flatMap((file) => {
if (!(file instanceof TFile)) {
return E.left(new NotAFileError(file));
}
return E.right(file);
})
)
}

return files;
export function get_tfiles_from_folder(folder_str: string, app: App): Either<FolderError, Array<TFile>> {
return pipe(
resolve_tfolder(folder_str, app),
E.flatMap((folder) => {
const files: Array<TFile> = [];
Vault.recurseChildren(folder, (file: TAbstractFile) => {
if (file instanceof TFile) {
files.push(file);
}
});
return E.right(files);
}),
E.map((files) => {
return files.sort((a, b) => {
return a.basename.localeCompare(b.basename);
});
}
))
}
2 changes: 1 addition & 1 deletion src/views/FormBuilder.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import InputBuilderSelect from "./components/InputBuilderSelect.svelte";
import InputFolder from "./components/InputFolder.svelte";
import { log_error } from "src/utils/Log";
import { ModalFormError } from "src/utils/Error";
import { ModalFormError } from "src/utils/ModalFormError";
export let definition: EditableFormDefinition = {
title: "",
Expand Down

0 comments on commit 9737e99

Please sign in to comment.