2024-04-07 17:49:39 +08:00
|
|
|
|
package tui
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"gitee_cli/config"
|
|
|
|
|
"gitee_cli/internal/api/enterprises"
|
|
|
|
|
"gitee_cli/internal/api/issue"
|
2024-04-15 23:53:52 +08:00
|
|
|
|
"gitee_cli/internal/api/issue_state"
|
2024-04-07 17:49:39 +08:00
|
|
|
|
"gitee_cli/internal/api/pull_request"
|
|
|
|
|
"gitee_cli/utils/git_utils"
|
2024-04-15 23:53:52 +08:00
|
|
|
|
"gitee_cli/utils/tui/selector_tui"
|
2024-04-07 17:49:39 +08:00
|
|
|
|
"github.com/atotto/clipboard"
|
|
|
|
|
"github.com/charmbracelet/bubbles/key"
|
|
|
|
|
"github.com/charmbracelet/bubbles/table"
|
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
|
"github.com/fatih/color"
|
|
|
|
|
"github.com/pkg/browser"
|
|
|
|
|
"os"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var baseStyle = lipgloss.NewStyle().
|
|
|
|
|
BorderStyle(lipgloss.RoundedBorder()).
|
|
|
|
|
BorderForeground(lipgloss.Color("240"))
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
Issue = iota
|
|
|
|
|
IssueType
|
|
|
|
|
PullRequest
|
|
|
|
|
Enterprise
|
|
|
|
|
SSHKey
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Table TODO 初始化改成 options 模式
|
|
|
|
|
type Table struct {
|
|
|
|
|
table table.Model
|
|
|
|
|
SelectedKey string
|
|
|
|
|
ViewMode bool
|
|
|
|
|
ResourceType int
|
|
|
|
|
Enterprise enterprises.Enterprise
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewTableModel(enterprise enterprises.Enterprise, resourceType int, columns []table.Column, rows []table.Row) Table {
|
|
|
|
|
|
|
|
|
|
height := 5
|
|
|
|
|
if resourceType == PullRequest {
|
|
|
|
|
height = 10
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t := table.New(
|
|
|
|
|
table.WithColumns(columns),
|
|
|
|
|
table.WithRows(rows),
|
|
|
|
|
table.WithFocused(true),
|
|
|
|
|
table.WithHeight(height),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
s := table.DefaultStyles()
|
|
|
|
|
s.Header = s.Header.
|
|
|
|
|
BorderStyle(lipgloss.NormalBorder()).
|
|
|
|
|
BorderForeground(lipgloss.Color("240")).
|
|
|
|
|
BorderBottom(true).
|
|
|
|
|
Bold(false)
|
|
|
|
|
s.Selected = s.Selected.
|
|
|
|
|
Foreground(lipgloss.Color("229")).
|
|
|
|
|
Background(lipgloss.Color("57")).
|
|
|
|
|
Bold(false)
|
|
|
|
|
if resourceType == PullRequest {
|
|
|
|
|
s.Selected = s.Selected.Background(lipgloss.Color("#8B4789"))
|
|
|
|
|
// 避免与 diff 快捷键冲突
|
|
|
|
|
t.KeyMap.HalfPageDown = key.NewBinding(
|
|
|
|
|
key.WithDisabled(),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
t.SetStyles(s)
|
|
|
|
|
return Table{table: t, Enterprise: enterprise, ResourceType: resourceType}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewTable(enterprise enterprises.Enterprise, resourceType int, columns []table.Column, rows []table.Row) *tea.Program {
|
|
|
|
|
if resourceType == SSHKey || resourceType == Enterprise {
|
|
|
|
|
return tea.NewProgram(NewTableModel(enterprise, resourceType, columns, rows), tea.WithAltScreen())
|
|
|
|
|
}
|
|
|
|
|
return tea.NewProgram(NewTableModel(enterprise, resourceType, columns, rows))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t Table) Init() tea.Cmd { return nil }
|
|
|
|
|
|
|
|
|
|
func (t Table) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
|
|
var cmd tea.Cmd
|
|
|
|
|
switch msg := msg.(type) {
|
|
|
|
|
case tea.KeyMsg:
|
|
|
|
|
switch msg.String() {
|
|
|
|
|
case "esc":
|
|
|
|
|
if t.table.Focused() {
|
|
|
|
|
t.table.Blur()
|
|
|
|
|
} else {
|
|
|
|
|
t.table.Focus()
|
|
|
|
|
}
|
|
|
|
|
case "c":
|
|
|
|
|
if t.ResourceType == Issue || t.ResourceType == Enterprise {
|
|
|
|
|
clipboard.WriteAll(t.table.SelectedRow()[0])
|
|
|
|
|
} else if t.ResourceType == PullRequest {
|
|
|
|
|
clipboard.WriteAll(t.table.SelectedRow()[1])
|
|
|
|
|
}
|
|
|
|
|
case "q", "ctrl+c":
|
|
|
|
|
t.SelectedKey = ""
|
|
|
|
|
return t, tea.Quit
|
2024-04-15 23:53:52 +08:00
|
|
|
|
case "s":
|
|
|
|
|
if t.ResourceType == Issue {
|
|
|
|
|
targetIssue, err := issue.Detail(t.Enterprise.Id, t.table.SelectedRow()[0])
|
|
|
|
|
if err != nil {
|
|
|
|
|
color.Red("获取任务详情失败!")
|
|
|
|
|
return t, tea.Quit
|
|
|
|
|
}
|
|
|
|
|
if issueStates, err := issue_state.ListWithIssue(t.Enterprise.Id, targetIssue.Id); err == nil {
|
|
|
|
|
var model tea.Model
|
|
|
|
|
options := make([]string, 0)
|
|
|
|
|
optionMap := make(map[string]int, 0)
|
|
|
|
|
optionMap, options = issue_state.FillOptions(issueStates, optionMap, options)
|
|
|
|
|
promote := "请选择要变更的状态"
|
|
|
|
|
selector := selector_tui.NewMapSelector(optionMap, options, promote, true)
|
|
|
|
|
if model, err = selector.Run(); err != nil {
|
|
|
|
|
color.Red("任务状态选择器加载失败!")
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
mapSelector, _ := model.(selector_tui.MapSelector)
|
|
|
|
|
|
|
|
|
|
issueStateId, err := mapSelector.SelectedValue()
|
|
|
|
|
if err != nil {
|
|
|
|
|
color.Red(err.Error())
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
if targetIssue, err = issue.Update(t.Enterprise.Id, targetIssue.Id, map[string]interface{}{
|
|
|
|
|
"issue_state_id": issueStateId,
|
|
|
|
|
}); err != nil {
|
|
|
|
|
color.Red(err.Error())
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
return t, tea.ClearScrollArea
|
|
|
|
|
} else {
|
|
|
|
|
color.Red("获取任务状态列表失败!")
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-07 17:49:39 +08:00
|
|
|
|
case "v":
|
|
|
|
|
if t.ResourceType == Issue {
|
|
|
|
|
if _issue, err := issue.Detail(t.Enterprise.Id, t.table.SelectedRow()[0]); err == nil {
|
|
|
|
|
NewPager(_issue.Title, _issue.Description, Markdown).Run()
|
|
|
|
|
} else {
|
|
|
|
|
color.Red("获取任务详情失败!")
|
|
|
|
|
return t, tea.Quit
|
|
|
|
|
}
|
|
|
|
|
} else if t.ResourceType == PullRequest {
|
|
|
|
|
path, _ := git_utils.ParseCurrentRepo()
|
|
|
|
|
if path == "" {
|
|
|
|
|
path = config.Conf.DefaultPathWithNamespace
|
|
|
|
|
}
|
|
|
|
|
t.SelectedKey = t.table.SelectedRow()[1]
|
|
|
|
|
if pullRequerst, err := pull_request.Detail(t.SelectedKey, path); err == nil {
|
|
|
|
|
NewPager(pullRequerst.Title, pullRequerst.Body, Markdown).Run()
|
|
|
|
|
} else {
|
|
|
|
|
color.Red("获取pr详情失败!")
|
|
|
|
|
return t, tea.Quit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "d":
|
|
|
|
|
if t.ResourceType == PullRequest {
|
|
|
|
|
path, _ := git_utils.ParseCurrentRepo()
|
|
|
|
|
t.SelectedKey = t.table.SelectedRow()[1]
|
|
|
|
|
if path == "" {
|
|
|
|
|
path = config.Conf.DefaultPathWithNamespace
|
|
|
|
|
}
|
|
|
|
|
if diff, err := pull_request.FetchPatchContent(t.SelectedKey, path); err == nil {
|
|
|
|
|
NewPager(t.table.SelectedRow()[0], diff, Diff).Run()
|
|
|
|
|
} else {
|
|
|
|
|
color.Red(err.Error())
|
|
|
|
|
return t, tea.Quit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "enter":
|
|
|
|
|
t.SelectedKey = t.table.SelectedRow()[0]
|
|
|
|
|
if t.ResourceType == Issue {
|
|
|
|
|
url := fmt.Sprintf("https://e.gitee.com/%s/dashboard?issue=%s", t.Enterprise.Path, t.SelectedKey)
|
|
|
|
|
browser.OpenURL(url)
|
|
|
|
|
} else if t.ResourceType == PullRequest {
|
|
|
|
|
path, _ := git_utils.ParseCurrentRepo()
|
|
|
|
|
if path == "" {
|
|
|
|
|
path = config.Conf.DefaultPathWithNamespace
|
|
|
|
|
}
|
|
|
|
|
t.SelectedKey = t.table.SelectedRow()[1]
|
|
|
|
|
url := fmt.Sprintf("https://gitee.com/%s/pulls/%s", path, t.SelectedKey)
|
|
|
|
|
if os.Getenv("CONVERT_ENT_URL") != "" {
|
|
|
|
|
url = fmt.Sprintf("https://e.gitee.com/%s/repos/%s/pulls/%s", strings.Split(path, "/")[0], path, t.SelectedKey)
|
|
|
|
|
}
|
|
|
|
|
browser.OpenURL(url)
|
|
|
|
|
} else {
|
|
|
|
|
return t, tea.Quit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
t.table, cmd = t.table.Update(msg)
|
|
|
|
|
return t, cmd
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t Table) View() string {
|
|
|
|
|
return baseStyle.Render(t.table.View()) + "\n"
|
|
|
|
|
}
|