Skip to content

Commit

Permalink
Merge branch 'feature/task-action'
Browse files Browse the repository at this point in the history
  • Loading branch information
RafaelGB committed May 2, 2024
2 parents ddb01b8 + 5c42569 commit 69a3794
Show file tree
Hide file tree
Showing 18 changed files with 331 additions and 30 deletions.
11 changes: 6 additions & 5 deletions WorkFlow Test/Workflow of new notes.canvas

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions docs/actions/TaskManagement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Task Management Action
Manage the rollover of tasks given the next properties:

* **Regex**: The regex to find the tasks in the notes.
* **Folder**: The folder where the notes will be searched.
* **Prefix**: The prefix to add to the task when it's rollovered.
* **Suffix**: The suffix to add to the task when it's rollovered.
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ They will be shown as a list of options to select from. The options are:
- **[Backlink](./actions/Backlink.md)**: Insert the wikilink of the built-in note template in the heading note that you have configured.
- **[Tags](./actions/Tags.md)**: Add tags to the built-in note template as property.
- **[CssClasses](./actions/CssClasses.md)**: Add a css class to the built-in note template as property.
- **[TaskManagement](./actions/TaskManagement.md)**: Rollover the not completed task given regex and folders.
- **[Script](./steps/Script.md)**: Executes a JavaScript script when the workflow is run. Configure the script with the code editor displayed in the settings of the action.
3 changes: 2 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ nav:
- 2.6 Tags: actions/Tags.md
- 2.7 Backlink: actions/Backlink.md
- 2.8 CssClasses: actions/CssClasses.md
- 2.9 Script: actions/Script.md
- 2.9 TaskManagement: actions/TaskManagement.md
- 2.10 Script: actions/Script.md
- 3. How to Contribute:
- 3.1 Wiki: how-to-contribute/Wiki.md
extra_javascript:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"author": "RafaelGB",
"license": "MIT",
"devDependencies": {
"@types/node": "20.12.7",
"@types/node": "20.12.8",
"@types/react": "18.3.1",
"@types/react-dom": "18.3.0",
"@types/uuid": "9.0.8",
Expand Down
2 changes: 1 addition & 1 deletion src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export { TagsAction } from './tags/TagsAction';
export { CssClassesAction } from './cssClasses/CssClassesAction';
export { ScriptAction } from './script/ScriptAction';
export { CheckboxAction } from './checkbox/CheckboxAction';

export { TaskManagementAction } from './taskManagement/TaskManagementAction';
export { CodeView } from './script/view/CodeView';
80 changes: 80 additions & 0 deletions src/actions/taskManagement/TaskManagementAction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { CustomZettelAction, ExecuteInfo } from "architecture/api";
import { FileService } from "architecture/plugin";
import { Notice, TFile } from "obsidian";
import { taskManagementSettings } from "./TaskManagementSettings";
import { TaskManagementElement } from "./typing";
import { t } from "architecture/lang";
import { log } from "architecture";

export class TaskManagementAction extends CustomZettelAction {
private static ICON = "list-checks";
id = "task-management";
defaultAction = {
type: this.id,
description: "Task Management",
hasUI: false,
id: this.id,
};

settings = taskManagementSettings;

getIcon(): string {
return TaskManagementAction.ICON;
}

async execute(info: ExecuteInfo): Promise<void> {
const { content } = info;
const {
initialFolder,
regex,
rollupHeader,
prefix = "",
suffix = "",
} = info.element as TaskManagementElement;
const initFilesToSearch = FileService.getTfilesFromFolder(
initialFolder || "/",
["md"]
);

log.debug(`Initial files to search: ${initFilesToSearch.length}`);
// Filter by regex
const compiledRegex = new RegExp(regex);
const filesToSearch = initFilesToSearch.filter((file) =>
file.basename.match(compiledRegex)
);
log.debug(`Files to search after regex: ${filesToSearch.length}`);

// Get all unfinished todos
const unfinishedTodos = [];
for (const file of filesToSearch) {
const todos = await getAllUnfinishedTodos(file, rollupHeader);
unfinishedTodos.push(...todos);
}
const normalizedTodos = unfinishedTodos
.map((task) => `${prefix}${task}${suffix}`)
.join("\n");

if (normalizedTodos.length !== 0) {
content.add(`${rollupHeader}\n${normalizedTodos}`);
}
}

getLabel(): string {
return t("type_option_task_management");
}
}

const getAllUnfinishedTodos = async (file: TFile, tasksHeader: string) => {
const contents = await FileService.getContent(file);
const contentsForDailyTasks = contents.split(tasksHeader)[1] || contents;
const unfinishedTodosRegex = /\t*- \[ \].*/g;
const unfinishedTodos = Array.from(
contentsForDailyTasks.matchAll(unfinishedTodosRegex)
).map(([todo]) => todo);
const fileWithoutTasks = contents.split(/.*\t*- \[ \].*\n?/g).join("");
await FileService.modify(file, fileWithoutTasks);
new Notice(
`Rollover ${unfinishedTodos.length} unfinished task/s from ${file.basename}`
);
return unfinishedTodos;
};
90 changes: 90 additions & 0 deletions src/actions/taskManagement/TaskManagementSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { ActionSetting } from "architecture/api";
import { t } from "architecture/lang";
import { Setting } from "obsidian";
import { TaskManagementElement } from "./typing";
import { FolderSuggest } from "architecture/settings";

export const taskManagementSettings: ActionSetting = (contentEl, _, action) => {
const { initialFolder, regex, rollupHeader, prefix, suffix } =
action as TaskManagementElement;

new Setting(contentEl)
.setName(t("step_builder_element_type_task_management_target_folder_title"))
.setDesc(
t("step_builder_element_type_task_management_target_folder_description")
)
.addSearch((cb) => {
new FolderSuggest(cb.inputEl);
cb.setPlaceholder("Example: path/to/folder")
.setValue(initialFolder || "")
.onChange((value: string) => {
if (value) {
action.initialFolder = value;
} else {
delete action.initialFolder;
}
});
});

new Setting(contentEl)
.setName(t("step_builder_element_type_task_management_regex_title"))
.setDesc(t("step_builder_element_type_task_management_regex_description"))
.addText((text) => {
text
.setPlaceholder(
t("step_builder_element_type_task_management_regex_placeholder")
)
.setValue(regex || "")
.onChange(async (value) => {
action.regex = value;
});
});

new Setting(contentEl)
.setName(t("step_builder_element_type_task_management_rollup_header_title"))
.setDesc(
t("step_builder_element_type_task_management_rollup_header_description")
)
.addText((text) => {
text
.setPlaceholder(
t(
"step_builder_element_type_task_management_rollup_header_placeholder"
)
)
.setValue(rollupHeader)
.onChange(async (value) => {
if (value) {
action.rollupHeader = value;
} else {
delete action.rollupHeader;
}
});
});

new Setting(contentEl)
.setName(t("step_builder_element_type_task_management_prefix_title"))
.setDesc(t("step_builder_element_type_task_management_prefix_description"))
.addText((text) => {
text.setValue(prefix || "").onChange(async (value) => {
action.prefix = value;
});
});

new Setting(contentEl)
.setName(t("step_builder_element_type_task_management_suffix_title"))
.setDesc(t("step_builder_element_type_task_management_suffix_description"))
.addText((text) => {
text
.setPlaceholder(
t("step_builder_element_type_task_management_suffix_placeholder")
)
.setValue(suffix || "!")
.onChange(async (value) => {
action.suffix = value;
});
});
if (action.suffix === undefined) {
action.suffix = "!";
}
};
9 changes: 9 additions & 0 deletions src/actions/taskManagement/typing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { FinalElement } from "application/notes";

export type TaskManagementElement = {
rollupHeader: string;
regex: string;
initialFolder?: string;
prefix?: string;
suffix?: string;
} & FinalElement;
13 changes: 7 additions & 6 deletions src/architecture/components/core/search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useOnClickAway, useScrollToSelected } from "architecture/hooks";
import ReactDOM from "react-dom";

export function Search<T>(props: SearchType<T>) {
const { onChange, options, placeholder } = props;
const { className, onChange, options, placeholder } = props;
// Refs
const ref = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -38,14 +38,14 @@ export function Search<T>(props: SearchType<T>) {
useEffect(() => {
if (visibleOptions && inputRef.current) {
const inputRect = inputRef.current.getBoundingClientRect();
const espacioAbajo = window.innerHeight - inputRect.bottom;
const espacioArriba = inputRect.top;
const spaceDown = window.innerHeight - inputRect.bottom;
const spaceUp = inputRect.top;

let top;
if (espacioAbajo >= optionsHeight || espacioAbajo > espacioArriba) {
top = inputRect.bottom; // Colocar debajo
if (spaceDown >= optionsHeight || spaceDown > spaceUp) {
top = inputRect.bottom; // render down
} else {
top = inputRect.top - optionsHeight; // Colocar arriba
top = inputRect.top - optionsHeight; // render up
}

setPosition({
Expand Down Expand Up @@ -95,6 +95,7 @@ export function Search<T>(props: SearchType<T>) {
<input
ref={inputRef}
type="search"
className={className}
value={value}
onChange={(e) => {
const value = e.target.value;
Expand Down
1 change: 1 addition & 0 deletions src/architecture/components/core/search/typing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export type SearchType<T> = {
onChange: (value: T | null) => void;
options: Record<string, T>;
placeholder?: string;
className?: string;
};
14 changes: 14 additions & 0 deletions src/architecture/lang/locale/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default {
type_option_selector: 'Selector',
type_option_tags: 'Tags',
type_option_cssclasses: 'CSS Classes',
type_option_task_management: 'Task Management',
menu_pane_create_new_step: 'Create new step',
menu_pane_edit_step: 'Edit step',
menu_pane_transform_note_into_step: 'Transform note into step',
Expand Down Expand Up @@ -118,6 +119,19 @@ export default {
step_builder_element_type_number_label_description: 'The label will be the title of your number',
step_builder_element_type_number_placeholder_title: 'Placeholder of number',
step_builder_element_type_number_placeholder_description: 'Helper text to display in the number',
step_builder_element_type_task_management_target_folder_title: 'Initial folder',
step_builder_element_type_task_management_target_folder_description: 'Choose the initial folder where the search will start to rollup the tasks',
step_builder_element_type_task_management_regex_title: 'Regex',
step_builder_element_type_task_management_regex_description: 'Regex to search notes with the desired tasks to rollup',
step_builder_element_type_task_management_regex_placeholder: '^my-regex-to-search-files*$',
step_builder_element_type_task_management_rollup_header_title: 'Header title',
step_builder_element_type_task_management_rollup_header_description: 'It will search for the heading into all the filtered notes and rollup the unfinished tasks',
step_builder_element_type_task_management_rollup_header_placeholder: '## Tasks',
step_builder_element_type_task_management_prefix_title: 'Prefix title',
step_builder_element_type_task_management_prefix_description: 'Prefix to add to the task after the rollup',
step_builder_element_type_task_management_suffix_title: 'Suffix title',
step_builder_element_type_task_management_suffix_description: 'Suffix to add to the task after the rollup',
step_builder_element_type_task_management_suffix_placeholder: '!',
step_builder_actions_management_title: 'Actions management',
step_builder_actions_management_add_action_tooltip: 'Add action',
// MENUS
Expand Down
14 changes: 14 additions & 0 deletions src/architecture/lang/locale/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default {
type_option_selector: 'Selector',
type_option_tags: 'Etiquetas',
type_option_cssclasses: 'Clases CSS',
type_option_task_management: 'Gestión de tareas',
menu_pane_create_new_step: 'Crear nuevo paso del ZettelFlow',
menu_pane_edit_step: 'Editar paso del ZettelFlow',
menu_pane_transform_note_into_step: 'Transformar a nota ZettelFlow',
Expand Down Expand Up @@ -119,6 +120,19 @@ export default {
step_builder_element_type_number_placeholder_description: 'Texto de ayuda para mostrar en el número',
step_builder_actions_management_title: 'Gestión de acciones',
step_builder_actions_management_add_action_tooltip: 'Añadir acción',
step_builder_element_type_task_management_target_folder_title: 'Carpeta inicial',
step_builder_element_type_task_management_target_folder_description: 'Carpeta inicial para buscar las notas con las tareas a rollup',
step_builder_element_type_task_management_regex_title: 'Regex para buscar tareas',
step_builder_element_type_task_management_regex_description: 'Regex para buscar las tareas en las notas',
step_builder_element_type_task_management_regex_placeholder: '^mi-filtro-regex-parabuscar-tareas*$',
step_builder_element_type_task_management_rollup_header_title: 'Encabezado a usar',
step_builder_element_type_task_management_rollup_header_description: 'Buscará el encabezado en todas las notas filtradas y agrupará las tareas no finalizadas',
step_builder_element_type_task_management_rollup_header_placeholder: '## Tareas',
step_builder_element_type_task_management_prefix_title: 'Prefijo para añadir a la tarea',
step_builder_element_type_task_management_prefix_description: 'Prefijo a añadir a la tarea tras del rollup',
step_builder_element_type_task_management_suffix_title: 'Sufijo para añadir a la tarea',
step_builder_element_type_task_management_suffix_description: 'Sufijo a añadir a la tarea tras del rollup',
step_builder_element_type_task_management_suffix_placeholder: '!',
// MENUS
editor_menu_rigth_click_title: 'Opciones del editor de ZettelFlow',
// NOTIFICATIONS
Expand Down
6 changes: 5 additions & 1 deletion src/architecture/plugin/services/FileService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ObsidianApi } from "architecture";
import { TAbstractFile, TFile, TFolder, Vault, normalizePath } from "obsidian";
import { DataWriteOptions, TAbstractFile, TFile, TFolder, Vault, normalizePath } from "obsidian";
export const FILE_EXTENSIONS = Object.freeze({
BASIC: ["md", "canvas"],
ONLY_CANVAS: ["canvas"],
Expand Down Expand Up @@ -53,6 +53,10 @@ export class FileService {
return await ObsidianApi.vault().cachedRead(file);
}

public static async modify(file: TFile, content: string, options?: DataWriteOptions | undefined): Promise<void> {
await ObsidianApi.vault().modify(file, content, options);
}

public static getFolder(folder_str: string): TFolder {
folder_str = normalizePath(folder_str);

Expand Down
3 changes: 2 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { actionsStore } from 'architecture/api/store/ActionsStore';
import {
BackLinkAction, CalendarAction, CheckboxAction, CodeView,
CssClassesAction, NumberAction, PromptAction, ScriptAction, SelectorAction,
TagsAction
TagsAction, TaskManagementAction
} from 'actions';
import { log } from 'architecture';
import { Hooks } from 'hooks';
Expand Down Expand Up @@ -59,5 +59,6 @@ export default class ZettelFlow extends Plugin {
actionsStore.registerAction(new TagsAction());
actionsStore.registerAction(new CssClassesAction());
actionsStore.registerAction(new ScriptAction());
actionsStore.registerAction(new TaskManagementAction());
}
}
Loading

0 comments on commit 69a3794

Please sign in to comment.