Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(input): if folder does not exist, the form does not fail #116

Merged
merged 3 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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