Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit 1df4f49

Browse files
committed
Merge branch '152-ability-to-rename-filenames-directly-in-the-database'
2 parents 271cf7f + f4dc5fa commit 1df4f49

File tree

10 files changed

+165
-66
lines changed

10 files changed

+165
-66
lines changed

src/cdm/TableStateInterface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export interface DataState {
4545
removeOptionForAllRows: (column: TableColumn, option: string, columns: TableColumn[],
4646
ddbbConfig: LocalSettings) => Promise<void>;
4747
dataviewRefresh: (column: TableColumn[], ddbbConfig: LocalSettings, filterConfig: FilterSettings) => void;
48+
renameFile: (rowIndex: number) => Promise<void>;
4849
}
4950
}
5051

src/components/RowContextMenu.tsx

Lines changed: 13 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import IconButton from "@mui/material/IconButton";
2-
import ListItemIcon from "@mui/material/ListItemIcon";
32
import { TableColumn } from "cdm/FolderModel";
43
import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
5-
import DeleteIcon from "@mui/icons-material/Delete";
64
import {
75
DEFAULT_COLUMN_CONFIG,
86
InputType,
97
MetadataDatabaseColumns,
108
} from "helpers/Constants";
119
import React from "react";
12-
import Menu from "@mui/material/Menu";
13-
import MenuItem from "@mui/material/MenuItem";
10+
import { showFileMenu } from "components/obsidianArq/commands";
1411

1512
const rowContextMenuColumn: TableColumn = {
1613
...MetadataDatabaseColumns.ROW_CONTEXT_MENU,
@@ -21,32 +18,21 @@ const rowContextMenuColumn: TableColumn = {
2118
cell: ({ row, table }) => {
2219
const { tableState } = table.options.meta;
2320
const rowActions = tableState.data((state) => state.actions);
24-
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
25-
const open = Boolean(anchorEl);
26-
27-
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
28-
row.getToggleSelectedHandler()({
29-
event: {
30-
target: {
31-
checked: !row.getIsSelected(),
32-
},
33-
},
34-
});
35-
setAnchorEl(event.currentTarget);
21+
const handleDeleteRow = () => {
22+
rowActions.removeRow(row.original);
3623
};
37-
const handleMenuClose = () => {
38-
setAnchorEl(null);
39-
row.getToggleSelectedHandler()({
40-
event: {
41-
target: {
42-
checked: !row.getIsSelected(),
43-
},
44-
},
45-
});
24+
25+
const handleRenameRow = () => {
26+
rowActions.renameFile(row.index);
4627
};
4728

48-
const handleDeleteRow = () => {
49-
rowActions.removeRow(row.original);
29+
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
30+
showFileMenu(
31+
row.original.__note__.getFile(),
32+
event.nativeEvent,
33+
handleDeleteRow,
34+
handleRenameRow
35+
);
5036
};
5137

5238
return (
@@ -66,31 +52,6 @@ const rowContextMenuColumn: TableColumn = {
6652
>
6753
<DragIndicatorIcon />
6854
</IconButton>
69-
<Menu
70-
anchorEl={anchorEl}
71-
id={`row-context-menu-${row.id}`}
72-
open={open}
73-
onClose={handleMenuClose}
74-
onClick={handleMenuClose}
75-
transformOrigin={{ horizontal: "left", vertical: "top" }}
76-
anchorOrigin={{ horizontal: "left", vertical: "bottom" }}
77-
key={`row-context-Menu-${row.id}`}
78-
>
79-
<MenuItem
80-
onClick={handleDeleteRow}
81-
key={`row-context-Menu-MenuItem-Delete-${row.id}`}
82-
>
83-
<ListItemIcon
84-
key={`row-context-Menu-MenuItem-ListItemIcon-${row.id}`}
85-
>
86-
<DeleteIcon
87-
fontSize="small"
88-
key={`row-context-Menu-MenuItem-DeleteIcon-${row.id}`}
89-
/>
90-
</ListItemIcon>
91-
Delete
92-
</MenuItem>
93-
</Menu>
9455
</>
9556
);
9657
},

src/components/cellTypes/MarkdownCell.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import { CellComponentProps } from "cdm/ComponentsModel";
22
import { renderMarkdown } from "components/obsidianArq/MarkdownRenderer";
33
import { c } from "helpers/StylesHelper";
4-
import { Link } from "obsidian-dataview";
54
import React, { useEffect, useRef } from "react";
65

76
const MarkdownCell = (mdProps: CellComponentProps) => {
87
const { defaultCell } = mdProps;
9-
const { cell } = defaultCell;
10-
const cellValue = cell.getValue() as string;
8+
const { table, row, column } = defaultCell;
9+
const { tableState } = table.options.meta;
10+
const markdownRow = tableState.data((state) => state.rows[row.index]);
1111
const mdRef = useRef<HTMLDivElement>();
1212
useEffect(() => {
1313
if (mdRef.current !== null) {
1414
mdRef.current.innerHTML = "";
15-
const split = cellValue.split("|");
15+
const split = markdownRow[column.id].toString().split("|");
1616
renderMarkdown(
1717
defaultCell,
1818
`[[${split[1]}|${split[0]}]]`,
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
Modal,
3+
TextComponent,
4+
} from "obsidian";
5+
6+
export class PromptModal extends Modal {
7+
private resolve: (value: string) => void;
8+
private reject: () => void;
9+
private submitted = false;
10+
private value: string;
11+
12+
constructor(
13+
private prompt_text: string,
14+
private default_value: string
15+
) {
16+
super(app);
17+
}
18+
19+
onOpen(): void {
20+
this.titleEl.setText(this.prompt_text);
21+
this.createForm();
22+
}
23+
24+
onClose(): void {
25+
this.contentEl.empty();
26+
if (!this.submitted) {
27+
this.reject();
28+
}
29+
}
30+
31+
createForm(): void {
32+
const div = this.contentEl.createDiv();
33+
div.addClass("templater-prompt-div");
34+
let textInput;
35+
textInput = new TextComponent(div);
36+
this.value = this.default_value ?? "";
37+
textInput.inputEl.addClass("templater-prompt-input");
38+
textInput.setPlaceholder("Type text here");
39+
textInput.setValue(this.value);
40+
textInput.onChange((value) => (this.value = value));
41+
textInput.inputEl.addEventListener("keydown", (evt: KeyboardEvent) =>
42+
this.enterCallback(evt)
43+
);
44+
}
45+
46+
private enterCallback(evt: KeyboardEvent) {
47+
if (evt.key === "Enter") {
48+
this.resolveAndClose(evt);
49+
}
50+
51+
}
52+
53+
private resolveAndClose(evt: Event | KeyboardEvent) {
54+
this.submitted = true;
55+
evt.preventDefault();
56+
this.resolve(this.value);
57+
this.close();
58+
}
59+
60+
async openAndGetValue(
61+
resolve: (value: string) => void,
62+
reject: () => void
63+
): Promise<void> {
64+
this.resolve = resolve;
65+
this.reject = reject;
66+
this.open();
67+
}
68+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Menu, TFile } from "obsidian";
2+
3+
/**
4+
* entry arguments for the command <br/>
5+
* on(
6+
* name: 'file-menu',
7+
* callback: (
8+
* menu: Menu,
9+
* file: TAbstractFile,
10+
* source: string,
11+
* leaf?: WorkspaceLeaf
12+
* ) => any, ctx?: any
13+
* ): EventRef;
14+
* @param file
15+
* @param position
16+
*/
17+
export function showFileMenu(file: TFile, event: MouseEvent, removeRow: () => void, rewriteFileOfRow: () => void) {
18+
const fileMenu = new Menu();
19+
fileMenu.addItem((item) => item
20+
.setTitle("Delete")
21+
.setIcon("trash")
22+
.onClick(removeRow));
23+
fileMenu.addItem((item) => item
24+
.setTitle("Rename")
25+
.setIcon("pencil")
26+
.onClick(rewriteFileOfRow));
27+
28+
app.workspace.trigger("file-menu", fileMenu, file, null, app.workspace.getMostRecentLeaf());
29+
fileMenu.showAtMouseEvent(event);
30+
}

src/stateManagement/data/DataStateActions.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import RemoveDataOfColumnHandlerAction from "stateManagement/data/handlers/Remov
88
import ParseDataOfColumnHandlerAction from "stateManagement/data/handlers/ParseDataOfColumnHandlerAction";
99
import DataviewRefreshHandlerAction from "stateManagement/data/handlers/DataviewRefreshHandlerAction";
1010
import RemoveOptionForAllRowsAction from "stateManagement/data/handlers/RemoveOptionForAllRowsAction";
11+
import RenameFileHandlerAction from "stateManagement/data/handlers/RenameFileHandlerAction";
1112
import { AbstractHandler } from "patterns/AbstractHandler";
1213

1314
class DataStateActions extends AbstractChain<TableActionResponse<DataState>> {
@@ -20,7 +21,8 @@ class DataStateActions extends AbstractChain<TableActionResponse<DataState>> {
2021
new RemoveRowHandlerAction(),
2122
new RemoveDataOfColumnHandlerAction(),
2223
new DataviewRefreshHandlerAction(),
23-
new RemoveOptionForAllRowsAction()
24+
new RemoveOptionForAllRowsAction(),
25+
new RenameFileHandlerAction()
2426
];
2527
}
2628
}

src/stateManagement/data/handlers/AddRowHandlerAction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default class AddRowlHandlerAction extends AbstractTableAction<DataState>
5858
...rowRecord.inline,
5959
file: { path: filepath },
6060
}),
61-
[MetadataColumns.FILE]: `[[${filepath}|${filename}]]`,
61+
[MetadataColumns.FILE]: `${filename}|${filepath}`,
6262
};
6363
return { rows: [...state.rows, row] }
6464
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { DataState, TableActionResponse } from "cdm/TableStateInterface";
2+
import { PromptModal } from "components/modals/PromptModal";
3+
import { MetadataColumns } from "helpers/Constants";
4+
import { resolve_tfile } from "helpers/FileManagement";
5+
import { Notice } from "obsidian";
6+
import { AbstractTableAction } from "stateManagement/AbstractTableAction";
7+
8+
export default class RenameFileHandlerAction extends AbstractTableAction<DataState> {
9+
handle(tableActionResponse: TableActionResponse<DataState>): TableActionResponse<DataState> {
10+
const { set, get, implementation } = tableActionResponse;
11+
implementation.actions.renameFile = async (rowIndex: number) => {
12+
try {
13+
const rowToRename = get().rows[rowIndex];
14+
15+
const oldFile = rowToRename[MetadataColumns.FILE].toString().split("|");
16+
const prompt_filename = new PromptModal(oldFile[0], "");
17+
18+
const renameFilePromise = (newFilename: string) => {
19+
const oldTfile = resolve_tfile(oldFile[1]);
20+
const newPath = `${oldTfile.parent.path}/${newFilename}.md`;
21+
app.vault.rename(oldTfile, newPath);
22+
const newFile = `${newFilename}|${newPath}`;
23+
rowToRename.__note__.filepath = newPath;
24+
rowToRename[MetadataColumns.FILE] = newFile;
25+
set((state) => {
26+
return {
27+
rows: [...state.rows.slice(0, rowIndex), rowToRename, ...state.rows.slice(rowIndex + 1)]
28+
}
29+
});
30+
}
31+
await prompt_filename.openAndGetValue(renameFilePromise, () => { new Notice("Rename cancelled") });
32+
33+
} catch (error) {
34+
//new Notice(`Error: Could not remove note from database. path does not exist: ${rowToRemove.__note__.filepath}`);
35+
}
36+
37+
};
38+
tableActionResponse.implementation = implementation;
39+
return this.goNext(tableActionResponse);
40+
41+
}
42+
}

src/stateManagement/data/handlers/UpdateCellHandlerAction.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default class UpdateCellHandlerAction extends AbstractTableAction<DataSta
4242
// Update row file
4343
modifiedRow[
4444
MetadataColumns.FILE
45-
] = `[[${view.file.parent.path}/${value}/${rowTFile.name}|${rowTFile.basename}]]`;
45+
] = `${rowTFile.basename}|${view.file.parent.path}/${value}/${rowTFile.name}`;
4646
// Check if action.value is a valid folder name
4747
const auxPath =
4848
value !== ""
@@ -54,13 +54,7 @@ export default class UpdateCellHandlerAction extends AbstractTableAction<DataSta
5454
recordRow[key] = value as Literal;
5555
});
5656

57-
modifiedRow.__note__ = new NoteInfo({
58-
...recordRow,
59-
file: {
60-
path: auxPath,
61-
},
62-
});
63-
57+
modifiedRow.__note__.filepath = auxPath;
6458
} else {
6559
// Save on disk
6660
await updateRowFileProxy(

src/stateManagement/useDataStore.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ function mockDataState(): DataState {
3333
removeOptionForAllRows: null,
3434
parseDataOfColumn: null,
3535
dataviewRefresh: null,
36+
renameFile: null
3637
},
3738
}
3839
}

0 commit comments

Comments
 (0)