Skip to content

Commit

Permalink
feat(ui): notify copied text
Browse files Browse the repository at this point in the history
Fixes: #154
  • Loading branch information
aymanbagabas committed May 2, 2023
1 parent bdac20b commit 5302c49
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 69 deletions.
4 changes: 2 additions & 2 deletions ui/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ func TruncateString(s string, max int) string {
return truncate.StringWithTail(s, uint(max), "…")
}

// RepoURL returns the URL of the repository.
func RepoURL(publicURL, name string) string {
// CloneCmd returns the URL of the repository.
func CloneCmd(publicURL, name string) string {
name = utils.SanitizeRepo(name) + ".git"
url, err := url.Parse(publicURL)
if err == nil {
Expand Down
36 changes: 25 additions & 11 deletions ui/components/statusbar/statusbar.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@ import (

// StatusBarMsg is a message sent to the status bar.
type StatusBarMsg struct {
Key string
Value string
Info string
Branch string
Key string
Value string
Info string
Extra string
}

// StatusBar is a status bar model.
type StatusBar struct {
common common.Common
msg StatusBarMsg
key string
value string
info string
extra string
}

// Model is an interface that supports setting the status bar information.
Expand Down Expand Up @@ -50,7 +53,18 @@ func (s *StatusBar) Init() tea.Cmd {
func (s *StatusBar) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case StatusBarMsg:
s.msg = msg
if msg.Key != "" {
s.key = msg.Key
}
if msg.Value != "" {
s.value = msg.Value
}
if msg.Info != "" {
s.info = msg.Info
}
if msg.Extra != "" {
s.extra = msg.Extra
}
}
return s, nil
}
Expand All @@ -63,14 +77,14 @@ func (s *StatusBar) View() string {
"repo-help",
st.StatusBarHelp.Render("? Help"),
)
key := st.StatusBarKey.Render(s.msg.Key)
key := st.StatusBarKey.Render(s.key)
info := ""
if s.msg.Info != "" {
info = st.StatusBarInfo.Render(s.msg.Info)
if s.info != "" {
info = st.StatusBarInfo.Render(s.info)
}
branch := st.StatusBarBranch.Render(s.msg.Branch)
branch := st.StatusBarBranch.Render(s.extra)
maxWidth := s.common.Width - w(key) - w(info) - w(branch) - w(help)
v := truncate.StringWithTail(s.msg.Value, uint(maxWidth-st.StatusBarValue.GetHorizontalFrameSize()), "…")
v := truncate.StringWithTail(s.value, uint(maxWidth-st.StatusBarValue.GetHorizontalFrameSize()), "…")
value := st.StatusBarValue.
Width(maxWidth).
Render(v)
Expand Down
2 changes: 1 addition & 1 deletion ui/pages/repo/empty.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ git push -u origin main
git remote add origin %[1]s
git push -u origin main
`+"```"+`
`, common.RepoURL(cfg.SSH.PublicURL, repo))
`, common.CloneCmd(cfg.SSH.PublicURL, repo))
}
2 changes: 1 addition & 1 deletion ui/pages/repo/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func (f *Files) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case key.Matches(msg, f.common.KeyMap.BackItem):
cmds = append(cmds, backCmd)
case key.Matches(msg, f.common.KeyMap.Copy):
f.common.Copy.Copy(f.currentContent.content)
cmds = append(cmds, copyCmd(f.currentContent.content, "File contents copied to clipboard"))
case key.Matches(msg, lineNo):
f.lineNumber = !f.lineNumber
f.code.SetShowLineNumber(f.lineNumber)
Expand Down
4 changes: 1 addition & 3 deletions ui/pages/repo/filesitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ func (d FileItemDelegate) Spacing() int { return 0 }

// Update implements list.ItemDelegate.
func (d FileItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
idx := m.Index()
item, ok := m.SelectedItem().(FileItem)
if !ok {
return nil
Expand All @@ -87,8 +86,7 @@ func (d FileItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
case tea.KeyMsg:
switch {
case key.Matches(msg, d.common.KeyMap.Copy):
d.common.Copy.Copy(item.Title())
return m.SetItem(idx, item)
return copyCmd(item.entry.Name(), fmt.Sprintf("File name %q copied to clipboard", item.entry.Name()))
}
}
return nil
Expand Down
9 changes: 1 addition & 8 deletions ui/pages/repo/logitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
// LogItem is a item in the log list that displays a git commit.
type LogItem struct {
*git.Commit
copied time.Time
}

// ID implements selector.IdentifiableItem.
Expand Down Expand Up @@ -57,7 +56,6 @@ func (d LogItemDelegate) Spacing() int { return 1 }

// Update updates the item. Implements list.ItemDelegate.
func (d LogItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
idx := m.Index()
item, ok := m.SelectedItem().(LogItem)
if !ok {
return nil
Expand All @@ -66,9 +64,7 @@ func (d LogItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
case tea.KeyMsg:
switch {
case key.Matches(msg, d.common.KeyMap.Copy):
item.copied = time.Now()
d.common.Copy.Copy(item.Hash())
return m.SetItem(idx, item)
return copyCmd(item.Hash(), fmt.Sprintf("Commit hash %q copied to clipboard", item.Hash()))
}
}
return nil
Expand All @@ -92,9 +88,6 @@ func (d LogItemDelegate) Render(w io.Writer, m list.Model, index int, listItem l
horizontalFrameSize := styles.Base.GetHorizontalFrameSize()

hash := i.Commit.ID.String()[:7]
if !i.copied.IsZero() && i.copied.Add(time.Second).After(time.Now()) {
hash = "copied"
}
title := styles.Title.Render(
common.TruncateString(i.Title(),
m.Width()-
Expand Down
4 changes: 1 addition & 3 deletions ui/pages/repo/refsitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ func (d RefItemDelegate) Spacing() int { return 0 }

// Update implements list.ItemDelegate.
func (d RefItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
idx := m.Index()
item, ok := m.SelectedItem().(RefItem)
if !ok {
return nil
Expand All @@ -76,8 +75,7 @@ func (d RefItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
case tea.KeyMsg:
switch {
case key.Matches(msg, d.common.KeyMap.Copy):
d.common.Copy.Copy(item.Title())
return m.SetItem(idx, item)
return copyCmd(item.ID(), fmt.Sprintf("Reference %q copied to clipboard", item.ID()))
}
}
return nil
Expand Down
58 changes: 28 additions & 30 deletions ui/pages/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package repo

import (
"fmt"
"time"

"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
Expand Down Expand Up @@ -56,9 +55,6 @@ type EmptyRepoMsg struct{}
// CopyURLMsg is a message to copy the URL of the current repository.
type CopyURLMsg struct{}

// ResetURLMsg is a message to reset the URL string.
type ResetURLMsg struct{}

// UpdateStatusBarMsg updates the status bar.
type UpdateStatusBarMsg struct{}

Expand All @@ -68,6 +64,12 @@ type RepoMsg backend.Repository
// BackMsg is a message to go back to the previous view.
type BackMsg struct{}

// CopyMsg is a message to indicate copied text.
type CopyMsg struct {
Text string
Message string
}

// Repo is a view for a git repository.
type Repo struct {
common common.Common
Expand All @@ -77,7 +79,6 @@ type Repo struct {
statusbar *statusbar.StatusBar
panes []common.Component
ref *git.Reference
copyURL time.Time
state state
spinner spinner.Model
panesReady [lastTab]bool
Expand Down Expand Up @@ -215,8 +216,9 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if r.selectedRepo != nil {
cmds = append(cmds, r.updateStatusBarCmd)
urlID := fmt.Sprintf("%s-url", r.selectedRepo.Name())
cmd := common.CloneCmd(r.common.Config().SSH.PublicURL, r.selectedRepo.Name())
if msg, ok := msg.(tea.MouseMsg); ok && r.common.Zone.Get(urlID).InBounds(msg) {
cmds = append(cmds, r.copyURLCmd())
cmds = append(cmds, copyCmd(cmd, "Command copied to clipboard"))
}
}
switch msg := msg.(type) {
Expand All @@ -234,14 +236,16 @@ func (r *Repo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
}
case CopyURLMsg:
case CopyMsg:
txt := msg.Text
if cfg := r.common.Config(); cfg != nil {
r.common.Copy.Copy(
common.RepoURL(cfg.SSH.PublicURL, r.selectedRepo.Name()),
)
r.common.Copy.Copy(txt)
}
case ResetURLMsg:
r.copyURL = time.Time{}
cmds = append(cmds, func() tea.Msg {
return statusbar.StatusBarMsg{
Value: msg.Message,
}
})
case ReadmeMsg, FileItemsMsg, LogCountMsg, LogItemsMsg, RefItemsMsg:
cmds = append(cmds, r.updateRepo(msg))
// We have two spinners, one is used to when loading the repository and the
Expand Down Expand Up @@ -345,10 +349,7 @@ func (r *Repo) headerView() string {
Align(lipgloss.Right)
var url string
if cfg := r.common.Config(); cfg != nil {
url = common.RepoURL(cfg.SSH.PublicURL, r.selectedRepo.Name())
}
if !r.copyURL.IsZero() && r.copyURL.Add(time.Second).After(time.Now()) {
url = "copied!"
url = common.CloneCmd(cfg.SSH.PublicURL, r.selectedRepo.Name())
}
url = common.TruncateString(url, r.common.Width-lipgloss.Width(desc)-1)
url = r.common.Zone.Mark(
Expand Down Expand Up @@ -378,10 +379,10 @@ func (r *Repo) updateStatusBarCmd() tea.Msg {
branch += " " + r.ref.Name().Short()
}
return statusbar.StatusBarMsg{
Key: r.selectedRepo.Name(),
Value: value,
Info: info,
Branch: branch,
Key: r.selectedRepo.Name(),
Value: value,
Info: info,
Extra: branch,
}
}

Expand Down Expand Up @@ -458,16 +459,13 @@ func (r *Repo) isReady() bool {
return ready
}

func (r *Repo) copyURLCmd() tea.Cmd {
r.copyURL = time.Now()
return tea.Batch(
func() tea.Msg {
return CopyURLMsg{}
},
tea.Tick(time.Second, func(time.Time) tea.Msg {
return ResetURLMsg{}
}),
)
func copyCmd(text, msg string) tea.Cmd {
return func() tea.Msg {
return CopyMsg{
Text: text,
Message: msg,
}
}
}

func updateStatusBarCmd() tea.Msg {
Expand Down
28 changes: 19 additions & 9 deletions ui/pages/selection/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ type Item struct {
repo backend.Repository
lastUpdate *time.Time
cmd string
copied time.Time
}

// New creates a new Item.
Expand All @@ -64,7 +63,7 @@ func NewItem(repo backend.Repository, cfg *config.Config) (Item, error) {
return Item{
repo: repo,
lastUpdate: lastUpdate,
cmd: common.RepoURL(cfg.SSH.PublicURL, repo.Name()),
cmd: common.CloneCmd(cfg.SSH.PublicURL, repo.Name()),
}, nil
}

Expand Down Expand Up @@ -98,6 +97,16 @@ func (i Item) Command() string {
type ItemDelegate struct {
common *common.Common
activePane *pane
copiedIdx int
}

// NewItemDelegate creates a new ItemDelegate.
func NewItemDelegate(common *common.Common, activePane *pane) *ItemDelegate {
return &ItemDelegate{
common: common,
activePane: activePane,
copiedIdx: -1,
}
}

// Width returns the item width.
Expand All @@ -107,16 +116,16 @@ func (d ItemDelegate) Width() int {
}

// Height returns the item height. Implements list.ItemDelegate.
func (d ItemDelegate) Height() int {
func (d *ItemDelegate) Height() int {
height := d.common.Styles.MenuItem.GetVerticalFrameSize() + d.common.Styles.MenuItem.GetHeight()
return height
}

// Spacing returns the spacing between items. Implements list.ItemDelegate.
func (d ItemDelegate) Spacing() int { return 1 }
func (d *ItemDelegate) Spacing() int { return 1 }

// Update implements list.ItemDelegate.
func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
func (d *ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
idx := m.Index()
item, ok := m.SelectedItem().(Item)
if !ok {
Expand All @@ -126,7 +135,7 @@ func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
case tea.KeyMsg:
switch {
case key.Matches(msg, d.common.KeyMap.Copy):
item.copied = time.Now()
d.copiedIdx = idx
d.common.Copy.Copy(item.Command())
return m.SetItem(idx, item)
}
Expand All @@ -135,7 +144,7 @@ func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
}

// Render implements list.ItemDelegate.
func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
func (d *ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
i := listItem.(Item)
s := strings.Builder{}
var matchedRunes []int
Expand Down Expand Up @@ -192,8 +201,9 @@ func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list
s.WriteRune('\n')
cmd := common.TruncateString(i.Command(), m.Width()-styles.Base.GetHorizontalFrameSize())
cmd = styles.Command.Render(cmd)
if !i.copied.IsZero() && i.copied.Add(time.Second).After(time.Now()) {
cmd = styles.Command.Render("Copied!")
if d.copiedIdx == index {
cmd += " " + styles.Desc.Render("(copied to clipboard)")
d.copiedIdx = -1
}
s.WriteString(cmd)
fmt.Fprint(w,
Expand Down
2 changes: 1 addition & 1 deletion ui/pages/selection/selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func New(c common.Common) *Selection {
SetString(defaultNoContent)
selector := selector.New(c,
[]selector.IdentifiableItem{},
ItemDelegate{&c, &sel.activePane})
NewItemDelegate(&c, &sel.activePane))
selector.SetShowTitle(false)
selector.SetShowHelp(false)
selector.SetShowStatusBar(false)
Expand Down

0 comments on commit 5302c49

Please sign in to comment.