From 84a956481b86c61f835bfdec56757d71109ab5e1 Mon Sep 17 00:00:00 2001 From: Owais Jamil Date: Sun, 22 Mar 2026 18:26:12 -0500 Subject: [PATCH] feat: update repo detail page with issue and PR selection * add issue and pull request comment * URI parsing utilities * add unit tests for normalizers and URI helpers --- docs/tasks/phase-2.md | 8 +- src/app/router/index.ts | 24 ++ src/components/repo/CommentThread.vue | 98 +++++++ src/domain/models/comment.ts | 15 + src/domain/models/issue.ts | 4 + src/domain/models/pull-request.ts | 11 + src/features/repo/IssueDetailPage.vue | 265 +++++++++++++++++ src/features/repo/PullRequestDetailPage.vue | 303 ++++++++++++++++++++ src/features/repo/RepoDetailPage.vue | 57 +++- src/features/repo/RepoIssues.vue | 9 +- src/features/repo/RepoPRs.vue | 9 +- src/mocks/issues.ts | 152 ++++++---- src/mocks/pull-requests.ts | 143 +++++---- src/services/tangled/endpoints.ts | 38 +++ src/services/tangled/normalizers.ts | 144 +++++++++- src/services/tangled/queries.ts | 125 ++++++++ src/services/tangled/uris.ts | 17 ++ tests/unit/tangled-normalizers.spec.ts | 67 +++++ 18 files changed, 1352 insertions(+), 137 deletions(-) create mode 100644 src/components/repo/CommentThread.vue create mode 100644 src/domain/models/comment.ts create mode 100644 src/features/repo/IssueDetailPage.vue create mode 100644 src/features/repo/PullRequestDetailPage.vue create mode 100644 src/services/tangled/uris.ts create mode 100644 tests/unit/tangled-normalizers.spec.ts diff --git a/docs/tasks/phase-2.md b/docs/tasks/phase-2.md index 167b700..50d00f3 100644 --- a/docs/tasks/phase-2.md +++ b/docs/tasks/phase-2.md @@ -43,15 +43,15 @@ - [x] Fetch issues for a repo from PDS records (`listIssueRecords` + `listIssueStateRecords` from owner's PDS) - [x] Display issue list with state filter (open/closed) -- [ ] Issue detail view: title, body, author, state -- [ ] Issue comments: fetch `sh.tangled.repo.issue.comment` records, render threaded +- [x] Issue detail view: title, body, author, state +- [x] Issue comments: fetch `sh.tangled.repo.issue.comment` records, render threaded ## Pull Requests (read-only) - [x] Fetch PRs for a repo from PDS records (`listPullRecords` + `listPullStatusRecords` from owner's PDS) - [x] Display PR list with status filter (open/closed/merged) -- [ ] PR detail view: title, body, author, source/target branches -- [ ] PR comments: fetch `sh.tangled.repo.pull.comment` records +- [x] PR detail view: title, body, author, source/target branches +- [x] PR comments: fetch `sh.tangled.repo.pull.comment` records ## Caching diff --git a/src/app/router/index.ts b/src/app/router/index.ts index cef10a9..97e966a 100644 --- a/src/app/router/index.ts +++ b/src/app/router/index.ts @@ -12,12 +12,36 @@ const routes: RouteRecordRaw[] = [ { path: "", redirect: "/tabs/home" }, { path: "home", component: () => import("@/features/home/HomePage.vue") }, { path: "home/repo/:owner/:repo", component: () => import("@/features/repo/RepoDetailPage.vue") }, + { + path: "home/repo/:owner/:repo/issues/:issueId", + component: () => import("@/features/repo/IssueDetailPage.vue"), + }, + { + path: "home/repo/:owner/:repo/pulls/:pullId", + component: () => import("@/features/repo/PullRequestDetailPage.vue"), + }, { path: "home/user/:handle", component: () => import("@/features/profile/UserProfilePage.vue") }, { path: "explore", component: () => import("@/features/explore/ExplorePage.vue") }, { path: "explore/repo/:owner/:repo", component: () => import("@/features/repo/RepoDetailPage.vue") }, + { + path: "explore/repo/:owner/:repo/issues/:issueId", + component: () => import("@/features/repo/IssueDetailPage.vue"), + }, + { + path: "explore/repo/:owner/:repo/pulls/:pullId", + component: () => import("@/features/repo/PullRequestDetailPage.vue"), + }, { path: "explore/user/:handle", component: () => import("@/features/profile/UserProfilePage.vue") }, { path: "activity", component: () => import("@/features/activity/ActivityPage.vue") }, { path: "activity/repo/:owner/:repo", component: () => import("@/features/repo/RepoDetailPage.vue") }, + { + path: "activity/repo/:owner/:repo/issues/:issueId", + component: () => import("@/features/repo/IssueDetailPage.vue"), + }, + { + path: "activity/repo/:owner/:repo/pulls/:pullId", + component: () => import("@/features/repo/PullRequestDetailPage.vue"), + }, { path: "activity/user/:handle", component: () => import("@/features/profile/UserProfilePage.vue") }, { path: "profile", component: () => import("@/features/profile/ProfilePage.vue") }, ], diff --git a/src/components/repo/CommentThread.vue b/src/components/repo/CommentThread.vue new file mode 100644 index 0000000..9b41b5b --- /dev/null +++ b/src/components/repo/CommentThread.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/src/domain/models/comment.ts b/src/domain/models/comment.ts new file mode 100644 index 0000000..3dd9a9a --- /dev/null +++ b/src/domain/models/comment.ts @@ -0,0 +1,15 @@ +export type DiscussionComment = { + atUri: string; + rkey: string; + body: string; + authorDid: string; + authorHandle: string; + createdAt: string; + mentions?: string[]; + references?: string[]; + depth: number; +}; + +export type IssueComment = DiscussionComment & { issueAtUri: string; replyTo?: string }; + +export type PullRequestComment = DiscussionComment & { pullAtUri: string }; diff --git a/src/domain/models/issue.ts b/src/domain/models/issue.ts index a65ea8e..345997b 100644 --- a/src/domain/models/issue.ts +++ b/src/domain/models/issue.ts @@ -2,6 +2,8 @@ type IssueState = "open" | "closed"; export type IssueSummary = { atUri: string; + rkey: string; + repoAtUri: string; title: string; authorDid: string; authorHandle: string; @@ -9,3 +11,5 @@ export type IssueSummary = { createdAt: string; commentCount?: number; }; + +export type IssueDetail = IssueSummary & { body?: string; mentions?: string[]; references?: string[] }; diff --git a/src/domain/models/pull-request.ts b/src/domain/models/pull-request.ts index af92e19..685bad6 100644 --- a/src/domain/models/pull-request.ts +++ b/src/domain/models/pull-request.ts @@ -2,6 +2,7 @@ type PRStatus = "open" | "merged" | "closed"; export type PullRequestSummary = { atUri: string; + rkey: string; title: string; authorDid: string; authorHandle: string; @@ -9,6 +10,16 @@ export type PullRequestSummary = { createdAt: string; updatedAt?: string; sourceBranch: string; + sourceRepoAtUri?: string; + sourceSha?: string; targetBranch: string; + targetRepoAtUri: string; roundCount?: number; }; + +export type PullRequestDetail = PullRequestSummary & { + body?: string; + mentions?: string[]; + references?: string[]; + patch?: string; +}; diff --git a/src/features/repo/IssueDetailPage.vue b/src/features/repo/IssueDetailPage.vue new file mode 100644 index 0000000..b129216 --- /dev/null +++ b/src/features/repo/IssueDetailPage.vue @@ -0,0 +1,265 @@ + + + + + diff --git a/src/features/repo/PullRequestDetailPage.vue b/src/features/repo/PullRequestDetailPage.vue new file mode 100644 index 0000000..3a43fce --- /dev/null +++ b/src/features/repo/PullRequestDetailPage.vue @@ -0,0 +1,303 @@ + + + + + diff --git a/src/features/repo/RepoDetailPage.vue b/src/features/repo/RepoDetailPage.vue index 1b9a91a..d03a928 100644 --- a/src/features/repo/RepoDetailPage.vue +++ b/src/features/repo/RepoDetailPage.vue @@ -45,16 +45,24 @@ :knot-host="knotHost" :knot-repo="knotRepo" :branch="defaultBranch" /> - - + +