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

Commit b58bcd6

Browse files
committed
Merge branch '574-support-of-obsidian-projects'
2 parents 8c81b59 + b7473e1 commit b58bcd6

File tree

13 files changed

+168
-55
lines changed

13 files changed

+168
-55
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"luxon": "3.1.0",
6060
"monkey-around": "2.3.0",
6161
"obsidian-dataview": "0.5.51",
62+
"obsidian-projects-types": "0.6.0",
6263
"react": "18.2.0",
6364
"react-csv": "2.2.2",
6465
"react-datepicker": "4.8.0",

src/DatabaseView.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,22 @@ export class DatabaseView extends TextFileView implements HoverParent {
5353
initial: InitialType;
5454
formulas: Record<string, unknown>;
5555

56-
constructor(leaf: WorkspaceLeaf, plugin: DBFolderPlugin) {
56+
constructor(leaf: WorkspaceLeaf, plugin: DBFolderPlugin, file?: TFile) {
5757
super(leaf);
5858
this.plugin = plugin;
5959
this.emitter = createEmitter();
60-
this.register(
61-
this.containerEl.onWindowMigrated(() => {
62-
this.plugin.removeView(this);
63-
this.plugin.addView(this, this.data);
64-
})
65-
);
60+
if (file) {
61+
this.file = file;
62+
this.plugin.removeView(this);
63+
this.plugin.addView(this, this.data);
64+
} else {
65+
this.register(
66+
this.containerEl.onWindowMigrated(() => {
67+
this.plugin.removeView(this);
68+
this.plugin.addView(this, this.data);
69+
})
70+
);
71+
}
6672
}
6773

6874
/**
@@ -218,13 +224,17 @@ export class DatabaseView extends TextFileView implements HoverParent {
218224
return await super.onUnloadFile(file);
219225
}
220226

227+
initRootContainer(file: TFile) {
228+
this.tableContainer = this.contentEl.createDiv(
229+
StyleClasses.TABLE_CONTAINER
230+
);
231+
this.tableContainer.setAttribute("id", file.path);
232+
this.rootContainer = createRoot(this.tableContainer);
233+
}
234+
221235
async onLoadFile(file: TFile) {
222236
try {
223-
this.tableContainer = this.contentEl.createDiv(
224-
StyleClasses.TABLE_CONTAINER
225-
);
226-
this.tableContainer.setAttribute("id", file.path);
227-
this.rootContainer = createRoot(this.tableContainer);
237+
this.initRootContainer(file);
228238
return await super.onLoadFile(file);
229239
} catch (e) {
230240
const stateManager = this.plugin.stateManagers.get(this.file);

src/api/obsidian-projects-api.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { DB_ICONS } from "helpers/Constants";
2+
import DBFolderPlugin from "main";
3+
4+
import {
5+
DatabaseView,
6+
} from 'DatabaseView';
7+
import { LOGGER } from "services/Logger";
8+
import { DataQueryResult, ProjectView, ProjectViewProps } from "obsidian-projects-types";
9+
import { resolve_tfile, resolve_tfolder } from "helpers/FileManagement";
10+
import { generateDbConfiguration, generateNewDatabase } from "helpers/CommandsHelper";
11+
12+
class ProjectAPI extends ProjectView {
13+
private plugin: DBFolderPlugin;
14+
private view: DatabaseView;
15+
16+
constructor(plugin: DBFolderPlugin) {
17+
super();
18+
this.plugin = plugin;
19+
}
20+
dataEl?: HTMLElement;
21+
22+
getViewType(): string {
23+
return "dbfolder-view";
24+
}
25+
26+
getDisplayName(): string {
27+
return "Database Folder";
28+
}
29+
30+
getIcon(): string {
31+
return DB_ICONS.NAME;
32+
}
33+
34+
async onData({ data }: DataQueryResult) {
35+
// Do nothing here
36+
// Data is handled by the database itself and will be updated automatically every time the view is opened
37+
38+
39+
}
40+
41+
// onOpens is called whenever the user activates your view.
42+
//
43+
// `contentEl` HTML element where you can attach your view.
44+
// `config` JSON object with optional view configuration.
45+
// `saveConfig` Callback to save configuration changes.
46+
async onOpen({ contentEl, config, saveConfig, project, viewId }: ProjectViewProps) {
47+
const { path } = project;
48+
let filePath = config.filepath;
49+
if (!filePath) {
50+
const folder = resolve_tfolder(path);
51+
// If the config is empty, we need to create a Default
52+
const dbConfig = generateDbConfiguration(this.plugin.settings.local_settings);
53+
await generateNewDatabase(dbConfig, folder, `${viewId}_db`, false);
54+
saveConfig({ filepath: `${path}/${viewId}_db.md` });
55+
}
56+
const leaf = app.workspace.getLeaf();
57+
const file = resolve_tfile(filePath);
58+
this.view = new DatabaseView(leaf, this.plugin, file);
59+
this.view.initRootContainer(file);
60+
await this.view.initDatabase();
61+
LOGGER.debug("Database initialized successfully from project view");
62+
this.dataEl = contentEl.createDiv().appendChild(this.view.containerEl);
63+
}
64+
65+
async onClose() {
66+
LOGGER.debug("Closing project view ", this.getDisplayName());
67+
}
68+
}
69+
70+
export default ProjectAPI;

src/cdm/FolderModel.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ export type TableDataType = {
100100
stateManager: StateManager,
101101
tableStore?: TableStateInterface
102102
}
103-
104103
export interface DatabaseHeaderProps {
105104
column: Column<RowDataType, Literal>,
106105
header: Header<RowDataType, Literal>,

src/cdm/ObsidianProjectsModel.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
interface ProjectView {
2+
path: string;
3+
}

src/components/NavBar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export function NavBar(navBarProps: NavBarProps) {
5959
InputType.MARKDOWN;
6060
view.plugin.setMarkdownView(view.leaf);
6161
};
62+
6263
const renderMenu = (
6364
<Menu
6465
anchorEl={anchorEl}

src/components/Table.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export function Table(tableData: TableDataType) {
120120
if (targetEl.tagName !== "A" || !view) return;
121121

122122
if (targetEl.hasClass("internal-link")) {
123-
view.app.workspace.trigger("hover-link", {
123+
app.workspace.trigger("hover-link", {
124124
event: e.nativeEvent,
125125
source: DatabaseCore.FRONTMATTER_KEY,
126126
hoverParent: view,
@@ -151,7 +151,7 @@ export function Table(tableData: TableDataType) {
151151
const normalizedPath = getNormalizedPath(href);
152152
const target =
153153
typeof href === "string" &&
154-
view.app.metadataCache.getFirstLinkpathDest(
154+
app.metadataCache.getFirstLinkpathDest(
155155
normalizedPath.root,
156156
view.file.path
157157
);

src/components/cellTypes/SelectCell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ const SelectCell = (popperProps: CellComponentProps) => {
8181
newValue: OnChangeValue<SelectValue, false>,
8282
actionMeta: ActionMeta<RowSelectOption>
8383
) => {
84-
const selectValue = newValue ? newValue.value : "";
84+
const selectValue = newValue ? newValue.value.toString() : "";
8585
const newCell = ParseService.parseRowToLiteral(
8686
selectRow,
8787
tableColumn,

src/helpers/CommandsHelper.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Notice, TFile, TFolder } from "obsidian";
33
import { LOGGER } from "services/Logger";
44
import { DatabaseCore, DatabaseFrontmatterOptions, DATABASE_CONFIG, DEFAULT_SETTINGS, YAML_INDENT } from "helpers/Constants";
55

6-
export async function generateNewDatabase(ddbbConfig: string, folder?: TFolder, ddbbName?: string) {
6+
export async function generateNewDatabase(ddbbConfig: string, folder?: TFolder, ddbbName?: string, autoOpen = true) {
77
const targetFolder = folder
88
?? app.fileManager.getNewFileParent(
99
app.workspace.getActiveFile()?.path || ''
@@ -22,10 +22,13 @@ export async function generateNewDatabase(ddbbConfig: string, folder?: TFolder,
2222
.concat('\n')
2323
.concat(DATABASE_CONFIG.END_CENTINEL)
2424
);
25-
await app.workspace.getMostRecentLeaf().setViewState({
26-
type: DatabaseCore.FRONTMATTER_KEY,
27-
state: { file: database.path },
28-
});
25+
// Open the new database file
26+
if (autoOpen) {
27+
await app.workspace.getMostRecentLeaf().setViewState({
28+
type: DatabaseCore.FRONTMATTER_KEY,
29+
state: { file: database.path },
30+
});
31+
}
2932
} catch (e) {
3033
LOGGER.error('Error creating database folder:', e);
3134
new Notice(`Error creating database folder: ${e}`, 5000);

src/helpers/FileManagement.ts

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@ import HelperException from "errors/HelperException";
44
import { normalizePath, TAbstractFile, TFile, TFolder, Vault } from "obsidian";
55
import { INLINE_POSITION, SourceDataTypes } from "helpers/Constants";
66
import { RowDataType } from "cdm/FolderModel";
7+
import { VaultManagerDB } from "services/FileManagerService";
78

8-
export function resolve_tfile(file_str: string): TFile {
9+
export function resolve_tfile(file_str: string, restrict: boolean = true): TFile {
910
file_str = normalizePath(file_str);
1011

1112
const file = app.vault.getAbstractFileByPath(file_str);
12-
if (!file) {
13+
if (!file && restrict) {
1314
throw new HelperException(`File "${file_str}" doesn't exist`);
1415
}
16+
1517
if (!(file instanceof TFile)) {
16-
throw new HelperException(`${file_str} is a folder, not a file`);
18+
if (restrict) {
19+
throw new HelperException(`${file_str} is a folder, not a file`);
20+
} else {
21+
return null;
22+
}
1723
}
1824

1925
return file;
@@ -152,3 +158,37 @@ export const resolveNewFilePath = ({
152158
.join("/");
153159
return `${folderPath}${subfolders ? `/${subfolders}` : ""}`;
154160
};
161+
162+
/**
163+
* Generate a new file with the structure of a database view
164+
* @param folderPath
165+
* @param filename
166+
* @param ddbbConfig
167+
* @returns
168+
*/
169+
export async function create_row_file(
170+
folderPath: string,
171+
filename: string,
172+
ddbbConfig: LocalSettings
173+
): Promise<string> {
174+
let trimedFilename = filename.replace(/\.[^/.]+$/, "").trim();
175+
let filepath = `${folderPath}/${trimedFilename}.md`;
176+
// Validate possible duplicates
177+
let sufixOfDuplicate = 0;
178+
while (resolve_tfile(filepath, false)) {
179+
sufixOfDuplicate++;
180+
filepath = `${folderPath}/${trimedFilename}-${sufixOfDuplicate}.md`;
181+
}
182+
183+
if (sufixOfDuplicate > 0) {
184+
trimedFilename = `${trimedFilename}-${sufixOfDuplicate}`;
185+
filename = `${trimedFilename} copy(${sufixOfDuplicate})`;
186+
}
187+
// Add note to persist row
188+
await VaultManagerDB.create_markdown_file(
189+
resolve_tfolder(folderPath),
190+
trimedFilename,
191+
ddbbConfig
192+
);
193+
return filepath;
194+
}

0 commit comments

Comments
 (0)