summaryrefslogtreecommitdiffstats
path: root/pkg/gui/app_status_manager.go
blob: 2c5a8b1ecca34492634584b60f9559e41d093d0f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package gui

import (
	"sync"
	"time"

	"github.com/jesseduffield/generics/slices"
	"github.com/jesseduffield/lazygit/pkg/utils"
)

// statusManager's job is to handle rendering of loading states and toast notifications
// that you see at the bottom left of the screen.
type statusManager struct {
	statuses []appStatus
	nextId   int
	mutex    sync.Mutex
}

type appStatus struct {
	message    string
	statusType string
	id         int
}

func (m *statusManager) removeStatus(id int) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	m.statuses = slices.Filter(m.statuses, func(status appStatus) bool {
		return status.id != id
	})
}

func (m *statusManager) addWaitingStatus(message string) int {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	m.nextId += 1
	id := m.nextId

	newStatus := appStatus{
		message:    message,
		statusType: "waiting",
		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 {
	if len(m.statuses) == 0 {
		return ""
	}
	topStatus := m.statuses[0]
	if topStatus.statusType == "waiting" {
		return topStatus.message + " " + utils.Loader()
	}
	return topStatus.message
}

func (gui *Gui) toast(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()
			gui.OnUIThread(func() error {
				return gui.renderString(gui.Views.AppStatus, appStatus)
			})

			if appStatus == "" {
				return
			}
		}
	})
}

// withWaitingStatus wraps a function and shows a waiting status while the function is still executing
func (gui *Gui) withWaitingStatus(message string, f func() error) error {
	go utils.Safe(func() {
		id := gui.statusManager.addWaitingStatus(message)

		defer func() {
			gui.statusManager.removeStatus(id)
		}()

		gui.renderAppStatus()

		if err := f(); err != nil {
			gui.OnUIThread(func() error {
				return gui.c.Error(err)
			})
		}
	})

	return nil
}