@@ -20,7 +20,11 @@ import type {
2020} from '../generated/graphql' ;
2121import type GitHubClient from '../github/GitHubClient' ;
2222import type { DiffCommitIDs , DiffWithCommitIDs } from '../github/diffTypes' ;
23- import type { GitHubPullRequestReviewThread , PullRequest } from '../github/pullRequestTimelineTypes' ;
23+ import type {
24+ GitHubPullRequestReviewThread ,
25+ PullRequest ,
26+ PullRequestReviewComment ,
27+ } from '../github/pullRequestTimelineTypes' ;
2428import type { PullsQueryInput , PullsWithPageInfo } from '../github/pullsTypes' ;
2529import type { Blob , Commit , DateTime , GitObjectID , ID , Version , VersionCommit } from '../github/types' ;
2630
@@ -706,6 +710,118 @@ export const gitHubPullRequestsAtom = atomFamily(
706710 } ,
707711) ;
708712
713+ // =============================================================================
714+ // Pull Request Pending Review ID
715+ // =============================================================================
716+
717+ /**
718+ * Migrated from: gitHubPullRequestPendingReviewID selector in recoil.ts
719+ *
720+ * A PR should have at most a single pending review per user. Any inline
721+ * comments made will either create a new pending review or be added to the
722+ * existing one.
723+ */
724+ export const gitHubPullRequestPendingReviewIDAtom = atom < ID | null > ( get => {
725+ const pullRequest = get ( gitHubPullRequestAtom ) ;
726+ const pendingReview = ( pullRequest ?. timelineItems ?. nodes ?? [ ] ) . find (
727+ item => item ?. __typename === 'PullRequestReview' && item . state === 'PENDING' ,
728+ ) ;
729+ return ( pendingReview as { id : ID } | undefined ) ?. id ?? null ;
730+ } ) ;
731+
732+ // =============================================================================
733+ // Pull Request Review Threads
734+ // =============================================================================
735+
736+ /**
737+ * Migrated from: gitHubPullRequestReviewThreads selector in recoil.ts
738+ *
739+ * Extracts and normalizes review threads from the pull request.
740+ */
741+ export const gitHubPullRequestReviewThreadsAtom = atom < GitHubPullRequestReviewThread [ ] > ( get => {
742+ const pullRequest = get ( gitHubPullRequestAtom ) ;
743+ return ( pullRequest ?. reviewThreads . nodes ?? [ ] ) . filter ( notEmpty ) . map ( reviewThread => {
744+ const { originalLine, diffSide, comments} = reviewThread ;
745+ const normalizedComments = ( comments ?. nodes ?? [ ] )
746+ . map ( comment => {
747+ if ( comment == null ) {
748+ return null ;
749+ }
750+
751+ const { id, author, originalCommit, path, state, bodyHTML} = comment ;
752+ const reviewThreadComment = {
753+ id,
754+ author : author ?? null ,
755+ originalCommit,
756+ path,
757+ state,
758+ bodyHTML,
759+ } ;
760+ return reviewThreadComment ;
761+ } )
762+ . filter ( notEmpty ) ;
763+ const firstCommentID = normalizedComments [ 0 ] . id ;
764+ return {
765+ firstCommentID,
766+ originalLine,
767+ diffSide,
768+ comments : normalizedComments ,
769+ } ;
770+ } ) ;
771+ } ) ;
772+
773+ /**
774+ * Migrated from: gitHubPullRequestReviewThreadsByFirstCommentID selector in recoil.ts
775+ *
776+ * Returns review threads indexed by the ID of their first comment.
777+ * Used to look up thread information when rendering inline comments.
778+ */
779+ export const gitHubPullRequestReviewThreadsByFirstCommentIDAtom = atom < {
780+ [ id : ID ] : GitHubPullRequestReviewThread ;
781+ } > ( get => {
782+ return Object . fromEntries (
783+ get ( gitHubPullRequestReviewThreadsAtom ) . map ( thread => [ thread . firstCommentID , thread ] ) ,
784+ ) ;
785+ } ) ;
786+
787+ // =============================================================================
788+ // Pull Request Comment Lookup
789+ // =============================================================================
790+
791+ // Note: PullRequestReviewComment type is imported from pullRequestTimelineTypes.ts
792+
793+ /**
794+ * Internal atom that indexes all review comments by their ID.
795+ */
796+ const gitHubPullRequestReviewCommentsByIDAtom = atom < Map < ID , PullRequestReviewComment > > ( get => {
797+ const reviewThreads = get ( gitHubPullRequestReviewThreadsAtom ) ;
798+ const commentsByID = new Map < ID , PullRequestReviewComment > ( ) ;
799+ reviewThreads . forEach ( ( { originalLine, comments} ) => {
800+ comments . forEach ( comment => {
801+ const { id} = comment ;
802+ if ( id != null ) {
803+ commentsByID . set ( id , { originalLine, comment} ) ;
804+ }
805+ } ) ;
806+ } ) ;
807+ return commentsByID ;
808+ } ) ;
809+
810+ /**
811+ * Migrated from: gitHubPullRequestCommentForID selectorFamily in recoil.ts
812+ *
813+ * Looks up a review comment by its ID. Returns the comment with its
814+ * original line number for display purposes.
815+ */
816+ export const gitHubPullRequestCommentForIDAtom = atomFamily (
817+ ( id : ID ) =>
818+ atom < PullRequestReviewComment | null > ( get => {
819+ const commentsByID = get ( gitHubPullRequestReviewCommentsByIDAtom ) ;
820+ return commentsByID . get ( id ) ?? null ;
821+ } ) ,
822+ ( a , b ) => a === b ,
823+ ) ;
824+
709825// =============================================================================
710826// GitHub Blob
711827// =============================================================================
0 commit comments