From a3dfcd5a9502e482190b255f7964b97a2c55e074 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 18 Nov 2020 09:08:32 +1100 Subject: [PATCH] toast notifications --- pkg/gui/app_status_manager.go | 95 ++++++++++++++++++++++++++--------- pkg/gui/branches_panel.go | 6 +-- pkg/gui/updates.go | 8 +-- 3 files changed, 78 insertions(+), 31 deletions(-) diff --git a/pkg/gui/app_status_manager.go b/pkg/gui/app_status_manager.go index a613f5adb..089344701 100644 --- a/pkg/gui/app_status_manager.go +++ b/pkg/gui/app_status_manager.go @@ -1,6 +1,7 @@ package gui import ( + "sync" "time" "github.com/jesseduffield/gocui" @@ -8,33 +9,68 @@ import ( ) type appStatus struct { - name string + message string statusType string - duration int + id int } type statusManager struct { statuses []appStatus + nextId int + mutex sync.Mutex } -func (m *statusManager) removeStatus(name string) { +func (m *statusManager) removeStatus(id int) { + m.mutex.Lock() + defer m.mutex.Unlock() + newStatuses := []appStatus{} for _, status := range m.statuses { - if status.name != name { + if status.id != id { newStatuses = append(newStatuses, status) } } m.statuses = newStatuses } -func (m *statusManager) addWaitingStatus(name string) { - m.removeStatus(name) +func (m *statusManager) addWaitingStatus(message string) int { + m.mutex.Lock() + defer m.mutex.Unlock() + + m.nextId += 1 + id := m.nextId + newStatus := appStatus{ - name: name, + message: message, statusType: "waiting", - duration: 0, + id: id, } m.statuses = append([]appStatus{newStatus}, m.statuses...) + + return id +} + +func (m *statusManager) addToastStatus(message string) int { + m.mutex.Lock() + defer m.mutex.Unlock() + + m.nextId++ + id := m.nextId + + newStatus := appStatus{ + message: message, + statusType: "toast", + id: id, + } + m.statuses = append([]appStatus{newStatus}, m.statuses...) + + go func() { + time.Sleep(time.Second * 2) + + m.removeStatus(id) + }() + + return id } func (m *statusManager) getStatusString() string { @@ -43,31 +79,42 @@ func (m *statusManager) getStatusString() string { } topStatus := m.statuses[0] if topStatus.statusType == "waiting" { - return topStatus.name + " " + utils.Loader() + return topStatus.message + " " + utils.Loader() } - return topStatus.name + return topStatus.message +} + +func (gui *Gui) raiseToastStatus(message string) { + gui.statusManager.addToastStatus(message) + + gui.renderAppStatus() +} + +func (gui *Gui) renderAppStatus() { + go utils.Safe(func() { + ticker := time.NewTicker(time.Millisecond * 50) + defer ticker.Stop() + for range ticker.C { + appStatus := gui.statusManager.getStatusString() + if appStatus == "" { + gui.renderString("appStatus", "") + return + } + gui.renderString("appStatus", appStatus) + } + }) } // WithWaitingStatus wraps a function and shows a waiting status while the function is still executing -func (gui *Gui) WithWaitingStatus(name string, f func() error) error { +func (gui *Gui) WithWaitingStatus(message string, f func() error) error { go utils.Safe(func() { - gui.statusManager.addWaitingStatus(name) + id := gui.statusManager.addWaitingStatus(message) defer func() { - gui.statusManager.removeStatus(name) + gui.statusManager.removeStatus(id) }() - go utils.Safe(func() { - ticker := time.NewTicker(time.Millisecond * 50) - defer ticker.Stop() - for range ticker.C { - appStatus := gui.statusManager.getStatusString() - if appStatus == "" { - return - } - gui.renderString("appStatus", appStatus) - } - }) + gui.renderAppStatus() if err := f(); err != nil { gui.g.Update(func(g *gocui.Gui) error { diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index 8aafe0682..9208f7085 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -107,9 +107,9 @@ func (gui *Gui) handleCopyPullRequestURLPress(g *gocui.Gui, v *gocui.View) error return gui.surfaceError(err) } - return gui.createPopupPanel(createPopupPanelOpts{ - prompt: gui.Tr.PullRequestURLCopiedToClipboard, - }) + gui.raiseToastStatus(gui.Tr.PullRequestURLCopiedToClipboard) + + return nil } func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { diff --git a/pkg/gui/updates.go b/pkg/gui/updates.go index 4357de1c1..02557deb9 100644 --- a/pkg/gui/updates.go +++ b/pkg/gui/updates.go @@ -41,13 +41,13 @@ func (gui *Gui) onBackgroundUpdateCheckFinish(newVersion string, err error) erro func (gui *Gui) startUpdating(newVersion string) { gui.State.Updating = true - gui.statusManager.addWaitingStatus("updating") - gui.Updater.Update(newVersion, gui.onUpdateFinish) + statusId := gui.statusManager.addWaitingStatus("updating") + gui.Updater.Update(newVersion, func(err error) error { return gui.onUpdateFinish(statusId, err) }) } -func (gui *Gui) onUpdateFinish(err error) error { +func (gui *Gui) onUpdateFinish(statusId int, err error) error { gui.State.Updating = false - gui.statusManager.removeStatus("updating") + gui.statusManager.removeStatus(statusId) gui.renderString("appStatus", "") if err != nil { return gui.createErrorPanel("Update failed: " + err.Error())