From d78bf950480e2450f772ae032ec070ec8842b78d Mon Sep 17 00:00:00 2001 From: Keith Daulton Date: Wed, 22 Mar 2023 11:02:49 -0400 Subject: [PATCH] Adds actions for PRs on the focus view --- .vscode/queries.github-graphql-nb | 2 +- src/commands/ghpr/openOrCreateWorktree.ts | 2 +- src/git/models/pullRequest.ts | 3 + src/plus/github/github.ts | 70 ++++++----- src/plus/github/models.ts | 4 + src/plus/webviews/focus/focusWebview.ts | 140 +++++++++++++++++++-- src/plus/webviews/focus/protocol.ts | 18 ++- .../apps/plus/focus/components/pull-request-row.ts | 23 +++- src/webviews/apps/plus/focus/focus.ts | 23 ++++ src/webviews/apps/shared/components/code-icon.ts | 13 +- 10 files changed, 246 insertions(+), 52 deletions(-) diff --git a/.vscode/queries.github-graphql-nb b/.vscode/queries.github-graphql-nb index dbd2eb4..ae77066 100644 --- a/.vscode/queries.github-graphql-nb +++ b/.vscode/queries.github-graphql-nb @@ -1 +1 @@ -{"cells":[{"code":"### Get Default Branch & Tip","kind":"markdown"},{"code":"query getDefaultBranchAndTip(\n\t$owner: String!\n\t$repo: String!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tdefaultBranchRef {\n\t\t\tname\n\t\t\ttarget { oid }\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Get Branches","kind":"markdown"},{"code":"query getBranches(\n\t$owner: String!\n\t$repo: String!\n\t$branchQuery: String\n\t$cursor: String\n\t$limit: Int = 100\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\trefs(query: $branchQuery, refPrefix: \"refs/heads/\", first: $limit, after: $cursor, orderBy: { field: TAG_COMMIT_DATE, direction: DESC }) {\n\t\t\tpageInfo {\n\t\t\t\tendCursor\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tname\n\t\t\t\ttarget {\n\t\t\t\t\toid\n\t\t\t\t\tcommitUrl\n\t\t\t\t\t...on Commit {\n\t\t\t\t\t\tauthoredDate\n\t\t\t\t\t\tcommittedDate\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Get Blame","kind":"markdown"},{"code":"query getBlame(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String!\n) {\n\tviewer { name }\n\trepository(owner: $owner, name: $repo) {\n\t\tobject(expression: $ref) {\n\t\t\t...on Commit {\n\t\t\t\tblame(path: $path) {\n\t\t\t\t\tranges {\n\t\t\t\t\t\tstartingLine\n\t\t\t\t\t\tendingLine\n\t\t\t\t\t\tage\n\t\t\t\t\t\tcommit {\n\t\t\t\t\t\t\toid\n\t\t\t\t\t\t\tparents(first: 3) { nodes { oid } }\n\t\t\t\t\t\t\tmessage\n\t\t\t\t\t\t\tadditions\n\t\t\t\t\t\t\tchangedFiles\n\t\t\t\t\t\t\tdeletions\n\t\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\t\tavatarUrl\n\t\t\t\t\t\t\t\tdate\n\t\t\t\t\t\t\t\temail\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcommitter {\n\t\t\t\t\t\t\t\tdate\n\t\t\t\t\t\t\t\temail\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"gitkraken\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"0b8f151bd0458340b7779a64884c97754c3cedb8\",\n\t\"path\": \"package.json\"\n}","kind":"code"},{"code":"### Get Commit for File","kind":"markdown"},{"code":"query getCommitForFile(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tref(qualifiedName: $ref) {\n\t\t\ttarget {\n\t\t\t\t... on Commit {\n\t\t\t\t\thistory(first: 1, path: $path) {\n\t\t\t\t\t\tnodes {\n\t\t\t\t\t\t\toid\n\t\t\t\t\t\t\tparents(first: 3) { nodes { oid } }\n\t\t\t\t\t\t\tmessage\n\t\t\t\t\t\t\tadditions\n\t\t\t\t\t\t\tchangedFiles\n\t\t\t\t\t\t\tdeletions\n\t\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\t\tdate\n\t\t\t\t\t\t\t\temail\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcommitter { date }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"refs/heads/main\",\n\t\"path\": \"src/extension.ts\"\n}","kind":"code"},{"code":"### Get Current User","kind":"markdown"},{"code":"query getCurrentUser(\n\t$owner: String!\n\t$repo: String!\n) {\n\tviewer { name }\n\trepository(name: $repo owner: $owner) {\n\t\tviewerPermission\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Get Commit","kind":"markdown"},{"code":"query getCommit(\n\t$owner: String!\n\t$repo: String!\n\t$ref: GitObjectID!\n) {\n\trepository(name: $repo owner: $owner) {\n\t\tobject(oid: $ref) {\n\t\t\t...on Commit {\n\t\t\t\toid\n\t\t\t\tparents(first: 3) { nodes { oid } }\n\t\t\t\tmessage\n\t\t\t\tadditions\n\t\t\t\tchangedFiles\n\t\t\t\tdeletions\n\t\t\t\tauthor {\n\t\t\t\t\tdate\n\t\t\t\t\temail\n\t\t\t\t\tname\n\t\t\t\t}\n\t\t\t\tcommitter { date }\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"54f28933055124d6ba3808a787f6947c929f9db0\"\n}","kind":"code"},{"code":"### Get Commits","kind":"markdown"},{"code":"query getCommits(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String\n\t$author: CommitAuthor\n\t$after: String\n\t$before: String\n\t$limit: Int = 100\n\t$since: GitTimestamp\n\t$until: GitTimestamp\n) {\n\tviewer { name }\n\trepository(name: $repo, owner: $owner) {\n\t\tobject(expression: $ref) {\n\t\t\t... on Commit {\n\t\t\t\thistory(first: $limit, author: $author, path: $path, after: $after, before: $before, since: $since, until: $until) {\n\t\t\t\t\tpageInfo {\n\t\t\t\t\t\tstartCursor\n\t\t\t\t\t\tendCursor\n\t\t\t\t\t\thasNextPage\n\t\t\t\t\t\thasPreviousPage\n\t\t\t\t\t}\n\t\t\t\t\tnodes {\n\t\t\t\t\t\t... on Commit {\n\t\t\t\t\t\t\toid\n\t\t\t\t\t\t\tmessage\n\t\t\t\t\t\t\tparents(first: 3) { nodes { oid } }\n\t\t\t\t\t\t\tadditions\n\t\t\t\t\t\t\tchangedFiles\n\t\t\t\t\t\t\tdeletions\n\t\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\t\tavatarUrl\n\t\t\t\t\t\t\t\tdate\n\t\t\t\t\t\t\t\temail\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcommitter {\n\t\t\t\t\t\t\t\t date\n\t\t\t\t\t\t\t\t email\n\t\t\t\t\t\t\t\t name\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"HEAD\",\n\t\"-path\": \"src/extension.ts\",\n\t\"since\": \"2022-02-07T00:00:00Z\"\n}","kind":"code"},{"code":"### Get Tags","kind":"markdown"},{"code":"query getTags(\n\t$owner: String!\n\t$repo: String!\n\t$tagQuery: String\n\t$cursor: String\n\t$limit: Int = 100\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\trefs(query: $tagQuery, refPrefix: \"refs/tags/\", first: $limit, after: $cursor, orderBy: { field: TAG_COMMIT_DATE, direction: DESC }) {\n\t\t\tpageInfo {\n\t\t\t\tendCursor\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tname\n\t\t\t\ttarget {\n\t\t\t\t\toid\n\t\t\t\t\tcommitUrl\n\t\t\t\t\t...on Commit {\n\t\t\t\t\t\tauthoredDate\n\t\t\t\t\t\tcommittedDate\n\t\t\t\t\t\tmessage\n\t\t\t\t\t}\n\t\t\t\t\t...on Tag {\n\t\t\t\t\t\tmessage\n\t\t\t\t\t\ttagger { date }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Get collaborators ","kind":"markdown"},{"code":"query getCollaborators (\n\t$owner: String!\n\t$repo: String!\n\t$cursor: String\n\t$limit: Int = 100\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tcollaborators(affiliation: ALL, first: $limit, after: $cursor) {\n\t\t\tpageInfo {\n\t\t\t\tendCursor\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tname\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Resolve reference","kind":"markdown"},{"code":"query resolveReference(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tobject(expression: $ref) {\n\t\t\t... on Commit {\n\t\t\t\thistory(first: 1, path: $path) {\n\t\t\t\t\tnodes { oid }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"d790e9db047769de079f6838c3578f3a47bf5930^\",\n\t\"path\": \"CODE_OF_CONDUCT.md\"\n}","kind":"code"},{"code":"### Get branches that contain commit","kind":"markdown"},{"code":"query getCommitBranches(\n\t$owner: String!\n\t$repo: String!\n\t$since: GitTimestamp!\n\t$until: GitTimestamp!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\trefs(first: 20, refPrefix: \"refs/heads/\", orderBy: { field: TAG_COMMIT_DATE, direction: DESC }) {\n\t\t\tnodes {\n\t\t\t\tname\n\t\t\t\ttarget {\n\t\t\t\t\t... on Commit {\n\t\t\t\t\t\thistory(first: 3, since: $since until: $until) {\n\t\t\t\t\t\t\tnodes { oid }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"since\": \"2022-01-06T01:07:46-04:00\",\n\t\"until\": \"2022-01-06T01:07:46-05:00\"\n}","kind":"code"},{"code":"query getCommitBranch(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$since: GitTimestamp!\n\t$until: GitTimestamp!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tref(qualifiedName: $ref) {\n\t\t\ttarget {\n\t\t\t\t... on Commit {\n\t\t\t\t\thistory(first: 3, since: $since until: $until) {\n\t\t\t\t\t\tnodes { oid }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"refs/heads/main\",\n\t\"since\": \"2022-01-06T01:07:46-04:00\",\n\t\"until\": \"2022-01-06T01:07:46-05:00\"\n}","kind":"code"},{"code":"### Get commit count for branch (ref)","kind":"markdown"},{"code":"query getCommitCount(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tref(qualifiedName: $ref) {\n\t\t\ttarget {\n\t\t\t\t... on Commit {\n\t\t\t\t\thistory(first: 1) {\n\t\t\t\t\t\ttotalCount\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"refs/heads/main\"\n}","kind":"code"},{"code":"### Get commit refs (sha)","kind":"markdown"},{"code":"query getCommitRefs(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String\n\t$since: GitTimestamp\n\t$until: GitTimestamp\n\t$limit: Int = 1\n) {\n\tviewer { name }\n\trepository(name: $repo, owner: $owner) {\n\t\tref(qualifiedName: $ref) {\n\t\t\thistory(first: $limit, path: $path, since: $since, until: $until) {\n\t\t\t\tnodes { oid, message }\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"refs/heads/main\",\n\t\"path\": \"extension.ts\",\n\t\"limit\": 2\n}","kind":"code"},{"code":"### Get next file commit","kind":"markdown"},{"code":"query getCommitDate(\n\t$owner: String!\n\t$repo: String!\n\t$ref: GitObjectID!\n) {\n\trepository(name: $repo owner: $owner) {\n\t\tobject(oid: $ref) {\n\t\t\t...on Commit {\n\t\t\t\tcommitter { date }\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"a03ff942c40665c451bd4c7768f46e3e5f00e97c\"\n}","kind":"code"},{"code":"query getNextCommitCursor(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String!\n\t$since: GitTimestamp!\n) {\n\trepository(name: $repo owner: $owner) {\n\t\tobject(expression: $ref) {\n\t\t\t... on Commit {\n\t\t\t\thistory(first:1, path: $path, since: $since) {\n\t\t\t\t\ttotalCount\n\t\t\t\t\tpageInfo { startCursor }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"b062e960b6ee5ca7ac081dd84d9217bd4b2051e0\",\n \"path\": \"src/extension.ts\",\n \"since\": \"2021-11-03T02:46:29-04:00\"\n}","kind":"code"},{"code":"query getNextCommit(\n\t$owner: String!\n\t$repo: String!\n $ref: String!\n $path: String!\n\t$before: String!\n) {\trepository(name: $repo owner: $owner) {\n object(expression: $ref) {\n ... on Commit {\n history(last:4, path: $path, before: $before) {\n totalCount\n pageInfo {\n startCursor\n }\n nodes {\n oid\n message\n committedDate\n }\n }\n }\n }\n }\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"496c35eaeff2c33d3f1256a25d83198ace6aa6b0\",\n \"path\": \"src/extension.ts\",\n \"before\": \"496c35eaeff2c33d3f1256a25d83198ace6aa6b0 4\"\n}","kind":"code"},{"code":"### Get Pull Request for Branch","kind":"markdown"},{"code":"query getPullRequestForBranch(\n\t$owner: String!\n\t$repo: String!\n\t$branch: String!\n\t$limit: Int!\n\t$include: [PullRequestState!]\n\t$avatarSize: Int\n) {\n\trepository(name: $repo, owner: $owner) {\n\t\trefs(query: $branch, refPrefix: \"refs/heads/\", first: 1) {\n\t\t\tnodes {\n\t\t\t\tassociatedPullRequests(first: $limit, orderBy: {field: UPDATED_AT, direction: DESC}, states: $include) {\n\t\t\t\t\tnodes {\n\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\tlogin\n\t\t\t\t\t\t\tavatarUrl(size: $avatarSize)\n\t\t\t\t\t\t\turl\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpermalink\n\t\t\t\t\t\tnumber\n\t\t\t\t\t\ttitle\n\t\t\t\t\t\tstate\n\t\t\t\t\t\tupdatedAt\n\t\t\t\t\t\tclosedAt\n\t\t\t\t\t\tmergedAt\n\t\t\t\t\t\trepository {\n\t\t\t\t\t\t\tisFork\n\t\t\t\t\t\t\towner {\n\t\t\t\t\t\t\t\tlogin\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"gitkraken\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"branch\": \"main\",\n\t\"limit\": 1\n}","kind":"code"},{"code":"### Get My Assigned Pull Requests","kind":"markdown"},{"code":"query getMyAssignedPullRequests($assigned: String!) {\n search(first: 100, query: $assigned, type: ISSUE) {\n nodes {\n ... on PullRequest {\n assignees(first: 100) {\n nodes {\n login\n avatarUrl\n url\n }\n }\n author {\n login\n avatarUrl\n url\n }\n baseRefName\n baseRefOid\n baseRepository {\n name\n owner {\n login\n }\n }\n checksUrl\n isDraft\n isCrossRepository\n isReadByViewer\n headRefName\n headRefOid\n headRepository {\n name\n owner {\n login\n }\n }\n permalink\n number\n title\n state\n additions\n deletions\n updatedAt\n closedAt\n mergeable\n mergedAt\n mergedBy {\n login\n }\n repository {\n isFork\n owner {\n login\n }\n }\n reviewDecision\n reviewRequests(first: 100) {\n nodes {\n asCodeOwner\n requestedReviewer {\n ... on User {\n login\n avatarUrl\n url\n }\n }\n }\n }\n totalCommentsCount\n }\n }\n }\n}\n\nvariables {\n \"assigned\": \"assignee:@me is:pr is:open archived:false repo:gitkraken/vscode-gitlens\"\n}","kind":"code"},{"code":"### Get My Assigned Issues","kind":"markdown"},{"code":"query MyQuery($assigned: String!) {\n search(first: 2, query: $assigned, type: ISSUE) {\n nodes {\n ... on Issue {\n assignees(first: 100) {\n nodes {\n login\n url\n avatarUrl\n }\n }\n author {\n login\n avatarUrl\n url\n }\n comments {\n totalCount\n }\n number\n title\n url\n createdAt\n closedAt\n closed\n updatedAt\n labels(first: 20) {\n nodes {\n color\n name\n }\n }\n reactions(content: THUMBS_UP) {\n totalCount\n }\n repository {\n name\n owner {\n login\n }\n }\n }\n }\n }\n}\n\nvariables {\n \"assigned\": \"assignee:@me type:issue is:open archived:false repo:gitkraken/vscode-gitlens\"\n}","kind":"code"}]} \ No newline at end of file +{"cells":[{"code":"### Get Default Branch & Tip","kind":"markdown"},{"code":"query getDefaultBranchAndTip(\n\t$owner: String!\n\t$repo: String!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tdefaultBranchRef {\n\t\t\tname\n\t\t\ttarget { oid }\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Get Branches","kind":"markdown"},{"code":"query getBranches(\n\t$owner: String!\n\t$repo: String!\n\t$branchQuery: String\n\t$cursor: String\n\t$limit: Int = 100\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\trefs(query: $branchQuery, refPrefix: \"refs/heads/\", first: $limit, after: $cursor, orderBy: { field: TAG_COMMIT_DATE, direction: DESC }) {\n\t\t\tpageInfo {\n\t\t\t\tendCursor\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tname\n\t\t\t\ttarget {\n\t\t\t\t\toid\n\t\t\t\t\tcommitUrl\n\t\t\t\t\t...on Commit {\n\t\t\t\t\t\tauthoredDate\n\t\t\t\t\t\tcommittedDate\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Get Blame","kind":"markdown"},{"code":"query getBlame(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String!\n) {\n\tviewer { name }\n\trepository(owner: $owner, name: $repo) {\n\t\tobject(expression: $ref) {\n\t\t\t...on Commit {\n\t\t\t\tblame(path: $path) {\n\t\t\t\t\tranges {\n\t\t\t\t\t\tstartingLine\n\t\t\t\t\t\tendingLine\n\t\t\t\t\t\tage\n\t\t\t\t\t\tcommit {\n\t\t\t\t\t\t\toid\n\t\t\t\t\t\t\tparents(first: 3) { nodes { oid } }\n\t\t\t\t\t\t\tmessage\n\t\t\t\t\t\t\tadditions\n\t\t\t\t\t\t\tchangedFiles\n\t\t\t\t\t\t\tdeletions\n\t\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\t\tavatarUrl\n\t\t\t\t\t\t\t\tdate\n\t\t\t\t\t\t\t\temail\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcommitter {\n\t\t\t\t\t\t\t\tdate\n\t\t\t\t\t\t\t\temail\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"gitkraken\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"0b8f151bd0458340b7779a64884c97754c3cedb8\",\n\t\"path\": \"package.json\"\n}","kind":"code"},{"code":"### Get Commit for File","kind":"markdown"},{"code":"query getCommitForFile(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tref(qualifiedName: $ref) {\n\t\t\ttarget {\n\t\t\t\t... on Commit {\n\t\t\t\t\thistory(first: 1, path: $path) {\n\t\t\t\t\t\tnodes {\n\t\t\t\t\t\t\toid\n\t\t\t\t\t\t\tparents(first: 3) { nodes { oid } }\n\t\t\t\t\t\t\tmessage\n\t\t\t\t\t\t\tadditions\n\t\t\t\t\t\t\tchangedFiles\n\t\t\t\t\t\t\tdeletions\n\t\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\t\tdate\n\t\t\t\t\t\t\t\temail\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcommitter { date }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"refs/heads/main\",\n\t\"path\": \"src/extension.ts\"\n}","kind":"code"},{"code":"### Get Current User","kind":"markdown"},{"code":"query getCurrentUser(\n\t$owner: String!\n\t$repo: String!\n) {\n\tviewer { name }\n\trepository(name: $repo owner: $owner) {\n\t\tviewerPermission\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Get Commit","kind":"markdown"},{"code":"query getCommit(\n\t$owner: String!\n\t$repo: String!\n\t$ref: GitObjectID!\n) {\n\trepository(name: $repo owner: $owner) {\n\t\tobject(oid: $ref) {\n\t\t\t...on Commit {\n\t\t\t\toid\n\t\t\t\tparents(first: 3) { nodes { oid } }\n\t\t\t\tmessage\n\t\t\t\tadditions\n\t\t\t\tchangedFiles\n\t\t\t\tdeletions\n\t\t\t\tauthor {\n\t\t\t\t\tdate\n\t\t\t\t\temail\n\t\t\t\t\tname\n\t\t\t\t}\n\t\t\t\tcommitter { date }\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"54f28933055124d6ba3808a787f6947c929f9db0\"\n}","kind":"code"},{"code":"### Get Commits","kind":"markdown"},{"code":"query getCommits(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String\n\t$author: CommitAuthor\n\t$after: String\n\t$before: String\n\t$limit: Int = 100\n\t$since: GitTimestamp\n\t$until: GitTimestamp\n) {\n\tviewer { name }\n\trepository(name: $repo, owner: $owner) {\n\t\tobject(expression: $ref) {\n\t\t\t... on Commit {\n\t\t\t\thistory(first: $limit, author: $author, path: $path, after: $after, before: $before, since: $since, until: $until) {\n\t\t\t\t\tpageInfo {\n\t\t\t\t\t\tstartCursor\n\t\t\t\t\t\tendCursor\n\t\t\t\t\t\thasNextPage\n\t\t\t\t\t\thasPreviousPage\n\t\t\t\t\t}\n\t\t\t\t\tnodes {\n\t\t\t\t\t\t... on Commit {\n\t\t\t\t\t\t\toid\n\t\t\t\t\t\t\tmessage\n\t\t\t\t\t\t\tparents(first: 3) { nodes { oid } }\n\t\t\t\t\t\t\tadditions\n\t\t\t\t\t\t\tchangedFiles\n\t\t\t\t\t\t\tdeletions\n\t\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\t\tavatarUrl\n\t\t\t\t\t\t\t\tdate\n\t\t\t\t\t\t\t\temail\n\t\t\t\t\t\t\t\tname\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcommitter {\n\t\t\t\t\t\t\t\t date\n\t\t\t\t\t\t\t\t email\n\t\t\t\t\t\t\t\t name\n\t\t\t\t\t\t\t }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"HEAD\",\n\t\"-path\": \"src/extension.ts\",\n\t\"since\": \"2022-02-07T00:00:00Z\"\n}","kind":"code"},{"code":"### Get Tags","kind":"markdown"},{"code":"query getTags(\n\t$owner: String!\n\t$repo: String!\n\t$tagQuery: String\n\t$cursor: String\n\t$limit: Int = 100\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\trefs(query: $tagQuery, refPrefix: \"refs/tags/\", first: $limit, after: $cursor, orderBy: { field: TAG_COMMIT_DATE, direction: DESC }) {\n\t\t\tpageInfo {\n\t\t\t\tendCursor\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tname\n\t\t\t\ttarget {\n\t\t\t\t\toid\n\t\t\t\t\tcommitUrl\n\t\t\t\t\t...on Commit {\n\t\t\t\t\t\tauthoredDate\n\t\t\t\t\t\tcommittedDate\n\t\t\t\t\t\tmessage\n\t\t\t\t\t}\n\t\t\t\t\t...on Tag {\n\t\t\t\t\t\tmessage\n\t\t\t\t\t\ttagger { date }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Get collaborators ","kind":"markdown"},{"code":"query getCollaborators (\n\t$owner: String!\n\t$repo: String!\n\t$cursor: String\n\t$limit: Int = 100\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tcollaborators(affiliation: ALL, first: $limit, after: $cursor) {\n\t\t\tpageInfo {\n\t\t\t\tendCursor\n\t\t\t\thasNextPage\n\t\t\t}\n\t\t\tnodes {\n\t\t\t\tname\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\"\n}","kind":"code"},{"code":"### Resolve reference","kind":"markdown"},{"code":"query resolveReference(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tobject(expression: $ref) {\n\t\t\t... on Commit {\n\t\t\t\thistory(first: 1, path: $path) {\n\t\t\t\t\tnodes { oid }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"d790e9db047769de079f6838c3578f3a47bf5930^\",\n\t\"path\": \"CODE_OF_CONDUCT.md\"\n}","kind":"code"},{"code":"### Get branches that contain commit","kind":"markdown"},{"code":"query getCommitBranches(\n\t$owner: String!\n\t$repo: String!\n\t$since: GitTimestamp!\n\t$until: GitTimestamp!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\trefs(first: 20, refPrefix: \"refs/heads/\", orderBy: { field: TAG_COMMIT_DATE, direction: DESC }) {\n\t\t\tnodes {\n\t\t\t\tname\n\t\t\t\ttarget {\n\t\t\t\t\t... on Commit {\n\t\t\t\t\t\thistory(first: 3, since: $since until: $until) {\n\t\t\t\t\t\t\tnodes { oid }\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"since\": \"2022-01-06T01:07:46-04:00\",\n\t\"until\": \"2022-01-06T01:07:46-05:00\"\n}","kind":"code"},{"code":"query getCommitBranch(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$since: GitTimestamp!\n\t$until: GitTimestamp!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tref(qualifiedName: $ref) {\n\t\t\ttarget {\n\t\t\t\t... on Commit {\n\t\t\t\t\thistory(first: 3, since: $since until: $until) {\n\t\t\t\t\t\tnodes { oid }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"refs/heads/main\",\n\t\"since\": \"2022-01-06T01:07:46-04:00\",\n\t\"until\": \"2022-01-06T01:07:46-05:00\"\n}","kind":"code"},{"code":"### Get commit count for branch (ref)","kind":"markdown"},{"code":"query getCommitCount(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n) {\n\trepository(owner: $owner, name: $repo) {\n\t\tref(qualifiedName: $ref) {\n\t\t\ttarget {\n\t\t\t\t... on Commit {\n\t\t\t\t\thistory(first: 1) {\n\t\t\t\t\t\ttotalCount\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"refs/heads/main\"\n}","kind":"code"},{"code":"### Get commit refs (sha)","kind":"markdown"},{"code":"query getCommitRefs(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String\n\t$since: GitTimestamp\n\t$until: GitTimestamp\n\t$limit: Int = 1\n) {\n\tviewer { name }\n\trepository(name: $repo, owner: $owner) {\n\t\tref(qualifiedName: $ref) {\n\t\t\thistory(first: $limit, path: $path, since: $since, until: $until) {\n\t\t\t\tnodes { oid, message }\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"refs/heads/main\",\n\t\"path\": \"extension.ts\",\n\t\"limit\": 2\n}","kind":"code"},{"code":"### Get next file commit","kind":"markdown"},{"code":"query getCommitDate(\n\t$owner: String!\n\t$repo: String!\n\t$ref: GitObjectID!\n) {\n\trepository(name: $repo owner: $owner) {\n\t\tobject(oid: $ref) {\n\t\t\t...on Commit {\n\t\t\t\tcommitter { date }\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"a03ff942c40665c451bd4c7768f46e3e5f00e97c\"\n}","kind":"code"},{"code":"query getNextCommitCursor(\n\t$owner: String!\n\t$repo: String!\n\t$ref: String!\n\t$path: String!\n\t$since: GitTimestamp!\n) {\n\trepository(name: $repo owner: $owner) {\n\t\tobject(expression: $ref) {\n\t\t\t... on Commit {\n\t\t\t\thistory(first:1, path: $path, since: $since) {\n\t\t\t\t\ttotalCount\n\t\t\t\t\tpageInfo { startCursor }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"b062e960b6ee5ca7ac081dd84d9217bd4b2051e0\",\n \"path\": \"src/extension.ts\",\n \"since\": \"2021-11-03T02:46:29-04:00\"\n}","kind":"code"},{"code":"query getNextCommit(\n\t$owner: String!\n\t$repo: String!\n $ref: String!\n $path: String!\n\t$before: String!\n) {\trepository(name: $repo owner: $owner) {\n object(expression: $ref) {\n ... on Commit {\n history(last:4, path: $path, before: $before) {\n totalCount\n pageInfo {\n startCursor\n }\n nodes {\n oid\n message\n committedDate\n }\n }\n }\n }\n }\n}\n\nvariables {\n\t\"owner\": \"eamodio\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"ref\": \"496c35eaeff2c33d3f1256a25d83198ace6aa6b0\",\n \"path\": \"src/extension.ts\",\n \"before\": \"496c35eaeff2c33d3f1256a25d83198ace6aa6b0 4\"\n}","kind":"code"},{"code":"### Get Pull Request for Branch","kind":"markdown"},{"code":"query getPullRequestForBranch(\n\t$owner: String!\n\t$repo: String!\n\t$branch: String!\n\t$limit: Int!\n\t$include: [PullRequestState!]\n\t$avatarSize: Int\n) {\n\trepository(name: $repo, owner: $owner) {\n\t\trefs(query: $branch, refPrefix: \"refs/heads/\", first: 1) {\n\t\t\tnodes {\n\t\t\t\tassociatedPullRequests(first: $limit, orderBy: {field: UPDATED_AT, direction: DESC}, states: $include) {\n\t\t\t\t\tnodes {\n\t\t\t\t\t\tauthor {\n\t\t\t\t\t\t\tlogin\n\t\t\t\t\t\t\tavatarUrl(size: $avatarSize)\n\t\t\t\t\t\t\turl\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpermalink\n\t\t\t\t\t\tnumber\n\t\t\t\t\t\ttitle\n\t\t\t\t\t\tstate\n\t\t\t\t\t\tupdatedAt\n\t\t\t\t\t\tclosedAt\n\t\t\t\t\t\tmergedAt\n\t\t\t\t\t\trepository {\n\t\t\t\t\t\t\tisFork\n\t\t\t\t\t\t\towner {\n\t\t\t\t\t\t\t\tlogin\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvariables {\n\t\"owner\": \"gitkraken\",\n\t\"repo\": \"vscode-gitlens\",\n\t\"branch\": \"main\",\n\t\"limit\": 1\n}","kind":"code"},{"code":"### Get My Assigned Pull Requests","kind":"markdown"},{"code":"query getMyAssignedPullRequests($assigned: String!) {\n search(first: 100, query: $assigned, type: ISSUE) {\n nodes {\n ... on PullRequest {\n assignees(first: 100) {\n nodes {\n login\n avatarUrl\n url\n }\n }\n author {\n login\n avatarUrl\n url\n }\n baseRefName\n baseRefOid\n baseRepository {\n name\n owner {\n login\n }\n url\n }\n checksUrl\n isDraft\n isCrossRepository\n isReadByViewer\n headRefName\n headRefOid\n headRepository {\n name\n owner {\n login\n }\n url\n }\n permalink\n number\n title\n state\n additions\n deletions\n updatedAt\n closedAt\n mergeable\n mergedAt\n mergedBy {\n login\n }\n repository {\n isFork\n owner {\n login\n }\n }\n reviewDecision\n reviewRequests(first: 100) {\n nodes {\n asCodeOwner\n requestedReviewer {\n ... on User {\n login\n avatarUrl\n url\n }\n }\n }\n }\n totalCommentsCount\n }\n }\n }\n}\n\nvariables {\n \"assigned\": \"assignee:@me is:pr is:open archived:false repo:gitkraken/vscode-gitlens\"\n}","kind":"code"},{"code":"### Get My Assigned Issues","kind":"markdown"},{"code":"query MyQuery($assigned: String!) {\n search(first: 2, query: $assigned, type: ISSUE) {\n nodes {\n ... on Issue {\n assignees(first: 100) {\n nodes {\n login\n url\n avatarUrl\n }\n }\n author {\n login\n avatarUrl\n url\n }\n comments {\n totalCount\n }\n number\n title\n url\n createdAt\n closedAt\n closed\n updatedAt\n labels(first: 20) {\n nodes {\n color\n name\n }\n }\n reactions(content: THUMBS_UP) {\n totalCount\n }\n repository {\n name\n owner {\n login\n }\n }\n }\n }\n }\n}\n\nvariables {\n \"assigned\": \"assignee:@me type:issue is:open archived:false repo:gitkraken/vscode-gitlens\"\n}","kind":"code"}]} \ No newline at end of file diff --git a/src/commands/ghpr/openOrCreateWorktree.ts b/src/commands/ghpr/openOrCreateWorktree.ts index f316e88..12b0aee 100644 --- a/src/commands/ghpr/openOrCreateWorktree.ts +++ b/src/commands/ghpr/openOrCreateWorktree.ts @@ -16,7 +16,7 @@ interface GHPRPullRequestNode { readonly pullRequestModel: GHPRPullRequest; } -interface GHPRPullRequest { +export interface GHPRPullRequest { readonly base: { readonly repositoryCloneUrl: { readonly repositoryName: string; diff --git a/src/git/models/pullRequest.ts b/src/git/models/pullRequest.ts index 5184f96..e0f4e3d 100644 --- a/src/git/models/pullRequest.ts +++ b/src/git/models/pullRequest.ts @@ -32,6 +32,7 @@ export interface PullRequestRef { branch: string; sha: string; exists: boolean; + url: string; } export interface PullRequestRefs { @@ -102,6 +103,7 @@ export function serializePullRequest(value: PullRequest): PullRequestShape { repo: value.refs.head.repo, sha: value.refs.head.sha, branch: value.refs.head.branch, + url: value.refs.head.url, }, base: { exists: value.refs.base.exists, @@ -109,6 +111,7 @@ export function serializePullRequest(value: PullRequest): PullRequestShape { repo: value.refs.base.repo, sha: value.refs.base.sha, branch: value.refs.base.branch, + url: value.refs.base.url, }, isCrossRepository: value.refs.isCrossRepository, } diff --git a/src/plus/github/github.ts b/src/plus/github/github.ts index 53e8069..b4f6d7b 100644 --- a/src/plus/github/github.ts +++ b/src/plus/github/github.ts @@ -63,24 +63,25 @@ const emptyBlameResult: GitHubBlame = Object.freeze({ ranges: [] }); const prNodeProperties = ` assignees(first: 10) { - nodes { + nodes { + login + avatarUrl + url + } +} +author { login avatarUrl url - } -} -author { - login - avatarUrl - url } baseRefName baseRefOid baseRepository { - name - owner { - login - } + name + owner { + login + } + url } checksUrl isDraft @@ -89,10 +90,11 @@ isReadByViewer headRefName headRefOid headRepository { - name - owner { - login - } + name + owner { + login + } + url } permalink number @@ -105,33 +107,33 @@ closedAt mergeable mergedAt mergedBy { - login + login } repository { - isFork - owner { - login - } + isFork + owner { + login + } } repository { - isFork - owner { - login - } + isFork + owner { + login + } } reviewDecision reviewRequests(first: 10) { - nodes { - asCodeOwner - id - requestedReviewer { - ... on User { - login - avatarUrl - url - } + nodes { + asCodeOwner + id + requestedReviewer { + ... on User { + login + avatarUrl + url + } + } } - } } totalCommentsCount `; diff --git a/src/plus/github/models.ts b/src/plus/github/models.ts index 327b50a..03bc4d3 100644 --- a/src/plus/github/models.ts +++ b/src/plus/github/models.ts @@ -127,6 +127,7 @@ export interface GitHubDetailedPullRequest extends GitHubPullRequest { owner: { login: string; }; + url: string; }; headRefName: string; headRefOid: string; @@ -135,6 +136,7 @@ export interface GitHubDetailedPullRequest extends GitHubPullRequest { owner: { login: string; }; + url: string; }; reviewDecision: GitHubPullRequestReviewDecision; isReadByViewer: boolean; @@ -272,6 +274,7 @@ export function fromGitHubPullRequestDetailed( repo: pr.baseRepository?.name, sha: pr.headRefOid, branch: pr.headRefName, + url: pr.headRepository?.url, }, base: { exists: pr.baseRepository != null, @@ -279,6 +282,7 @@ export function fromGitHubPullRequestDetailed( repo: pr.baseRepository?.name, sha: pr.baseRefOid, branch: pr.baseRefName, + url: pr.baseRepository?.url, }, isCrossRepository: pr.isCrossRepository, }, diff --git a/src/plus/webviews/focus/focusWebview.ts b/src/plus/webviews/focus/focusWebview.ts index 6f4986b..3dd61a3 100644 --- a/src/plus/webviews/focus/focusWebview.ts +++ b/src/plus/webviews/focus/focusWebview.ts @@ -1,28 +1,40 @@ -import { Disposable } from 'vscode'; +import { Disposable, Uri, window } from 'vscode'; +import type { GHPRPullRequest } from '../../../commands'; import { Commands, ContextKeys } from '../../../constants'; import type { Container } from '../../../container'; import { setContext } from '../../../context'; import { PlusFeatures } from '../../../features'; +import { add as addRemote } from '../../../git/actions/remote'; +import * as RepoActions from '../../../git/actions/repository'; import type { SearchedIssue } from '../../../git/models/issue'; import { serializeIssue } from '../../../git/models/issue'; -import type { SearchedPullRequest } from '../../../git/models/pullRequest'; +import type { PullRequestShape, SearchedPullRequest } from '../../../git/models/pullRequest'; import { PullRequestMergeableState, PullRequestReviewDecision, serializePullRequest, } from '../../../git/models/pullRequest'; +import { createReference, getReferenceFromBranch } from '../../../git/models/reference'; import type { GitRemote } from '../../../git/models/remote'; import type { Repository, RepositoryChangeEvent } from '../../../git/models/repository'; import { RepositoryChange, RepositoryChangeComparisonMode } from '../../../git/models/repository'; +import { parseGitRemoteUrl } from '../../../git/parsers/remoteParser'; import type { RichRemoteProvider } from '../../../git/remotes/richRemoteProvider'; import type { Subscription } from '../../../subscription'; import { SubscriptionState } from '../../../subscription'; -import { registerCommand } from '../../../system/command'; +import { executeCommand, registerCommand } from '../../../system/command'; +import type { IpcMessage } from '../../../webviews/protocol'; +import { onIpc } from '../../../webviews/protocol'; import { WebviewBase } from '../../../webviews/webviewBase'; import type { SubscriptionChangeEvent } from '../../subscription/subscriptionService'; import { ensurePlusFeaturesEnabled } from '../../subscription/utils'; -import type { State } from './protocol'; -import { DidChangeStateNotificationType, DidChangeSubscriptionNotificationType } from './protocol'; +import type { OpenWorktreeParams, State, SwitchToBranchParams } from './protocol'; +import { + DidChangeStateNotificationType, + DidChangeSubscriptionNotificationType, + OpenWorktreeCommandType, + SwitchToBranchCommandType, +} from './protocol'; interface RepoWithRichRemote { repo: Repository; @@ -31,9 +43,13 @@ interface RepoWithRichRemote { isGitHub: boolean; } +interface SearchedPullRequestWithRemote extends SearchedPullRequest { + repoAndRemote: RepoWithRichRemote; +} + export class FocusWebview extends WebviewBase { private _bootstrapping = true; - private _pullRequests: SearchedPullRequest[] = []; + private _pullRequests: SearchedPullRequestWithRemote[] = []; private _issues: SearchedIssue[] = []; private _etagSubscription?: number; private _repositoryEventsDisposable?: Disposable; @@ -70,6 +86,111 @@ export class FocusWebview extends WebviewBase { void setContext(ContextKeys.FocusFocused, focused); } + protected override onMessageReceived(e: IpcMessage) { + switch (e.method) { + case SwitchToBranchCommandType.method: + onIpc(SwitchToBranchCommandType, e, params => this.onSwitchBranch(params)); + break; + case OpenWorktreeCommandType.method: + onIpc(OpenWorktreeCommandType, e, params => this.onOpenWorktree(params)); + break; + } + } + + private findSearchedPullRequest(pullRequest: PullRequestShape): SearchedPullRequestWithRemote | undefined { + return this._pullRequests?.find(r => r.pullRequest.id === pullRequest.id); + } + + private async getRemoteBranch(searchedPullRequest: SearchedPullRequestWithRemote) { + const pullRequest = searchedPullRequest.pullRequest; + const repo = await searchedPullRequest.repoAndRemote.repo.getMainRepository(); + const remoteUri = Uri.parse(pullRequest.refs!.head.url); + const remoteUrl = remoteUri.toString(); + if (repo == null) { + void window.showWarningMessage(`Unable to find main repository(${remoteUrl}) for PR #${pullRequest.id}`); + return; + } + + const [, remoteDomain, remotePath] = parseGitRemoteUrl(remoteUrl); + const remoteOwner = pullRequest.refs!.head.owner; + const ref = pullRequest.refs!.head.branch; + + let remote: GitRemote | undefined; + [remote] = await repo.getRemotes({ filter: r => r.matches(remoteDomain, remotePath) }); + if (remote != null) { + // Ensure we have the latest from the remote + await this.container.git.fetch(repo.path, { remote: remote.name }); + } else { + const result = await window.showInformationMessage( + `Unable to find a remote for '${remoteUrl}'. Would you like to add a new remote?`, + { modal: true }, + { title: 'Yes' }, + { title: 'No', isCloseAffordance: true }, + ); + if (result?.title !== 'Yes') return; + + await addRemote(repo, remoteOwner, remoteUrl, { + confirm: false, + fetch: true, + reveal: false, + }); + [remote] = await repo.getRemotes({ filter: r => r.url === remoteUrl }); + if (remote == null) return; + } + + const remoteBranchName = `${remote.name}/${ref}`; + const reference = createReference(remoteBranchName, repo.path, { + refType: 'branch', + name: remoteBranchName, + remote: true, + }); + + return { + remote: remote, + reference: reference, + }; + } + + private async onSwitchBranch({ pullRequest }: SwitchToBranchParams) { + const searchedPullRequestWithRemote = this.findSearchedPullRequest(pullRequest); + if (searchedPullRequestWithRemote == null) return Promise.resolve(); + + const remoteBranch = await this.getRemoteBranch(searchedPullRequestWithRemote); + if (remoteBranch == null) return Promise.resolve(); + + return RepoActions.switchTo(remoteBranch.remote.repoPath, remoteBranch.reference); + } + + private async onOpenWorktree({ pullRequest }: OpenWorktreeParams) { + const baseUri = Uri.parse(pullRequest.refs!.base.url); + const repoAndRemote = this.findSearchedPullRequest(pullRequest)?.repoAndRemote; + const localInfo = repoAndRemote!.repo.folder; + return executeCommand(Commands.OpenOrCreateWorktreeForGHPR, { + base: { + repositoryCloneUrl: { + repositoryName: pullRequest.refs!.base.repo, + owner: pullRequest.refs!.base.owner, + url: baseUri, + }, + }, + githubRepository: { + rootUri: localInfo!.uri, + }, + head: { + ref: pullRequest.refs!.head.branch, + sha: pullRequest.refs!.head.sha, + repositoryCloneUrl: { + repositoryName: pullRequest.refs!.head.repo, + owner: pullRequest.refs!.head.owner, + url: Uri.parse(pullRequest.refs!.head.url), + }, + }, + item: { + number: parseInt(pullRequest.id, 10), + }, + }); + } + private async onSubscriptionChanged(e: SubscriptionChangeEvent) { if (e.etag === this._etagSubscription) return; @@ -193,14 +314,15 @@ export class FocusWebview extends WebviewBase { } } - private async getMyPullRequests(richRepos: RepoWithRichRemote[]): Promise { + private async getMyPullRequests(richRepos: RepoWithRichRemote[]): Promise { const allPrs = []; - for (const { remote } of richRepos) { + for (const richRepo of richRepos) { + const { remote } = richRepo; const prs = await this.container.git.getMyPullRequests(remote); if (prs == null) { continue; } - allPrs.push(...prs.filter(pr => pr.reasons.length > 0)); + allPrs.push(...prs.filter(pr => pr.reasons.length > 0).map(pr => ({ ...pr, repoAndRemote: richRepo }))); } function getScore(pr: SearchedPullRequest) { diff --git a/src/plus/webviews/focus/protocol.ts b/src/plus/webviews/focus/protocol.ts index b922dc2..757d11c 100644 --- a/src/plus/webviews/focus/protocol.ts +++ b/src/plus/webviews/focus/protocol.ts @@ -1,7 +1,7 @@ import type { IssueShape } from '../../../git/models/issue'; import type { PullRequestShape } from '../../../git/models/pullRequest'; import type { Subscription } from '../../../subscription'; -import { IpcNotificationType } from '../../../webviews/protocol'; +import { IpcCommandType, IpcNotificationType } from '../../../webviews/protocol'; export type State = { isPlus: boolean; @@ -30,6 +30,22 @@ export interface RepoWithRichProvider { isConnected: boolean; } +// Commands + +export interface OpenWorktreeParams { + pullRequest: PullRequestShape; +} + +export const OpenWorktreeCommandType = new IpcCommandType('focus/pr/openWorktree'); + +export interface SwitchToBranchParams { + pullRequest: PullRequestShape; +} + +export const SwitchToBranchCommandType = new IpcCommandType('focus/pr/switchToBranch'); + +// Notifications + export interface DidChangeStateNotificationParams { state: State; } diff --git a/src/webviews/apps/plus/focus/components/pull-request-row.ts b/src/webviews/apps/plus/focus/components/pull-request-row.ts index fc25ff8..67923dd 100644 --- a/src/webviews/apps/plus/focus/components/pull-request-row.ts +++ b/src/webviews/apps/plus/focus/components/pull-request-row.ts @@ -100,8 +100,19 @@ const template = html` -${x => x.pullRequest!.deletions} - + @@ -295,4 +306,12 @@ export class PullRequestRow extends FASTElement { return assignees; } + + onOpenWorktreeClick(_e: Event) { + this.$emit('open-worktree', this.pullRequest!); + } + + onSwitchBranchClick(_e: Event) { + this.$emit('switch-branch', this.pullRequest!); + } } diff --git a/src/webviews/apps/plus/focus/focus.ts b/src/webviews/apps/plus/focus/focus.ts index d86e10f..69e440d 100644 --- a/src/webviews/apps/plus/focus/focus.ts +++ b/src/webviews/apps/plus/focus/focus.ts @@ -1,8 +1,11 @@ import { provideVSCodeDesignSystem, vsCodeButton } from '@vscode/webview-ui-toolkit'; +import type { PullRequestShape } from '../../../../git/models/pullRequest'; import type { State } from '../../../../plus/webviews/focus/protocol'; import { DidChangeStateNotificationType, DidChangeSubscriptionNotificationType, + OpenWorktreeCommandType, + SwitchToBranchCommandType, } from '../../../../plus/webviews/focus/protocol'; import type { IpcMessage } from '../../../protocol'; import { ExecuteCommandType, onIpc } from '../../../protocol'; @@ -65,10 +68,30 @@ export class FocusApp extends App { this.onPlusActionClicked(e, target), ), ); + disposables.push( + DOM.on('pull-request-row', 'open-worktree', (e, target: HTMLElement) => + this.onOpenWorktree(e, target), + ), + ); + disposables.push( + DOM.on('pull-request-row', 'switch-branch', (e, target: HTMLElement) => + this.onSwitchBranch(e, target), + ), + ); return disposables; } + private onSwitchBranch(e: CustomEvent, _target: HTMLElement) { + if (e.detail?.refs?.head == null) return; + this.sendCommand(SwitchToBranchCommandType, { pullRequest: e.detail }); + } + + private onOpenWorktree(e: CustomEvent, _target: HTMLElement) { + if (e.detail?.refs?.head == null) return; + this.sendCommand(OpenWorktreeCommandType, { pullRequest: e.detail }); + } + private onDataActionClicked(_e: MouseEvent, target: HTMLElement) { const action = target.dataset.action; this.onActionClickedCore(action); diff --git a/src/webviews/apps/shared/components/code-icon.ts b/src/webviews/apps/shared/components/code-icon.ts index 3608aee..7edf875 100644 --- a/src/webviews/apps/shared/components/code-icon.ts +++ b/src/webviews/apps/shared/components/code-icon.ts @@ -1490,25 +1490,30 @@ const styles = css` :host([icon='target']):before { content: '\\ebf8'; } - :host([icon='gl-pinned-filled']):before { + :host([icon^='gl-']) { font-family: 'glicons'; + } + :host([icon='gl-pinned-filled']):before { content: '\\f11c'; /* TODO: see relative positioning needed in every use-case */ position: relative; left: 1px; } :host([icon='gl-graph']):before { - font-family: 'glicons'; content: '\\f102'; } :host([icon='gl-list-auto']):before { - font-family: 'glicons'; content: '\\f11a'; } :host([icon='gl-clock']):before { - font-family: 'glicons'; content: '\\f11d'; } + :host([icon='gl-worktrees-view']):before { + content: '\\f112'; + } + :host([icon='gl-switch']):before { + content: '\\f118'; + } @keyframes codicon-spin { 100% {