244 lines
6.3 KiB
Go
244 lines
6.3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"code.gitea.io/sdk/gitea"
|
|
"github.com/charmbracelet/bubbles/key"
|
|
"github.com/charmbracelet/bubbles/list"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
// Activity stores an entry in a gitea user's activity feed.
|
|
type Activity struct {
|
|
act_user *gitea.User
|
|
act_user_id int64
|
|
comment gitea.Comment
|
|
comment_id int64
|
|
content string
|
|
created string
|
|
id int64
|
|
is_private bool
|
|
op_type string
|
|
ref_name string
|
|
repo *gitea.Repository
|
|
repo_id int64
|
|
user_id int64
|
|
}
|
|
|
|
// getActivityFeed returns the authenticated user's activity feed.
|
|
func getActivityFeed() []Activity {
|
|
feed := []Activity{}
|
|
for _, server := range config.servers {
|
|
client := Servers[server.servername]
|
|
resp, err := http.Get(fmt.Sprintf("%s/api/v1/users/%s/activities/feeds?limit=15&page=1",
|
|
server.url,
|
|
server.username,
|
|
))
|
|
if err != nil {
|
|
println("Failed to make HTTP request")
|
|
continue
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
var data []map[string]interface{}
|
|
err = json.NewDecoder(resp.Body).Decode(&data)
|
|
if err != nil {
|
|
fmt.Println("Error decoding JSON: "+fmt.Sprint(err))
|
|
continue
|
|
}
|
|
for _, obj := range data {
|
|
act_user, _, err := client.GetUserInfo(obj["act_user"].(map[string]interface{})["login"].(string))
|
|
if err != nil {
|
|
fmt.Println("Error getting user: "+fmt.Sprint(err))
|
|
continue
|
|
}
|
|
var comment gitea.Comment
|
|
if obj["comment"] != nil {
|
|
raw_comment := obj["comment"].(map[string]interface{})
|
|
comment = gitea.Comment{
|
|
Body: raw_comment["body"].(string),
|
|
HTMLURL: raw_comment["html_url"].(string),
|
|
ID: int64(raw_comment["id"].(float64)),
|
|
IssueURL: raw_comment["issue_url"].(string),
|
|
OriginalAuthor: raw_comment["original_author"].(string),
|
|
OriginalAuthorID: int64(raw_comment["original_author_id"].(float64)),
|
|
}
|
|
}
|
|
feed = append(feed, Activity{
|
|
// repo: repo,
|
|
act_user: act_user,
|
|
act_user_id: int64(obj["act_user_id"].(float64)),
|
|
comment: comment,
|
|
comment_id: int64(obj["comment_id"].(float64)),
|
|
content: obj["content"].(string),
|
|
created: obj["created"].(string),
|
|
id: int64(obj["id"].(float64)),
|
|
is_private: obj["is_private"].(bool),
|
|
op_type: obj["op_type"].(string),
|
|
ref_name: obj["ref_name"].(string),
|
|
repo_id: int64(obj["repo_id"].(float64)),
|
|
user_id: int64(obj["user_id"].(float64)),
|
|
})
|
|
}
|
|
}
|
|
return feed
|
|
}
|
|
|
|
type listKeyMap struct {
|
|
toggleSpinner key.Binding
|
|
toggleTitleBar key.Binding
|
|
toggleStatusBar key.Binding
|
|
togglePagination key.Binding
|
|
toggleHelpMenu key.Binding
|
|
}
|
|
|
|
func newListKeyMap() *listKeyMap {
|
|
return &listKeyMap{
|
|
toggleSpinner: key.NewBinding(
|
|
key.WithKeys("s"),
|
|
key.WithHelp("s", "toggle spinner"),
|
|
),
|
|
toggleTitleBar: key.NewBinding(
|
|
key.WithKeys("T"),
|
|
key.WithHelp("T", "toggle title"),
|
|
),
|
|
toggleStatusBar: key.NewBinding(
|
|
key.WithKeys("S"),
|
|
key.WithHelp("S", "toggle status"),
|
|
),
|
|
togglePagination: key.NewBinding(
|
|
key.WithKeys("P"),
|
|
key.WithHelp("P", "toggle pagination"),
|
|
),
|
|
toggleHelpMenu: key.NewBinding(
|
|
key.WithKeys("H"),
|
|
key.WithHelp("H", "toggle help"),
|
|
),
|
|
}
|
|
}
|
|
|
|
type feedviewer struct {
|
|
list list.Model
|
|
feed []Activity
|
|
appStyle lipgloss.Style
|
|
keys listKeyMap
|
|
}
|
|
|
|
func (m feedviewer) Init() tea.Cmd {
|
|
m.appStyle = lipgloss.NewStyle().Padding(1, 2)
|
|
return nil
|
|
}
|
|
|
|
func (m feedviewer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.WindowSizeMsg:
|
|
h, v := m.appStyle.GetFrameSize()
|
|
m.list.SetSize(msg.Width-h, msg.Height-v)
|
|
case tea.KeyMsg:
|
|
if m.list.FilterState() == list.Filtering {
|
|
break
|
|
}
|
|
switch {
|
|
case key.Matches(msg, m.keys.toggleSpinner):
|
|
return m, m.list.ToggleSpinner()
|
|
case key.Matches(msg, m.keys.toggleTitleBar):
|
|
v := !m.list.ShowTitle()
|
|
m.list.SetShowTitle(v)
|
|
m.list.SetShowFilter(v)
|
|
m.list.SetFilteringEnabled(v)
|
|
return m, nil
|
|
case key.Matches(msg, m.keys.toggleStatusBar):
|
|
m.list.SetShowStatusBar(!m.list.ShowStatusBar())
|
|
return m, nil
|
|
case key.Matches(msg, m.keys.togglePagination):
|
|
m.list.SetShowPagination(!m.list.ShowPagination())
|
|
return m, nil
|
|
case key.Matches(msg, m.keys.toggleHelpMenu):
|
|
m.list.SetShowHelp(!m.list.ShowHelp())
|
|
return m, nil
|
|
default:
|
|
switch msg.String() {
|
|
case "ctrl+c":
|
|
return m, tea.Quit
|
|
}
|
|
}
|
|
}
|
|
newListModel, cmd := m.list.Update(msg)
|
|
m.list = newListModel
|
|
return m, tea.Batch(cmd)
|
|
}
|
|
|
|
func (m feedviewer) View() string {
|
|
return m.appStyle.Render(m.list.View())
|
|
}
|
|
|
|
type item struct {
|
|
Activity
|
|
}
|
|
|
|
func (i item) Title() string {
|
|
switch i.Activity.op_type {
|
|
case "create_issue":
|
|
return fmt.Sprintf("%s created an issue", i.act_user.UserName)
|
|
case "commit_repo":
|
|
var data map[string]interface{}
|
|
if err := json.NewDecoder(strings.NewReader(i.content)).Decode(&data); err != nil {
|
|
return "JSON decode error: "+fmt.Sprint(err)
|
|
}
|
|
commits := data["Commits"].([]interface{})
|
|
commits_text := fmt.Sprintf("%d commit", len(commits))
|
|
if len(commits) > 1 {
|
|
commits_text += "s"
|
|
}
|
|
return fmt.Sprintf("%s pushed %s to %s", i.act_user.UserName, commits_text, i.ref_name)
|
|
case "comment_issue":
|
|
split := strings.Split(i.content, "|")
|
|
issue_num := split[0]
|
|
// comment := strings.TrimPrefix(i.content, issue_num+"|")
|
|
return fmt.Sprintf("%s commented on #%s", i.act_user.UserName, issue_num)
|
|
default:
|
|
return fmt.Sprintf("%s performed an unknown action. (%s)", i.act_user.UserName, i.op_type)
|
|
}
|
|
}
|
|
func (i item) Description() string {
|
|
switch i.op_type {
|
|
case "commit_repo":
|
|
var data map[string]interface{}
|
|
if err := json.NewDecoder(strings.NewReader(i.content)).Decode(&data); err != nil {
|
|
return "JSON decode error: "+fmt.Sprint(err)
|
|
}
|
|
s := ""
|
|
commits := data["Commits"].([]interface{})
|
|
commit := commits[0].(map[string]interface{})
|
|
s += styles.text.Render(fmt.Sprintf("[%s] ", commit["Sha1"].(string)[0:10])) +
|
|
commit["Message"].(string)
|
|
return s
|
|
default: return ""
|
|
}
|
|
|
|
}
|
|
func (i item) FilterValue() string { return "" }
|
|
|
|
func feed() {
|
|
feed := getActivityFeed()
|
|
items := []list.Item{}
|
|
|
|
for _, activity := range feed {
|
|
items = append(items, item{
|
|
activity,
|
|
})
|
|
}
|
|
|
|
p := feedviewer{}
|
|
p.list = list.New(items, list.NewDefaultDelegate(), 0, 0)
|
|
// Setup list
|
|
if _, err := tea.NewProgram(p, tea.WithAltScreen()).Run(); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|