Files
orca/src/main/ipc/github-issue-mutation-handlers.ts
T
NeilandKatsuma Takehisa ce07786266 Keep opened issue details and edits in the selected repository (#24729)
Keeps a fork issue’s details, metadata and edits bound to the repository the user opened, including same-number issues in fork and upstream. Repairs selected-assignee leakage and delayed failed edits repainting another issue.

Fixes #24378

Incorporates and cross-reviews contributor PR #24379, including its source-resolver correction and regression material. Covers the contributor PR’s Project-row identity and retained-dialog mutation findings. The final published head passes focused tests, hidden macOS rendering and current CI; the callback-timing bot thread has an evidence-based response.

Co-authored-by: Katsuma Takehisa <k.takehisa@nissogr.com>
2026-10-03 23:19:55 -07:00

141 lines
4.4 KiB
TypeScript

import { ipcMain } from 'electron'
import type { GitHubOwnerRepo } from '../../shared/github/pull-request-types'
import type { GitHubIssueUpdate } from '../../shared/issue-mutation-types'
import type { TaskSourceContext } from '../../shared/task-source-context'
import { addIssueComment, listAssignableUsers, listLabels, updateIssue } from '../github/client'
import type { Store } from '../persistence'
import {
assertRegisteredGitHubRepo,
getGitHubLocalGitOptionArgs,
getGitHubRepoConnectionId,
type GitHubRepoScopedArgs
} from './github-repo-routing'
import { broadcastGitHubWorkItemMutation } from './github-work-item-mutation-events'
export function registerGitHubIssueMutationHandlers(store: Store): void {
ipcMain.handle(
'gh:updateIssue',
async (
event,
args: GitHubRepoScopedArgs & {
number: number
updates: GitHubIssueUpdate
ownerRepo?: GitHubOwnerRepo
}
) => {
const repo = assertRegisteredGitHubRepo(args, store)
if (typeof args.number !== 'number' || !Number.isInteger(args.number) || args.number < 1) {
return { ok: false, error: 'Invalid issue number' }
}
if (!args.updates || typeof args.updates !== 'object') {
return { ok: false, error: 'Updates object is required' }
}
const result = await updateIssue(
repo.path,
args.number,
args.updates,
getGitHubRepoConnectionId(repo),
getGitHubLocalGitOptionArgs(store, repo)[0],
repo.issueSourcePreference,
...(args.ownerRepo ? [args.ownerRepo] : [])
)
if (result.ok) {
broadcastGitHubWorkItemMutation(
{ repoPath: repo.path, repoId: repo.id, type: 'issue', number: args.number },
event.sender.id
)
}
return result
}
)
ipcMain.handle(
'gh:addIssueComment',
async (
event,
args: {
repoPath: string
repoId?: string
sourceContext?: TaskSourceContext | null
number: number
body: string
type?: 'issue' | 'pr'
prRepo?: GitHubOwnerRepo | null
}
) => {
const repo = assertRegisteredGitHubRepo(args, store)
if (typeof args.number !== 'number' || !Number.isInteger(args.number) || args.number < 1) {
return { ok: false, error: 'Invalid issue number' }
}
if (!args.body?.trim()) {
return { ok: false, error: 'Comment body required' }
}
const result = await addIssueComment(
repo.path,
args.number,
args.body.trim(),
getGitHubRepoConnectionId(repo),
args.prRepo ?? null,
getGitHubLocalGitOptionArgs(store, repo)[0],
// Why: the issue source selector only scopes issues; PR comments keep their resolution.
args.type === 'pr' ? undefined : repo.issueSourcePreference
)
if (result.ok) {
broadcastGitHubWorkItemMutation(
{
repoPath: repo.path,
repoId: repo.id,
type: args.type ?? 'issue',
number: args.number
},
event.sender.id
)
}
return result
}
)
ipcMain.handle(
'gh:listLabels',
(_event, args: GitHubRepoScopedArgs & { ownerRepo?: GitHubOwnerRepo }) => {
const repo = assertRegisteredGitHubRepo(args, store)
const localGitOptions = getGitHubLocalGitOptionArgs(store, repo)
const connectionId = getGitHubRepoConnectionId(repo)
if (args.ownerRepo) {
return listLabels(
repo.path,
repo.issueSourcePreference,
connectionId,
localGitOptions[0],
args.ownerRepo
)
}
return listLabels(repo.path, repo.issueSourcePreference, connectionId, ...localGitOptions)
}
)
ipcMain.handle(
'gh:listAssignableUsers',
(_event, args: GitHubRepoScopedArgs & { ownerRepo?: GitHubOwnerRepo }) => {
const repo = assertRegisteredGitHubRepo(args, store)
const localGitOptions = getGitHubLocalGitOptionArgs(store, repo)
const connectionId = getGitHubRepoConnectionId(repo)
if (args.ownerRepo) {
return listAssignableUsers(
repo.path,
repo.issueSourcePreference,
connectionId,
localGitOptions[0],
args.ownerRepo
)
}
return listAssignableUsers(
repo.path,
repo.issueSourcePreference,
connectionId,
...localGitOptions
)
}
)
}