logitem.go

  1package repo
  2
  3import (
  4	"fmt"
  5	"io"
  6	"strings"
  7	"time"
  8
  9	"github.com/charmbracelet/bubbles/key"
 10	"github.com/charmbracelet/bubbles/list"
 11	tea "github.com/charmbracelet/bubbletea"
 12	"github.com/charmbracelet/lipgloss"
 13	"github.com/charmbracelet/soft-serve/git"
 14	"github.com/charmbracelet/soft-serve/ui/common"
 15)
 16
 17// LogItem is a item in the log list that displays a git commit.
 18type LogItem struct {
 19	*git.Commit
 20	copied time.Time
 21}
 22
 23// ID implements selector.IdentifiableItem.
 24func (i LogItem) ID() string {
 25	return i.Hash()
 26}
 27
 28func (i LogItem) Hash() string {
 29	return i.Commit.ID.String()
 30}
 31
 32// Title returns the item title. Implements list.DefaultItem.
 33func (i LogItem) Title() string {
 34	if i.Commit != nil {
 35		return strings.Split(i.Commit.Message, "\n")[0]
 36	}
 37	return ""
 38}
 39
 40// Description returns the item description. Implements list.DefaultItem.
 41func (i LogItem) Description() string { return "" }
 42
 43// FilterValue implements list.Item.
 44func (i LogItem) FilterValue() string { return i.Title() }
 45
 46// LogItemDelegate is the delegate for LogItem.
 47type LogItemDelegate struct {
 48	common *common.Common
 49}
 50
 51// Height returns the item height. Implements list.ItemDelegate.
 52func (d LogItemDelegate) Height() int { return 2 }
 53
 54// Spacing returns the item spacing. Implements list.ItemDelegate.
 55func (d LogItemDelegate) Spacing() int { return 1 }
 56
 57// Update updates the item. Implements list.ItemDelegate.
 58func (d LogItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 59	idx := m.Index()
 60	item, ok := m.SelectedItem().(LogItem)
 61	if !ok {
 62		return nil
 63	}
 64	switch msg := msg.(type) {
 65	case tea.KeyMsg:
 66		switch {
 67		case key.Matches(msg, d.common.KeyMap.Copy):
 68			item.copied = time.Now()
 69			d.common.Copy.Copy(item.Hash())
 70			return m.SetItem(idx, item)
 71		}
 72	}
 73	return nil
 74}
 75
 76var (
 77	highlight = lipgloss.NewStyle().Foreground(lipgloss.Color("#F1F1F1"))
 78)
 79
 80// Render renders the item. Implements list.ItemDelegate.
 81func (d LogItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
 82	styles := d.common.Styles
 83	i, ok := listItem.(LogItem)
 84	if !ok {
 85		return
 86	}
 87	if i.Commit == nil {
 88		return
 89	}
 90
 91	width := lipgloss.Width
 92	titleStyle := styles.LogItemTitle.Copy()
 93	style := styles.LogItemInactive
 94	if index == m.Index() {
 95		titleStyle.Bold(true)
 96		style = styles.LogItemActive
 97	}
 98	hash := " " + i.Commit.ID.String()[:7]
 99	if !i.copied.IsZero() && i.copied.Add(time.Second).After(time.Now()) {
100		hash = "copied"
101	}
102	title := titleStyle.Render(
103		common.TruncateString(i.Title(), m.Width()-style.GetHorizontalFrameSize()-width(hash)-2),
104	)
105	hashStyle := styles.LogItemHash.Copy().
106		Align(lipgloss.Right).
107		Width(m.Width() -
108			style.GetHorizontalFrameSize() -
109			width(title) -
110			// FIXME where this "1" is coming from?
111			1)
112	if index == m.Index() {
113		hashStyle = hashStyle.Bold(true)
114	}
115	hash = hashStyle.Render(hash)
116	author := highlight.Render(i.Author.Name)
117	commiter := highlight.Render(i.Committer.Name)
118	who := ""
119	if author != "" && commiter != "" {
120		who = fmt.Sprintf("%s committed", commiter)
121		if author != commiter {
122			who = fmt.Sprintf("%s authored and %s", author, who)
123		}
124		who += " "
125	}
126	date := fmt.Sprintf("on %s", i.Committer.When.Format("Feb 02"))
127	if i.Committer.When.Year() != time.Now().Year() {
128		date += fmt.Sprintf(" %d", i.Committer.When.Year())
129	}
130	who += date
131	who = common.TruncateString(who, m.Width()-style.GetHorizontalFrameSize())
132	fmt.Fprint(w,
133		style.Render(
134			lipgloss.JoinVertical(lipgloss.Top,
135				lipgloss.JoinHorizontal(lipgloss.Left,
136					title,
137					hash,
138				),
139				who,
140			),
141		),
142	)
143}