Skip to content

Commit c3f8bfe

Browse files
committed
WIP: settings profiles with compact UI layout #40
1 parent 81bc3ee commit c3f8bfe

13 files changed

Lines changed: 683 additions & 400 deletions

src/issue-file-manager.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,10 @@ export class IssueFileManager {
9090
await this.fileHelpers.ensureFolderExists(repo.customIssueFolder.trim());
9191
} else {
9292
// For default structure, ensure nested path exists
93-
await this.fileHelpers.ensureFolderExists(repo.issueFolder);
94-
await this.fileHelpers.ensureFolderExists(`${repo.issueFolder}/${ownerCleaned}`);
95-
await this.fileHelpers.ensureFolderExists(`${repo.issueFolder}/${ownerCleaned}/${repoCleaned}`);
93+
const issueFolder = repo.issueFolder ?? "GitHub";
94+
await this.fileHelpers.ensureFolderExists(issueFolder);
95+
await this.fileHelpers.ensureFolderExists(`${issueFolder}/${ownerCleaned}`);
96+
await this.fileHelpers.ensureFolderExists(`${issueFolder}/${ownerCleaned}/${repoCleaned}`);
9697
}
9798

9899
const file = this.app.vault.getAbstractFileByPath(`${issueFolderPath}/${fileName}`);

src/main.ts

Lines changed: 138 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { FileManager } from "./file-manager";
1212
import { GitHubTrackerSettingTab } from "./settings-tab";
1313
import { NoticeManager } from "./notice-manager";
1414
import { GitHubKanbanView, KANBAN_VIEW_TYPE } from "./kanban-view";
15+
import { getEffectiveRepoSettings, stripProfileFieldsFromRepo } from "./util/settingsUtils";
1516

1617
export default class GitHubTrackerPlugin extends Plugin {
1718
settings: GitHubTrackerSettings = DEFAULT_SETTINGS;
@@ -215,6 +216,7 @@ export default class GitHubTrackerPlugin extends Plugin {
215216

216217
this.isSyncing = true;
217218
try {
219+
const effectiveRepo = getEffectiveRepoSettings(repo, this.settings);
218220
this.noticeManager.info(`Syncing repository: ${repositoryName}`);
219221
const [owner, repoName] = repo.repository.split("/");
220222
if (!owner || !repoName) {
@@ -225,9 +227,9 @@ export default class GitHubTrackerPlugin extends Plugin {
225227
}
226228

227229
// Sync Issues
228-
if (repo.trackIssues) {
230+
if (effectiveRepo.trackIssues) {
229231
this.noticeManager.debug(
230-
`Fetching issues for ${repo.repository}`,
232+
`Fetching issues for ${effectiveRepo.repository}`,
231233
);
232234
const allIssuesIncludingRecentlyClosed =
233235
await this.gitHubClient.fetchRepositoryIssues(
@@ -242,35 +244,35 @@ export default class GitHubTrackerPlugin extends Plugin {
242244
);
243245

244246
// Decide which issues to filter based on settings
245-
const issuesToFilter = repo.includeClosedIssues
247+
const issuesToFilter = effectiveRepo.includeClosedIssues
246248
? allIssuesIncludingRecentlyClosed
247249
: openIssues;
248250

249251
const filteredIssues = this.fileManager.filterIssues(
250-
repo,
252+
effectiveRepo,
251253
issuesToFilter,
252254
);
253255

254256
this.noticeManager.debug(
255-
`Processing ${filteredIssues.length} issues (from ${openIssues.length} open issues) for ${repo.repository}`,
257+
`Processing ${filteredIssues.length} issues (from ${openIssues.length} open issues) for ${effectiveRepo.repository}`,
256258
);
257259

258260
const currentIssueNumbers = new Set(
259261
filteredIssues.map((issue: any) => issue.number.toString()),
260262
);
261263

262264
await this.fileManager.createIssueFiles(
263-
repo,
265+
effectiveRepo,
264266
filteredIssues,
265267
allIssuesIncludingRecentlyClosed,
266268
currentIssueNumbers,
267269
);
268270
}
269271

270272
// Sync Pull Requests
271-
if (repo.trackPullRequest) {
273+
if (effectiveRepo.trackPullRequest) {
272274
this.noticeManager.debug(
273-
`Fetching pull requests for ${repo.repository}`,
275+
`Fetching pull requests for ${effectiveRepo.repository}`,
274276
);
275277

276278
const allPullRequestsIncludingRecentlyClosed =
@@ -287,25 +289,25 @@ export default class GitHubTrackerPlugin extends Plugin {
287289
);
288290

289291
// Decide which pull requests to filter based on settings
290-
const pullRequestsToFilter = repo.includeClosedPullRequests
292+
const pullRequestsToFilter = effectiveRepo.includeClosedPullRequests
291293
? allPullRequestsIncludingRecentlyClosed
292294
: openPullRequests;
293295

294296
const filteredPRs = this.fileManager.filterPullRequests(
295-
repo,
297+
effectiveRepo,
296298
pullRequestsToFilter,
297299
);
298300

299301
this.noticeManager.debug(
300-
`Processing ${filteredPRs.length} pull requests (from ${openPullRequests.length} open PRs) for ${repo.repository}`,
302+
`Processing ${filteredPRs.length} pull requests (from ${openPullRequests.length} open PRs) for ${effectiveRepo.repository}`,
301303
);
302304

303305
const currentPRNumbers = new Set(
304306
filteredPRs.map((pr: any) => pr.number.toString()),
305307
);
306308

307309
await this.fileManager.createPullRequestFiles(
308-
repo,
310+
effectiveRepo,
309311
filteredPRs,
310312
allPullRequestsIncludingRecentlyClosed,
311313
currentPRNumbers,
@@ -583,6 +585,48 @@ export default class GitHubTrackerPlugin extends Plugin {
583585
await this.saveData(this.settings);
584586
}
585587

588+
// Cleanup: Remove empty migrated profiles and reassign repos to default
589+
let needsCleanup = false;
590+
for (const repo of this.settings.repositories) {
591+
if (repo.profileId && repo.profileId.startsWith("migrated-")) {
592+
const profile = this.settings.profiles.find(p => p.id === repo.profileId);
593+
if (profile) {
594+
const hasCustomValues = (
595+
profile.issueUpdateMode !== undefined ||
596+
profile.allowDeleteIssue !== undefined ||
597+
profile.issueFolder !== undefined ||
598+
profile.issueNoteTemplate !== undefined ||
599+
profile.issueContentTemplate !== undefined ||
600+
profile.useCustomIssueContentTemplate !== undefined ||
601+
profile.includeIssueComments !== undefined ||
602+
profile.pullRequestUpdateMode !== undefined ||
603+
profile.allowDeletePullRequest !== undefined ||
604+
profile.pullRequestFolder !== undefined ||
605+
profile.pullRequestNoteTemplate !== undefined ||
606+
profile.pullRequestContentTemplate !== undefined ||
607+
profile.useCustomPullRequestContentTemplate !== undefined ||
608+
profile.includePullRequestComments !== undefined ||
609+
profile.includeClosedIssues !== undefined ||
610+
profile.includeClosedPullRequests !== undefined ||
611+
profile.trackIssues !== undefined ||
612+
profile.trackPullRequest !== undefined
613+
);
614+
if (!hasCustomValues) {
615+
repo.profileId = "default";
616+
needsCleanup = true;
617+
}
618+
}
619+
}
620+
}
621+
if (needsCleanup) {
622+
// Remove orphaned profiles (no repo references them)
623+
const usedProfileIds = new Set(this.settings.repositories.map(r => r.profileId));
624+
this.settings.profiles = this.settings.profiles.filter(
625+
p => p.id === "default" || p.id === "default-project" || usedProfileIds.has(p.id)
626+
);
627+
await this.saveData(this.settings);
628+
}
629+
586630
// Migrate repos that still have old per-repo settings into their own profiles
587631
const defaultProfile = this.settings.profiles.find(p => p.id === "default") ?? DEFAULT_REPOSITORY_PROFILE;
588632
let needsSave = false;
@@ -593,6 +637,24 @@ export default class GitHubTrackerPlugin extends Plugin {
593637
return repo;
594638
}
595639

640+
// Check if repo actually has any profile-managed fields to migrate
641+
// (after stripProfileFieldsFromRepo they won't exist anymore)
642+
const hasProfileFields = (
643+
(repo as any).issueUpdateMode !== undefined ||
644+
(repo as any).issueFolder !== undefined ||
645+
(repo as any).issueNoteTemplate !== undefined ||
646+
(repo as any).pullRequestUpdateMode !== undefined ||
647+
(repo as any).pullRequestFolder !== undefined ||
648+
(repo as any).pullRequestNoteTemplate !== undefined
649+
);
650+
651+
if (!hasProfileFields) {
652+
// No profile-managed fields on repo — nothing to migrate
653+
if (!repo.profileId) repo.profileId = "default";
654+
delete (repo as any).ignoreGlobalSettings;
655+
return repo;
656+
}
657+
596658
const merged = Object.assign({}, DEFAULT_REPOSITORY_TRACKING, repo);
597659

598660
// Check if repo has values that differ from the default profile
@@ -657,13 +719,44 @@ export default class GitHubTrackerPlugin extends Plugin {
657719
// Defaults first, then override with saved values
658720
this.settings.repositories = this.settings.repositories.map(repo => {
659721
const merged = Object.assign({}, DEFAULT_REPOSITORY_TRACKING, repo);
660-
// Ensure critical fields are never undefined
661-
if (!merged.issueFolder) merged.issueFolder = DEFAULT_REPOSITORY_TRACKING.issueFolder;
662-
if (!merged.pullRequestFolder) merged.pullRequestFolder = DEFAULT_REPOSITORY_TRACKING.pullRequestFolder;
663722
if (!merged.profileId) merged.profileId = "default";
664723
return merged;
665724
});
666725

726+
// Migrate trackIssues/trackPullRequest from repos into their profiles
727+
let needsTrackMigration = false;
728+
for (const repo of this.settings.repositories) {
729+
// Check if repo still has trackIssues/trackPullRequest explicitly set
730+
// (they are now optional and profile-managed)
731+
if ((repo as any).trackIssues !== undefined || (repo as any).trackPullRequest !== undefined) {
732+
const profileId = repo.profileId || "default";
733+
// Only migrate into non-default profiles (default profile would affect all repos)
734+
if (profileId !== "default") {
735+
const profile = this.settings.profiles.find(p => p.id === profileId);
736+
if (profile && profile.type === "repository") {
737+
if (profile.trackIssues === undefined && (repo as any).trackIssues !== undefined) {
738+
profile.trackIssues = (repo as any).trackIssues;
739+
}
740+
if (profile.trackPullRequest === undefined && (repo as any).trackPullRequest !== undefined) {
741+
profile.trackPullRequest = (repo as any).trackPullRequest;
742+
}
743+
}
744+
}
745+
// Remove migrated fields from repo so migration doesn't re-trigger
746+
delete (repo as any).trackIssues;
747+
delete (repo as any).trackPullRequest;
748+
needsTrackMigration = true;
749+
}
750+
}
751+
if (needsTrackMigration) {
752+
await this.saveData(this.settings);
753+
}
754+
755+
// Hydrate profile-managed fields onto each repo from its profile
756+
this.settings.repositories = this.settings.repositories.map(repo => {
757+
return getEffectiveRepoSettings(repo, this.settings);
758+
});
759+
667760
}
668761

669762
async saveSettings() {
@@ -690,7 +783,22 @@ export default class GitHubTrackerPlugin extends Plugin {
690783
};
691784
}
692785

693-
await this.saveData(this.settings);
786+
// Strip profile-managed fields from repos before persisting
787+
const dataToSave = {
788+
...this.settings,
789+
repositories: this.settings.repositories.map(repo =>
790+
stripProfileFieldsFromRepo(repo)
791+
),
792+
};
793+
await this.saveData(dataToSave);
794+
795+
// Re-hydrate in-memory repos so profile changes take effect immediately
796+
// Mutate existing objects in place to preserve closure references in the UI
797+
for (const repo of this.settings.repositories) {
798+
const effective = getEffectiveRepoSettings(repo, this.settings);
799+
Object.assign(repo, effective);
800+
}
801+
694802
const token = this.getGitHubToken();
695803
if (token) {
696804
this.gitHubClient?.initializeClient();
@@ -749,14 +857,14 @@ export default class GitHubTrackerPlugin extends Plugin {
749857

750858
try {
751859
for (const repo of this.settings.repositories) {
752-
if (!repo.trackIssues) continue;
753-
754860
const [owner, repoName] = repo.repository.split("/");
755861
if (!owner || !repoName) continue;
756862

757863
try {
864+
const effectiveRepo = getEffectiveRepoSettings(repo, this.settings);
865+
if (!effectiveRepo.trackIssues) continue;
758866
this.noticeManager.debug(
759-
`Fetching issues for ${repo.repository}`,
867+
`Fetching issues for ${effectiveRepo.repository}`,
760868
);
761869
const allIssuesIncludingRecentlyClosed =
762870
await this.gitHubClient.fetchRepositoryIssues(
@@ -771,12 +879,12 @@ export default class GitHubTrackerPlugin extends Plugin {
771879
);
772880

773881
// Decide which issues to filter based on settings
774-
const issuesToFilter = repo.includeClosedIssues
882+
const issuesToFilter = effectiveRepo.includeClosedIssues
775883
? allIssuesIncludingRecentlyClosed
776884
: openIssues;
777885

778886
const filteredIssues = this.fileManager.filterIssues(
779-
repo,
887+
effectiveRepo,
780888
issuesToFilter,
781889
);
782890

@@ -790,14 +898,14 @@ export default class GitHubTrackerPlugin extends Plugin {
790898
);
791899

792900
await this.fileManager.createIssueFiles(
793-
repo,
901+
effectiveRepo,
794902
filteredIssues,
795903
allIssuesIncludingRecentlyClosed,
796904
currentIssueNumbers,
797905
);
798906

799907
this.noticeManager.debug(
800-
`Processed ${filteredIssues.length} open issues for ${repo.repository}`,
908+
`Processed ${filteredIssues.length} open issues for ${effectiveRepo.repository}`,
801909
);
802910
} catch (repoError: unknown) {
803911
this.noticeManager.error(
@@ -825,14 +933,14 @@ export default class GitHubTrackerPlugin extends Plugin {
825933

826934
try {
827935
for (const repo of this.settings.repositories) {
828-
if (!repo.trackPullRequest) continue;
829-
830936
const [owner, repoName] = repo.repository.split("/");
831937
if (!owner || !repoName) continue;
832938

833939
try {
940+
const effectiveRepo = getEffectiveRepoSettings(repo, this.settings);
941+
if (!effectiveRepo.trackPullRequest) continue;
834942
this.noticeManager.debug(
835-
`Fetching pull requests for ${repo.repository}`,
943+
`Fetching pull requests for ${effectiveRepo.repository}`,
836944
);
837945

838946
const allPullRequestsIncludingRecentlyClosed =
@@ -849,12 +957,12 @@ export default class GitHubTrackerPlugin extends Plugin {
849957
);
850958

851959
// Decide which pull requests to filter based on settings
852-
const pullRequestsToFilter = repo.includeClosedPullRequests
960+
const pullRequestsToFilter = effectiveRepo.includeClosedPullRequests
853961
? allPullRequestsIncludingRecentlyClosed
854962
: openPullRequests;
855963

856964
const filteredPRs = this.fileManager.filterPullRequests(
857-
repo,
965+
effectiveRepo,
858966
pullRequestsToFilter,
859967
);
860968

@@ -869,14 +977,14 @@ export default class GitHubTrackerPlugin extends Plugin {
869977
);
870978

871979
await this.fileManager.createPullRequestFiles(
872-
repo,
980+
effectiveRepo,
873981
filteredPRs,
874982
allPullRequestsIncludingRecentlyClosed,
875983
currentPRNumbers,
876984
);
877985

878986
this.noticeManager.debug(
879-
`Processed ${filteredPRs.length} open pull requests for ${repo.repository}`,
987+
`Processed ${filteredPRs.length} open pull requests for ${effectiveRepo.repository}`,
880988
);
881989
} catch (repoError: unknown) {
882990
this.noticeManager.error(

src/pr-file-manager.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,10 @@ export class PullRequestFileManager {
9191
await this.fileHelpers.ensureFolderExists(repo.customPullRequestFolder.trim());
9292
} else {
9393
// For default structure, ensure nested path exists
94-
await this.fileHelpers.ensureFolderExists(repo.pullRequestFolder);
95-
await this.fileHelpers.ensureFolderExists(`${repo.pullRequestFolder}/${ownerCleaned}`);
96-
await this.fileHelpers.ensureFolderExists(`${repo.pullRequestFolder}/${ownerCleaned}/${repoCleaned}`);
94+
const pullRequestFolder = repo.pullRequestFolder ?? "GitHub Pull Requests";
95+
await this.fileHelpers.ensureFolderExists(pullRequestFolder);
96+
await this.fileHelpers.ensureFolderExists(`${pullRequestFolder}/${ownerCleaned}`);
97+
await this.fileHelpers.ensureFolderExists(`${pullRequestFolder}/${ownerCleaned}/${repoCleaned}`);
9798
}
9899

99100
const file = this.app.vault.getAbstractFileByPath(`${pullRequestFolderPath}/${fileName}`);

0 commit comments

Comments
 (0)