Skip to content

Commit d442804

Browse files
committed
fix: stabilize repository sidebar ordering
1 parent 46c61ee commit d442804

4 files changed

Lines changed: 44 additions & 7 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to Forge are documented in this file.
44

55
## [Unreleased]
66

7+
- Kept the all repositories list alphabetically ordered when selecting a repository.
8+
79
## [0.0.31]
810

911

‎src/App.tsx‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import {
6262
findNewOpenPullRequests,
6363
formatDuration,
6464
githubUrlClickActionForDetail,
65+
groupRepositoriesByOwner,
6566
isLiveStatus,
6667
latestViewerPullRequestReviewEvent,
6768
mergeFavoriteRepoSnapshots,
@@ -1210,13 +1211,7 @@ export function App() {
12101211
}, [allRepos, sidebarSearch]);
12111212

12121213
const repoGroups = useMemo(() => {
1213-
const groups = new Map<string, RepoSummary[]>();
1214-
for (const repo of visibleRepos) {
1215-
const list = groups.get(repo.owner) ?? [];
1216-
list.push(repo);
1217-
groups.set(repo.owner, list);
1218-
}
1219-
return Array.from(groups.entries()).sort(([a], [b]) => a.localeCompare(b));
1214+
return groupRepositoriesByOwner(visibleRepos);
12201215
}, [visibleRepos]);
12211216

12221217
const middleItems = useMemo<MiddleItem[]>(() => {

‎src/appLogic.test.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
findNewOpenPullRequests,
2020
formatDuration,
2121
githubUrlClickActionForDetail,
22+
groupRepositoriesByOwner,
2223
isFailedWorkflowRun,
2324
isPullRequestAuthor,
2425
isLiveStatus,
@@ -35,6 +36,24 @@ import {
3536
} from "./appLogic";
3637

3738
describe("appLogic", () => {
39+
it("groups repositories alphabetically by owner and repository name", () => {
40+
const repos = [
41+
{ owner: "zeta", name: "selected", fullName: "zeta/selected", updatedAt: "2026-01-01T00:00:00Z" },
42+
{ owner: "alpha", name: "zebra", fullName: "alpha/zebra", updatedAt: "2026-01-03T00:00:00Z" },
43+
{ owner: "alpha", name: "aardvark", fullName: "alpha/aardvark", updatedAt: "2026-01-02T00:00:00Z" },
44+
{ owner: "beta", name: "repo-10", fullName: "beta/repo-10", updatedAt: "2026-01-04T00:00:00Z" },
45+
{ owner: "beta", name: "repo-2", fullName: "beta/repo-2", updatedAt: "2026-01-05T00:00:00Z" }
46+
] as RepoSummary[];
47+
48+
expect(
49+
groupRepositoriesByOwner(repos).map(([owner, ownerRepos]) => [owner, ownerRepos.map((repo) => repo.name)])
50+
).toEqual([
51+
["alpha", ["aardvark", "zebra"]],
52+
["beta", ["repo-2", "repo-10"]],
53+
["zeta", ["selected"]]
54+
]);
55+
});
56+
3857
it("formats durations for workflow runs", () => {
3958
expect(formatDuration(null)).toBe("");
4059
expect(formatDuration(9_800)).toBe("9s");

‎src/appLogic.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,27 @@ function repoSnapshotKey(repo: Pick<RepoSummary, "owner" | "name">): string {
1616
return `${repo.owner}/${repo.name}`;
1717
}
1818

19+
const repositoryNameCollator = new Intl.Collator(undefined, {
20+
numeric: true,
21+
sensitivity: "base"
22+
});
23+
24+
export function groupRepositoriesByOwner(repos: RepoSummary[]): Array<[string, RepoSummary[]]> {
25+
const groups = new Map<string, RepoSummary[]>();
26+
for (const repo of repos) {
27+
const list = groups.get(repo.owner) ?? [];
28+
list.push(repo);
29+
groups.set(repo.owner, list);
30+
}
31+
32+
return Array.from(groups.entries())
33+
.map(([owner, ownerRepos]) => [
34+
owner,
35+
[...ownerRepos].sort((left, right) => repositoryNameCollator.compare(left.name, right.name))
36+
] as [string, RepoSummary[]])
37+
.sort(([leftOwner], [rightOwner]) => repositoryNameCollator.compare(leftOwner, rightOwner));
38+
}
39+
1940
export function mergeFavoriteRepoSnapshots(
2041
current: FavoriteRepoSnapshots,
2142
favoriteKeys: string[],

0 commit comments

Comments
 (0)