cli/pkg/cmd/pr/shared/commentable.go
Mislav Marohnić a70b69e359
Bring the "Press Enter" UI closer to the authentication experience
- "Press Enter" is both bold
- "Enter" is capitalized
- The prompt ends with "..."
2021-01-21 09:47:38 -08:00

168 lines
4.2 KiB
Go

package shared
import (
"bufio"
"errors"
"fmt"
"io"
"net/http"
"github.com/AlecAivazis/survey/v2"
"github.com/cli/cli/api"
"github.com/cli/cli/internal/config"
"github.com/cli/cli/internal/ghrepo"
"github.com/cli/cli/pkg/cmdutil"
"github.com/cli/cli/pkg/iostreams"
"github.com/cli/cli/pkg/surveyext"
"github.com/cli/cli/utils"
"github.com/spf13/cobra"
)
type InputType int
const (
InputTypeEditor InputType = iota
InputTypeInline
InputTypeWeb
)
type Commentable interface {
Link() string
Identifier() string
}
type CommentableOptions struct {
IO *iostreams.IOStreams
HttpClient func() (*http.Client, error)
RetrieveCommentable func() (Commentable, ghrepo.Interface, error)
EditSurvey func() (string, error)
InteractiveEditSurvey func() (string, error)
ConfirmSubmitSurvey func() (bool, error)
OpenInBrowser func(string) error
Interactive bool
InputType InputType
Body string
}
func CommentablePreRun(cmd *cobra.Command, opts *CommentableOptions) error {
inputFlags := 0
if cmd.Flags().Changed("body") {
opts.InputType = InputTypeInline
inputFlags++
}
if web, _ := cmd.Flags().GetBool("web"); web {
opts.InputType = InputTypeWeb
inputFlags++
}
if editor, _ := cmd.Flags().GetBool("editor"); editor {
opts.InputType = InputTypeEditor
inputFlags++
}
if inputFlags == 0 {
if !opts.IO.CanPrompt() {
return &cmdutil.FlagError{Err: errors.New("--body or --web required when not running interactively")}
}
opts.Interactive = true
} else if inputFlags == 1 {
if !opts.IO.CanPrompt() && opts.InputType == InputTypeEditor {
return &cmdutil.FlagError{Err: errors.New("--body or --web required when not running interactively")}
}
} else if inputFlags > 1 {
return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of --body, --editor, or --web")}
}
return nil
}
func CommentableRun(opts *CommentableOptions) error {
commentable, repo, err := opts.RetrieveCommentable()
if err != nil {
return err
}
switch opts.InputType {
case InputTypeWeb:
openURL := commentable.Link() + "#issuecomment-new"
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL))
}
return opts.OpenInBrowser(openURL)
case InputTypeEditor:
var body string
if opts.Interactive {
body, err = opts.InteractiveEditSurvey()
} else {
body, err = opts.EditSurvey()
}
if err != nil {
return err
}
opts.Body = body
}
if opts.Interactive {
cont, err := opts.ConfirmSubmitSurvey()
if err != nil {
return err
}
if !cont {
return errors.New("Discarding...")
}
}
httpClient, err := opts.HttpClient()
if err != nil {
return err
}
apiClient := api.NewClientFromHTTP(httpClient)
params := api.CommentCreateInput{Body: opts.Body, SubjectId: commentable.Identifier()}
url, err := api.CommentCreate(apiClient, repo.RepoHost(), params)
if err != nil {
return err
}
fmt.Fprintln(opts.IO.Out, url)
return nil
}
func CommentableConfirmSubmitSurvey() (bool, error) {
var confirm bool
submit := &survey.Confirm{
Message: "Submit?",
Default: true,
}
err := survey.AskOne(submit, &confirm)
return confirm, err
}
func CommentableInteractiveEditSurvey(cf func() (config.Config, error), io *iostreams.IOStreams) func() (string, error) {
return func() (string, error) {
editorCommand, err := cmdutil.DetermineEditor(cf)
if err != nil {
return "", err
}
if editorCommand == "" {
editorCommand = surveyext.DefaultEditorName()
}
cs := io.ColorScheme()
fmt.Fprintf(io.Out, "- %s to draft your comment in %s... ", cs.Bold("Press Enter"), cs.Bold(editorCommand))
_ = waitForEnter(io.In)
return surveyext.Edit(editorCommand, "*.md", "", io.In, io.Out, io.ErrOut, nil)
}
}
func CommentableEditSurvey(cf func() (config.Config, error), io *iostreams.IOStreams) func() (string, error) {
return func() (string, error) {
editorCommand, err := cmdutil.DetermineEditor(cf)
if err != nil {
return "", err
}
return surveyext.Edit(editorCommand, "*.md", "", io.In, io.Out, io.ErrOut, nil)
}
}
func waitForEnter(r io.Reader) error {
scanner := bufio.NewScanner(r)
scanner.Scan()
return scanner.Err()
}