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}