Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
diamondburned
GitHub Repository: diamondburned/gtkcord4
Path: blob/main/internal/sidebar/userbar.go
366 views
1
package sidebar
2
3
import (
4
"context"
5
"regexp"
6
"strconv"
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/gotk4/pkg/pango"
12
"github.com/diamondburned/gotkit/components/onlineimage"
13
"github.com/diamondburned/gotkit/gtkutil"
14
"github.com/diamondburned/gotkit/gtkutil/cssutil"
15
"github.com/diamondburned/gotkit/gtkutil/imgutil"
16
"libdb.so/dissent/internal/gtkcord"
17
)
18
19
type userBar struct {
20
// *gtk.ActionBar
21
*gtk.Box
22
avatar *onlineimage.Avatar
23
name *gtk.Label
24
status *gtk.Image
25
menu *gtk.ToggleButton
26
27
ctx context.Context
28
}
29
30
var userBarCSS = cssutil.Applier("user-bar", `
31
.user-bar-avatar {
32
padding: 6px;
33
}
34
.user-bar-menu {
35
margin: 0 6px;
36
}
37
`)
38
39
func newUserBar(ctx context.Context, menuActions []gtkutil.PopoverMenuItem) *userBar {
40
b := userBar{ctx: ctx}
41
b.avatar = onlineimage.NewAvatar(ctx, imgutil.HTTPProvider, gtkcord.UserBarAvatarSize)
42
b.avatar.AddCSSClass("user-bar-avatar")
43
44
b.name = gtk.NewLabel("")
45
b.name.AddCSSClass("user-bar-name")
46
b.name.SetSelectable(true)
47
b.name.SetXAlign(0)
48
b.name.SetHExpand(true)
49
b.name.SetWrap(false)
50
b.name.SetEllipsize(pango.EllipsizeEnd)
51
52
b.status = gtk.NewImage()
53
b.status.AddCSSClass("user-bar-status")
54
b.updatePresence(nil)
55
56
b.menu = gtk.NewToggleButton()
57
b.menu.AddCSSClass("user-bar-menu")
58
b.menu.SetIconName("menu-large-symbolic")
59
b.menu.SetTooltipText("Main Menu")
60
b.menu.SetHasFrame(false)
61
b.menu.SetVAlign(gtk.AlignCenter)
62
b.menu.ConnectClicked(func() {
63
p := gtkutil.NewPopoverMenuCustom(b.menu, gtk.PosTop, menuActions)
64
p.ConnectHide(func() { b.menu.SetActive(false) })
65
gtkutil.PopupFinally(p)
66
})
67
68
b.Box = gtk.NewBox(gtk.OrientationHorizontal, 0)
69
b.Box.Append(b.avatar)
70
b.Box.Append(b.name)
71
b.Box.Append(b.status)
72
b.Box.Append(b.menu)
73
userBarCSS(b)
74
75
anim := b.avatar.EnableAnimation()
76
anim.ConnectMotion(b)
77
78
vis := gtkutil.WithVisibility(ctx, b)
79
80
client := gtkcord.FromContext(ctx)
81
client.BindHandler(vis,
82
func(ev gateway.Event) {
83
switch ev := ev.(type) {
84
case *gateway.UserUpdateEvent:
85
b.updateUser(&ev.User)
86
case
87
*gateway.PresenceUpdateEvent,
88
*gateway.PresencesReplaceEvent,
89
*gateway.SessionsReplaceEvent,
90
*gateway.UserSettingsUpdateEvent,
91
*gateway.ReadyEvent:
92
b.invalidatePresence()
93
}
94
},
95
(*gateway.UserUpdateEvent)(nil),
96
(*gateway.PresenceUpdateEvent)(nil),
97
(*gateway.PresencesReplaceEvent)(nil),
98
(*gateway.SessionsReplaceEvent)(nil),
99
(*gateway.UserSettingsUpdateEvent)(nil),
100
(*gateway.ReadyEvent)(nil),
101
)
102
103
me, _ := client.Me()
104
if me != nil {
105
b.updateUser(me)
106
107
}
108
109
return &b
110
}
111
112
var discriminatorRe = regexp.MustCompile(`#\d{1,4}$`)
113
114
func (b *userBar) updateUser(me *discord.User) {
115
tag := me.Username
116
if v, _ := strconv.Atoi(me.Discriminator); v != 0 {
117
tag += `<span size="smaller">` + "#" + me.Discriminator + "</span>"
118
}
119
120
var name string
121
if me.DisplayName != "" {
122
name = me.DisplayName + "\n" + `<span size="smaller">` + tag + "</span>"
123
} else {
124
name = tag
125
}
126
127
displayName := me.DisplayName
128
if displayName == "" {
129
displayName = me.Username
130
}
131
132
b.avatar.SetText(displayName)
133
b.avatar.SetFromURL(me.AvatarURL())
134
b.name.SetMarkup(name)
135
b.name.SetTooltipMarkup(name)
136
}
137
138
func (b *userBar) updatePresence(presence *discord.Presence) {
139
if presence == nil {
140
b.status.SetTooltipText(statusText(discord.UnknownStatus))
141
b.status.SetFromIconName(statusIcon(discord.UnknownStatus))
142
return
143
}
144
145
if presence.User.Username != "" {
146
b.updateUser(&presence.User)
147
}
148
149
b.status.SetTooltipText(statusText(presence.Status))
150
b.status.SetFromIconName(statusIcon(presence.Status))
151
}
152
153
func (b *userBar) invalidatePresence() {
154
state := gtkcord.FromContext(b.ctx)
155
me, _ := state.Me()
156
157
presence, _ := state.PresenceStore.Presence(0, me.ID)
158
if presence != nil {
159
b.updatePresence(presence)
160
}
161
}
162
163
func statusIcon(status discord.Status) string {
164
switch status {
165
case discord.OnlineStatus:
166
return "user-available"
167
case discord.DoNotDisturbStatus:
168
return "user-busy"
169
case discord.IdleStatus:
170
return "user-idle"
171
case discord.InvisibleStatus:
172
return "user-invisible"
173
case discord.OfflineStatus:
174
return "user-offline"
175
case discord.UnknownStatus:
176
fallthrough
177
default:
178
return "user-status-pending"
179
}
180
}
181
182
func statusText(status discord.Status) string {
183
switch status {
184
case discord.OnlineStatus:
185
return "Online"
186
case discord.DoNotDisturbStatus:
187
return "Busy"
188
case discord.IdleStatus:
189
return "Idle"
190
case discord.InvisibleStatus:
191
return "Invisible"
192
case discord.OfflineStatus:
193
return "Offline"
194
case discord.UnknownStatus:
195
fallthrough
196
default:
197
return "Unknown"
198
}
199
}
200
201