Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
diamondburned
GitHub Repository: diamondburned/gtkcord4
Path: blob/main/internal/sidebar/directbutton/view.go
366 views
1
package directbutton
2
3
import (
4
"context"
5
"log/slog"
6
"sort"
7
8
"github.com/diamondburned/arikawa/v3/discord"
9
"github.com/diamondburned/arikawa/v3/gateway"
10
"github.com/diamondburned/gotk4/pkg/gtk/v4"
11
"github.com/diamondburned/gotkit/gtkutil"
12
"github.com/diamondburned/gotkit/gtkutil/cssutil"
13
"github.com/diamondburned/ningen/v3"
14
"github.com/diamondburned/ningen/v3/states/read"
15
"libdb.so/dissent/internal/gtkcord"
16
"libdb.so/dissent/internal/sidebar/sidebutton"
17
)
18
19
type View struct {
20
*gtk.Box
21
DM *Button
22
23
mentioned struct {
24
IDs []discord.ChannelID
25
Buttons map[discord.ChannelID]*ChannelButton
26
}
27
28
ctx context.Context
29
}
30
31
var viewCSS = cssutil.Applier("dmbutton-view", `
32
`)
33
34
func NewView(ctx context.Context) *View {
35
v := View{
36
Box: gtk.NewBox(gtk.OrientationVertical, 0),
37
DM: NewButton(ctx),
38
ctx: ctx,
39
}
40
41
v.mentioned.IDs = make([]discord.ChannelID, 0, 4)
42
v.mentioned.Buttons = make(map[discord.ChannelID]*ChannelButton, 4)
43
44
v.Append(v.DM)
45
viewCSS(v)
46
47
vis := gtkutil.WithVisibility(ctx, v)
48
49
state := gtkcord.FromContext(ctx)
50
state.BindHandler(vis, func(ev gateway.Event) {
51
switch ev := ev.(type) {
52
case *read.UpdateEvent:
53
if !ev.GuildID.IsValid() {
54
v.Invalidate()
55
}
56
case *gateway.MessageCreateEvent:
57
if !ev.GuildID.IsValid() {
58
v.Invalidate()
59
}
60
}
61
},
62
(*read.UpdateEvent)(nil),
63
(*gateway.MessageCreateEvent)(nil),
64
)
65
66
return &v
67
}
68
69
type channelUnreadStatus struct {
70
*discord.Channel
71
UnreadCount int
72
}
73
74
func (v *View) Invalidate() {
75
state := gtkcord.FromContext(v.ctx)
76
77
// This is slow, but whatever.
78
dms, err := state.PrivateChannels()
79
if err != nil {
80
slog.Error(
81
"failed to get private channels for DM button view",
82
"err", err)
83
84
// Clear all DMs.
85
v.update(nil)
86
return
87
}
88
89
var unreads map[discord.ChannelID]channelUnreadStatus
90
for i, dm := range dms {
91
count := state.ChannelCountUnreads(dm.ID, ningen.UnreadOpts{})
92
if count == 0 {
93
continue
94
}
95
96
if unreads == nil {
97
unreads = make(map[discord.ChannelID]channelUnreadStatus, 4)
98
}
99
100
unreads[dm.ID] = channelUnreadStatus{
101
Channel: &dms[i],
102
UnreadCount: count,
103
}
104
}
105
106
v.update(unreads)
107
}
108
109
func (v *View) update(unreads map[discord.ChannelID]channelUnreadStatus) {
110
for _, unread := range unreads {
111
button, ok := v.mentioned.Buttons[unread.Channel.ID]
112
if !ok {
113
button = NewChannelButton(v.ctx, unread.Channel.ID)
114
v.mentioned.Buttons[unread.Channel.ID] = button
115
}
116
117
button.Update(unread.Channel)
118
button.InvalidateUnread()
119
}
120
121
// Purge all buttons off the widget.
122
gtkutil.RemoveChildren(v)
123
124
// Delete unused buttons.
125
for id := range v.mentioned.Buttons {
126
if _, ok := unreads[id]; !ok {
127
delete(v.mentioned.Buttons, id)
128
}
129
}
130
131
// Recreate the IDs slice.
132
v.mentioned.IDs = v.mentioned.IDs[:0]
133
for id := range unreads {
134
v.mentioned.IDs = append(v.mentioned.IDs, id)
135
}
136
137
// Sort the IDs slice. We'll sort it according to the time that the last
138
// message was sent: the most recent message will be at the top.
139
sort.Slice(v.mentioned.IDs, func(i, j int) bool {
140
mi := unreads[v.mentioned.IDs[i]]
141
mj := unreads[v.mentioned.IDs[j]]
142
return mi.LastMessageID > mj.LastMessageID
143
})
144
145
// Append the buttons back to the widget.
146
v.Append(v.DM)
147
for _, id := range v.mentioned.IDs {
148
v.Append(v.mentioned.Buttons[id])
149
}
150
}
151
152
func (v *View) Unselect() {
153
v.SetSelected(false)
154
}
155
156
func (v *View) SetSelected(selected bool) {
157
if selected {
158
v.DM.Pill.State = sidebutton.PillActive
159
} else {
160
v.DM.Pill.State = sidebutton.PillDisabled
161
}
162
v.DM.Pill.Invalidate()
163
}
164
165