// Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package interact import ( "slices" "code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/task" "github.com/AlecAivazis/survey/v2" ) // EditIssue interactively edits an issue func EditIssue(ctx context.TeaContext, index int64) (*task.EditIssueOption, error) { var opts = task.EditIssueOption{} var err error ctx.Owner, ctx.Repo, err = promptRepoSlug(ctx.Owner, ctx.Repo) if err != nil { return &opts, err } c := ctx.Login.Client() i, _, err := c.GetIssue(ctx.Owner, ctx.Repo, index) if err != nil { return &opts, err } opts = task.EditIssueOption{ Index: index, Title: &i.Title, Body: &i.Body, Deadline: i.Deadline, } if len(i.Assignees) != 0 { for _, a := range i.Assignees { opts.AddAssignees = append(opts.AddAssignees, a.UserName) } } if len(i.Labels) != 0 { for _, l := range i.Labels { opts.AddLabels = append(opts.AddLabels, l.Name) } } if i.Milestone != nil { opts.Milestone = &i.Milestone.Title } if err := promptIssueEditProperties(&ctx, &opts); err != nil { return &opts, err } return &opts, err } func promptIssueEditProperties(ctx *context.TeaContext, o *task.EditIssueOption) error { var milestoneName string var labelsSelected []string var err error selectableChan := make(chan (issueSelectables), 1) go fetchIssueSelectables(ctx.Login, ctx.Owner, ctx.Repo, selectableChan) // title promptOpts := survey.WithValidator(survey.Required) promptI := &survey.Input{Message: "Issue title:", Default: *o.Title} if err = survey.AskOne(promptI, o.Title, promptOpts); err != nil { return err } // description promptD := NewMultiline(Multiline{ Message: "Issue description:", Default: *o.Body, Syntax: "md", UseEditor: config.GetPreferences().Editor, EditorAppendDefault: true, EditorHideDefault: true, }) if err = survey.AskOne(promptD, o.Body); err != nil { return err } // wait until selectables are fetched selectables := <-selectableChan if selectables.Err != nil { return selectables.Err } // skip remaining props if we don't have permission to set them if !selectables.Repo.Permissions.Push { return nil } currAssignees := o.AddAssignees newAssignees := selectables.Assignees for _, c := range currAssignees { if i := slices.Index(newAssignees, c); i != -1 { newAssignees = slices.Delete(newAssignees, i, i+1) } } // assignees if o.AddAssignees, err = promptMultiSelect("Add Assignees:", newAssignees, "[other]"); err != nil { return err } // milestone if len(selectables.MilestoneList) != 0 { var defaultMS string if o.Milestone != nil { defaultMS = *o.Milestone } if milestoneName, err = promptSelect("Milestone:", selectables.MilestoneList, "", "[none]", defaultMS); err != nil { return err } o.Milestone = &milestoneName } // labels if len(selectables.LabelList) != 0 { promptL := &survey.MultiSelect{Message: "Labels:", Options: selectables.LabelList, VimMode: true, Default: o.AddLabels} if err := survey.AskOne(promptL, &labelsSelected); err != nil { return err } // removed labels for _, l := range o.AddLabels { if !slices.Contains(labelsSelected, l) { o.RemoveLabels = append(o.RemoveLabels, l) } } // added labels o.AddLabels = make([]string, len(labelsSelected)) for i, l := range labelsSelected { o.AddLabels[i] = l } } // deadline if o.Deadline, err = promptDatetime("Due date:"); err != nil { return err } return nil }