mirror of
				https://gitea.com/gitea/tea.git
				synced 2025-10-31 01:05:26 +01:00 
			
		
		
		
	 4c00b8b571
			
		
	
	4c00b8b571
	
	
	
		
			
			Fix #772 Reviewed-on: https://gitea.com/gitea/tea/pulls/786 Reviewed-by: Bo-Yi Wu (吳柏毅) <appleboy.tw@gmail.com>
		
			
				
	
	
		
			177 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2025 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package interact
 | |
| 
 | |
| import (
 | |
| 	"slices"
 | |
| 	"strings"
 | |
| 
 | |
| 	"code.gitea.io/tea/modules/config"
 | |
| 	"code.gitea.io/tea/modules/context"
 | |
| 	"code.gitea.io/tea/modules/task"
 | |
| 	"code.gitea.io/tea/modules/theme"
 | |
| 
 | |
| 	"github.com/charmbracelet/huh"
 | |
| )
 | |
| 
 | |
| // EditIssue interactively edits an issue
 | |
| func EditIssue(ctx context.TeaContext, index int64) (*task.EditIssueOption, error) {
 | |
| 	opts := task.EditIssueOption{}
 | |
| 	var err error
 | |
| 
 | |
| 	ctx.Owner, ctx.Repo, err = promptRepoSlug(ctx.Owner, ctx.Repo)
 | |
| 	if err != nil {
 | |
| 		return &opts, err
 | |
| 	}
 | |
| 	printTitleAndContent("Target repo:", ctx.Owner+"/"+ctx.Repo)
 | |
| 
 | |
| 	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
 | |
| 	if err := huh.NewInput().
 | |
| 		Title("Issue title:").
 | |
| 		Value(o.Title).
 | |
| 		Validate(huh.ValidateNotEmpty()).
 | |
| 		WithTheme(theme.GetTheme()).
 | |
| 		Run(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	printTitleAndContent("Issue title:", *o.Title)
 | |
| 
 | |
| 	// description
 | |
| 	if err := huh.NewForm(
 | |
| 		huh.NewGroup(
 | |
| 			huh.NewText().
 | |
| 				Title("Issue description(markdown):").
 | |
| 				ExternalEditor(config.GetPreferences().Editor).
 | |
| 				EditorExtension("md").
 | |
| 				Value(o.Body),
 | |
| 		),
 | |
| 	).
 | |
| 		WithTheme(theme.GetTheme()).
 | |
| 		Run(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	printTitleAndContent("Issue description(markdown):", *o.Body)
 | |
| 
 | |
| 	// 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
 | |
| 	}
 | |
| 	printTitleAndContent("Assignees:", strings.Join(o.AddAssignees, "\n"))
 | |
| 
 | |
| 	// 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
 | |
| 		printTitleAndContent("Milestone:", milestoneName)
 | |
| 	}
 | |
| 
 | |
| 	// labels
 | |
| 	if len(selectables.LabelList) != 0 {
 | |
| 		copy(labelsSelected, o.AddLabels)
 | |
| 		if err := huh.NewMultiSelect[string]().
 | |
| 			Title("Labels:").
 | |
| 			Options(huh.NewOptions(selectables.LabelList...)...).
 | |
| 			Value(&labelsSelected).
 | |
| 			WithTheme(theme.GetTheme()).
 | |
| 			Run(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		printTitleAndContent("Labels:", strings.Join(labelsSelected, "\n"))
 | |
| 
 | |
| 		// 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
 | |
| 	}
 | |
| 	deadlineStr := "No due date"
 | |
| 	if o.Deadline != nil && !o.Deadline.IsZero() {
 | |
| 		deadlineStr = o.Deadline.Format("2006-01-02")
 | |
| 	}
 | |
| 	printTitleAndContent("Due date:", deadlineStr)
 | |
| 
 | |
| 	return nil
 | |
| }
 |