Skip to content

Commit 6e7d597

Browse files
committed
feat: add custom folder settings for issues and pull requests #6
1 parent 61523c5 commit 6e7d597

5 files changed

Lines changed: 394 additions & 102 deletions

File tree

src/file-manager.ts

Lines changed: 104 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ export class FileManager {
9696

9797
const repoCleaned = repoName.replace(/\//g, "-");
9898
const ownerCleaned = owner.replace(/\//g, "-");
99-
const issueFolder = `${repo.issueFolder}/${ownerCleaned}/${repoCleaned}`;
100-
const pullRequestFolder = `${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}`;
99+
const issueFolder = this.getIssueFolderPath(repo, ownerCleaned, repoCleaned);
100+
const pullRequestFolder = this.getPullRequestFolderPath(repo, ownerCleaned, repoCleaned);
101101

102102
await this.cleanupEmptyIssueFolder(
103103
repo,
@@ -117,24 +117,41 @@ export class FileManager {
117117

118118
// ----- Private helper methods -----
119119

120+
/**
121+
* Get the issue folder path for a repository
122+
*/
123+
private getIssueFolderPath(repo: RepositoryTracking, ownerCleaned: string, repoCleaned: string): string {
124+
if (repo.useCustomIssueFolder && repo.customIssueFolder && repo.customIssueFolder.trim()) {
125+
return repo.customIssueFolder.trim();
126+
}
127+
return `${repo.issueFolder}/${ownerCleaned}/${repoCleaned}`;
128+
}
129+
130+
/**
131+
* Get the pull request folder path for a repository
132+
*/
133+
private getPullRequestFolderPath(repo: RepositoryTracking, ownerCleaned: string, repoCleaned: string): string {
134+
if (repo.useCustomPullRequestFolder && repo.customPullRequestFolder && repo.customPullRequestFolder.trim()) {
135+
return repo.customPullRequestFolder.trim();
136+
}
137+
return `${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}`;
138+
}
139+
120140
private async cleanupDeletedIssues(
121141
repo: RepositoryTracking,
122142
ownerCleaned: string,
123143
repoCleaned: string,
124144
allIssuesIncludingRecentlyClosed: any[],
125145
): Promise<void> {
126-
const repoFolder = this.app.vault.getAbstractFileByPath(
127-
`${repo.issueFolder}/${ownerCleaned}/${repoCleaned}`,
128-
);
146+
const issueFolderPath = this.getIssueFolderPath(repo, ownerCleaned, repoCleaned);
147+
const repoFolder = this.app.vault.getAbstractFileByPath(issueFolderPath);
129148

130149
if (repoFolder) {
131150
const files = this.app.vault
132151
.getFiles()
133152
.filter(
134153
(file) =>
135-
file.path.startsWith(
136-
`${repo.issueFolder}/${ownerCleaned}/${repoCleaned}/`,
137-
) && file.extension === "md",
154+
file.path.startsWith(`${issueFolderPath}/`) && file.extension === "md",
138155
);
139156

140157
for (const file of files) {
@@ -184,18 +201,15 @@ export class FileManager {
184201
repoCleaned: string,
185202
allPullRequestsIncludingRecentlyClosed: any[],
186203
): Promise<void> {
187-
const repoFolder = this.app.vault.getAbstractFileByPath(
188-
`${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}`,
189-
);
204+
const pullRequestFolderPath = this.getPullRequestFolderPath(repo, ownerCleaned, repoCleaned);
205+
const repoFolder = this.app.vault.getAbstractFileByPath(pullRequestFolderPath);
190206

191207
if (repoFolder) {
192208
const files = this.app.vault
193209
.getFiles()
194210
.filter(
195211
(file) =>
196-
file.path.startsWith(
197-
`${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}/`,
198-
) && file.extension === "md",
212+
file.path.startsWith(`${pullRequestFolderPath}/`) && file.extension === "md",
199213
);
200214

201215
for (const file of files) {
@@ -244,15 +258,20 @@ export class FileManager {
244258
issue: any,
245259
): Promise<void> {
246260
const fileName = `Issue - ${issue.number}.md`;
247-
await this.ensureFolderExists(repo.issueFolder);
248-
await this.ensureFolderExists(`${repo.issueFolder}/${ownerCleaned}`);
249-
await this.ensureFolderExists(
250-
`${repo.issueFolder}/${ownerCleaned}/${repoCleaned}`,
251-
);
261+
const issueFolderPath = this.getIssueFolderPath(repo, ownerCleaned, repoCleaned);
252262

253-
const file = this.app.vault.getAbstractFileByPath(
254-
`${repo.issueFolder}/${ownerCleaned}/${repoCleaned}/${fileName}`,
255-
);
263+
// Ensure folder structure exists
264+
if (repo.useCustomIssueFolder && repo.customIssueFolder && repo.customIssueFolder.trim()) {
265+
// For custom folders, just ensure the custom path exists
266+
await this.ensureFolderExists(repo.customIssueFolder.trim());
267+
} else {
268+
// For default structure, ensure nested path exists
269+
await this.ensureFolderExists(repo.issueFolder);
270+
await this.ensureFolderExists(`${repo.issueFolder}/${ownerCleaned}`);
271+
await this.ensureFolderExists(`${repo.issueFolder}/${ownerCleaned}/${repoCleaned}`);
272+
}
273+
274+
const file = this.app.vault.getAbstractFileByPath(`${issueFolderPath}/${fileName}`);
256275

257276
const [owner, repoName] = repo.repository.split("/");
258277
const comments = await this.gitHubClient.fetchIssueComments(
@@ -319,10 +338,7 @@ export class FileManager {
319338
}
320339
}
321340
} else {
322-
await this.app.vault.create(
323-
`${repo.issueFolder}/${ownerCleaned}/${repoCleaned}/${fileName}`,
324-
content,
325-
);
341+
await this.app.vault.create(`${issueFolderPath}/${fileName}`, content);
326342
this.noticeManager.debug(`Created issue file for ${issue.number}`);
327343
}
328344
}
@@ -334,18 +350,20 @@ export class FileManager {
334350
pr: any,
335351
): Promise<void> {
336352
const fileName = `Pull Request - ${pr.number}.md`;
353+
const pullRequestFolderPath = this.getPullRequestFolderPath(repo, ownerCleaned, repoCleaned);
337354

338-
await this.ensureFolderExists(repo.pullRequestFolder);
339-
await this.ensureFolderExists(
340-
`${repo.pullRequestFolder}/${ownerCleaned}`,
341-
);
342-
await this.ensureFolderExists(
343-
`${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}`,
344-
);
355+
// Ensure folder structure exists
356+
if (repo.useCustomPullRequestFolder && repo.customPullRequestFolder && repo.customPullRequestFolder.trim()) {
357+
// For custom folders, just ensure the custom path exists
358+
await this.ensureFolderExists(repo.customPullRequestFolder.trim());
359+
} else {
360+
// For default structure, ensure nested path exists
361+
await this.ensureFolderExists(repo.pullRequestFolder);
362+
await this.ensureFolderExists(`${repo.pullRequestFolder}/${ownerCleaned}`);
363+
await this.ensureFolderExists(`${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}`);
364+
}
345365

346-
const file = this.app.vault.getAbstractFileByPath(
347-
`${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}/${fileName}`,
348-
);
366+
const file = this.app.vault.getAbstractFileByPath(`${pullRequestFolderPath}/${fileName}`);
349367

350368
const [owner, repoName] = repo.repository.split("/");
351369
const comments = await this.gitHubClient.fetchPullRequestComments(
@@ -412,10 +430,7 @@ export class FileManager {
412430
}
413431
}
414432
} else {
415-
await this.app.vault.create(
416-
`${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}/${fileName}`,
417-
content,
418-
);
433+
await this.app.vault.create(`${pullRequestFolderPath}/${fileName}`, content);
419434
this.noticeManager.debug(`Created PR file for ${pr.number}`);
420435
}
421436
}
@@ -546,28 +561,31 @@ ${this.formatComments(comments, this.settings.escapeMode)}
546561
}
547562
}
548563

549-
if (files.length === 0) {
550-
this.noticeManager.info(
551-
`Deleting empty folder: ${issueFolder}`,
552-
);
553-
const folder =
554-
this.app.vault.getAbstractFileByPath(issueFolder);
555-
if (folder instanceof TFolder && folder.children.length === 0) {
556-
await this.app.fileManager.trashFile(folder);
557-
}
558-
}
559-
560-
const issueOwnerFolder = this.app.vault.getAbstractFileByPath(
561-
`${repo.issueFolder}/${ownerCleaned}`,
562-
);
563-
564-
if (issueOwnerFolder instanceof TFolder) {
565-
const files = issueOwnerFolder.children;
564+
// Only cleanup nested folder structure if not using custom folder
565+
if (!repo.useCustomIssueFolder || !repo.customIssueFolder || !repo.customIssueFolder.trim()) {
566566
if (files.length === 0) {
567567
this.noticeManager.info(
568-
`Deleting empty folder: ${issueOwnerFolder.path}`,
568+
`Deleting empty folder: ${issueFolder}`,
569569
);
570-
await this.app.fileManager.trashFile(issueOwnerFolder);
570+
const folder =
571+
this.app.vault.getAbstractFileByPath(issueFolder);
572+
if (folder instanceof TFolder && folder.children.length === 0) {
573+
await this.app.fileManager.trashFile(folder);
574+
}
575+
}
576+
577+
const issueOwnerFolder = this.app.vault.getAbstractFileByPath(
578+
`${repo.issueFolder}/${ownerCleaned}`,
579+
);
580+
581+
if (issueOwnerFolder instanceof TFolder) {
582+
const files = issueOwnerFolder.children;
583+
if (files.length === 0) {
584+
this.noticeManager.info(
585+
`Deleting empty folder: ${issueOwnerFolder.path}`,
586+
);
587+
await this.app.fileManager.trashFile(issueOwnerFolder);
588+
}
571589
}
572590
}
573591
}
@@ -606,38 +624,40 @@ ${this.formatComments(comments, this.settings.escapeMode)}
606624
}
607625
}
608626

609-
if (files.length === 0) {
610-
this.noticeManager.info(
611-
`Deleting empty folder: ${pullRequestFolder}`,
612-
);
613-
const folder =
614-
this.app.vault.getAbstractFileByPath(pullRequestFolder);
615-
if (folder instanceof TFolder && folder.children.length === 0) {
616-
await this.app.fileManager.trashFile(folder);
617-
}
618-
}
619-
620-
const pullRequestOwnerFolder = this.app.vault.getAbstractFileByPath(
621-
`${repo.pullRequestFolder}/${ownerCleaned}`,
622-
);
623-
624-
if (pullRequestOwnerFolder instanceof TFolder) {
625-
const files = pullRequestOwnerFolder.children;
627+
// Only cleanup nested folder structure if not using custom folder
628+
if (!repo.useCustomPullRequestFolder || !repo.customPullRequestFolder || !repo.customPullRequestFolder.trim()) {
626629
if (files.length === 0) {
627630
this.noticeManager.info(
628-
`Deleting empty folder: ${pullRequestOwnerFolder.path}`,
629-
);
630-
await this.app.fileManager.trashFile(
631-
pullRequestOwnerFolder,
631+
`Deleting empty folder: ${pullRequestFolder}`,
632632
);
633+
const folder =
634+
this.app.vault.getAbstractFileByPath(pullRequestFolder);
635+
if (folder instanceof TFolder && folder.children.length === 0) {
636+
await this.app.fileManager.trashFile(folder);
637+
}
638+
}
639+
640+
const pullRequestOwnerFolder = this.app.vault.getAbstractFileByPath(
641+
`${repo.pullRequestFolder}/${ownerCleaned}`,
642+
);
643+
644+
if (pullRequestOwnerFolder instanceof TFolder) {
645+
const files = pullRequestOwnerFolder.children;
646+
if (files.length === 0) {
647+
this.noticeManager.info(
648+
`Deleting empty folder: ${pullRequestOwnerFolder.path}`,
649+
);
650+
await this.app.fileManager.trashFile(
651+
pullRequestOwnerFolder,
652+
);
653+
}
633654
}
634655
}
635656
}
636657
}
637658

638-
/**
639-
* Format comments section for issues and pull requests
640-
*/
659+
// Format comments section for issues and pull requests
660+
641661
private formatComments(
642662
comments: any[],
643663
escapeMode: "disabled" | "normal" | "strict" | "veryStrict",
@@ -647,9 +667,7 @@ ${this.formatComments(comments, this.settings.escapeMode)}
647667
}
648668

649669
comments.sort(
650-
(a, b) =>
651-
new Date(a.created_at).getTime() -
652-
new Date(b.created_at).getTime(),
670+
(a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime(),
653671
);
654672

655673
let commentSection = "\n## Comments\n\n";

src/main.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ export default class GitHubTrackerPlugin extends Plugin {
130130
async loadSettings() {
131131
const loadedData = await this.loadData();
132132
this.settings = Object.assign({}, DEFAULT_SETTINGS, loadedData);
133+
134+
// Migrate existing repositories to include new custom folder properties
135+
this.settings.repositories = this.settings.repositories.map(repo => {
136+
return Object.assign({}, DEFAULT_REPOSITORY_TRACKING, repo);
137+
});
133138
}
134139

135140
async saveSettings() {

0 commit comments

Comments
 (0)