main 5041353

history_sidebar_reactivity.go RawHistoryFolder

1package component
2
3import (
4	"time"
5
6	"github.com/bnema/puregotk/v4/glib"
7	"github.com/bnema/puregotk/v4/gtk"
8)
9
10type relativeTimeLabelBinding struct {
11	label       *gtk.Label
12	lastVisited time.Time
13}
14
15func (hs *HistorySidebar) RequestReloadIfVisible(reason string) {
16	_ = reason
17	if hs == nil {
18		return
19	}
20	hs.mu.RLock()
21	if hs.destroyed || !hs.visible {
22		hs.mu.RUnlock()
23		return
24	}
25	hs.mu.RUnlock()
26	cb := glib.SourceFunc(func(uintptr) bool {
27		hs.requestReloadIfVisibleOnMain()
28		return false
29	})
30	hs.scheduleIdle(cb)
31}
32
33func (hs *HistorySidebar) requestReloadIfVisibleOnMain() {
34	hs.mu.Lock()
35	if hs.destroyed || !hs.visible {
36		hs.mu.Unlock()
37		return
38	}
39	oldTimer := hs.reloadDebounceTimer
40	hs.reloadDebounceTimer = 0
41	hs.mu.Unlock()
42
43	if oldTimer != 0 {
44		hs.removeSource(oldTimer)
45	}
46
47	var timerID uint
48	reloadCb := glib.SourceFunc(func(uintptr) bool {
49		hs.mu.Lock()
50		if hs.reloadDebounceTimer != timerID {
51			hs.mu.Unlock()
52			return false
53		}
54		hs.reloadDebounceTimer = 0
55		if hs.destroyed || !hs.visible {
56			hs.mu.Unlock()
57			return false
58		}
59		hs.mu.Unlock()
60		hs.Reload()
61		return false
62	})
63	timerID = hs.addTimeout(sidebarReloadDebounceMs, reloadCb)
64
65	hs.mu.Lock()
66	if hs.destroyed || !hs.visible {
67		hs.mu.Unlock()
68		if timerID != 0 {
69			hs.removeSource(timerID)
70		}
71		return
72	}
73	hs.reloadDebounceTimer = timerID
74	hs.mu.Unlock()
75}
76
77func (hs *HistorySidebar) cancelReloadDebounceLocked() uint {
78	timerID := hs.reloadDebounceTimer
79	hs.reloadDebounceTimer = 0
80	return timerID
81}
82
83func (hs *HistorySidebar) bindRelativeTimeLabel(label *gtk.Label, visited time.Time) {
84	if label == nil {
85		return
86	}
87	hs.mu.Lock()
88	if !hs.destroyed {
89		hs.relativeTimeLabelBinds = append(hs.relativeTimeLabelBinds, relativeTimeLabelBinding{label: label, lastVisited: visited})
90	}
91	hs.mu.Unlock()
92}
93
94func (hs *HistorySidebar) clearRelativeTimeBindingsLocked() {
95	hs.relativeTimeLabelBinds = nil
96}
97
98func (hs *HistorySidebar) startRelativeTimeTicker() {
99	hs.mu.Lock()
100	if hs.destroyed || !hs.visible {
101		hs.mu.Unlock()
102		return
103	}
104	oldTicker := hs.relativeTimeTicker
105	hs.relativeTimeTicker = 0
106	hs.relativeTimeDayKey = hs.currentDayKey()
107	hs.relativeTimeDayKeySet = true
108	hs.mu.Unlock()
109
110	if oldTicker != 0 {
111		hs.removeSource(oldTicker)
112	}
113
114	hs.scheduleRelativeTimeRefresh()
115
116	var tickerID uint
117	tickCb := glib.SourceFunc(func(uintptr) bool {
118		hs.mu.RLock()
119		keep := !hs.destroyed && hs.visible && hs.relativeTimeTicker == tickerID
120		hs.mu.RUnlock()
121		if !keep {
122			return false
123		}
124		hs.scheduleRelativeTimeRefresh()
125		return true
126	})
127	tickerID = hs.addTimeout(sidebarRelativeTickMs, tickCb)
128
129	hs.mu.Lock()
130	if hs.destroyed || !hs.visible {
131		hs.mu.Unlock()
132		if tickerID != 0 {
133			hs.removeSource(tickerID)
134		}
135		return
136	}
137	hs.relativeTimeTicker = tickerID
138	hs.mu.Unlock()
139}
140
141func (hs *HistorySidebar) scheduleRelativeTimeRefresh() {
142	cb := glib.SourceFunc(func(uintptr) bool {
143		hs.updateRelativeTimeLabelsOnMain()
144		return false
145	})
146	hs.scheduleIdle(cb)
147}
148
149func (hs *HistorySidebar) updateRelativeTimeLabelsOnMain() {
150	now := hs.currentTime()
151	hs.mu.RLock()
152	if hs.destroyed || !hs.visible {
153		hs.mu.RUnlock()
154		return
155	}
156	currentDay := dayKeyFromTime(now)
157	lastDay := hs.relativeTimeDayKey
158	daySet := hs.relativeTimeDayKeySet
159	bindings := append([]relativeTimeLabelBinding(nil), hs.relativeTimeLabelBinds...)
160	hs.mu.RUnlock()
161
162	if daySet && currentDay != lastDay {
163		shouldReload := false
164		hs.mu.Lock()
165		if !hs.destroyed && hs.visible && hs.relativeTimeDayKeySet && hs.relativeTimeDayKey != currentDay {
166			hs.relativeTimeDayKey = currentDay
167			shouldReload = true
168		}
169		hs.mu.Unlock()
170		if shouldReload {
171			hs.RequestReloadIfVisible("day-boundary")
172		}
173		return
174	}
175
176	for _, binding := range bindings {
177		if binding.label == nil {
178			continue
179		}
180		binding.label.SetText(relativeTimeAt(binding.lastVisited, now))
181	}
182}
183
184func (hs *HistorySidebar) currentTime() time.Time {
185	if hs != nil && hs.now != nil {
186		return hs.now()
187	}
188	return time.Now()
189}
190
191func (hs *HistorySidebar) currentDayKey() dayKey {
192	return dayKeyFromTime(hs.currentTime())
193}
194
195func dayKeyFromTime(t time.Time) dayKey {
196	return dayKey{year: t.Year(), month: t.Month(), day: t.Day()}
197}
198
199func (hs *HistorySidebar) addTimeout(ms uint, cb glib.SourceFunc) uint {
200	if hs != nil && hs.timeoutAdd != nil {
201		return hs.timeoutAdd(ms, cb)
202	}
203	return glib.TimeoutAdd(ms, &cb, 0)
204}
205
206func (hs *HistorySidebar) removeSource(id uint) {
207	if id == 0 {
208		return
209	}
210	if hs != nil && hs.sourceRemove != nil {
211		hs.sourceRemove(id)
212		return
213	}
214	glib.SourceRemove(id)
215}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close