From 777df725b6fcfbe81064d27acf5f4cd054d8212b Mon Sep 17 00:00:00 2001 From: sunglocto Date: Mon, 2 Feb 2026 13:23:08 +0000 Subject: [PATCH] Add affiliation medals --- assets/admin.png | Bin 0 -> 600 bytes assets/member.png | Bin 0 -> 597 bytes assets/noaffiliation.png | Bin 0 -> 591 bytes assets/outcast.png | Bin 0 -> 538 bytes assets/owner.png | Bin 0 -> 584 bytes gtk-helpers.go | 20 +++++++++- gtk-message.go | 18 ++++----- main.go | 82 +++++++++++++++++++++++++++++++++++++- style.css | 8 ++++ xmpp-displayed_markers.go | 2 +- 10 files changed, 117 insertions(+), 13 deletions(-) create mode 100644 assets/admin.png create mode 100644 assets/member.png create mode 100644 assets/noaffiliation.png create mode 100644 assets/outcast.png create mode 100644 assets/owner.png diff --git a/assets/admin.png b/assets/admin.png new file mode 100644 index 0000000000000000000000000000000000000000..2034f84cf6164c036ee8dec075be4ab0b6b86705 GIT binary patch literal 600 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QUz`&5F${;2w15(7m&@yE)0|P@@+cX9ShJyOO z*(%94VH{{MSd{GV2`_vDoiKYqXY^5d`%!wCI@5rmZ@4<<6x$j~~4K zvTEni{bz3+@?luF=j7tehc4N$J$B}N?=A3uM%liTSK6nqSQ6#dJ9o|Dv)7ONG92<{ zn6Z58r&7Chdr#gAW$RzCe*dY@-FxaVlc%#|H8S6sgRcz$BwdoTVQW(?~qGTvqB zTzdZL(({jdvjd(uv9CUKVeytjr>@@Hapc_FY@J&VUVJLG6*BIKWz^=ne@ZfAbP6+kJvFi9>2ySv*$a0J9DN4?B8>G zYWdoK&3o@&d|6f7&U0?zO_xBm&mnA6wg@o@gax(mPf?L7x2-+t35s%0S3j3^P6QU-Bn(wOc2{&)YMtt~2U|4oacJcv+-;a7LS23)-6#M^W zpL-{RQ#ZrUYsp_`n|z&T@#T2*uiKfwZYKTzbN0vO*e^$eeq4$BdAt0?%eeH#3!r3o?|08%C{ehS;mhg5#F-4~OBsG%PfVD>FnJHdyG{Q8 zKkfYgX~(-oPH$H`f4flrdWp@lJ`wwM*?bHP4ALb*exP965UIN0;Tx0b)&?hozHEAL z%)z7|8v61jvmv`>90LQxF;5rA5Q%WxGofNl0SvBza%|g_$`ltciItU$-QUrOFA zndtPA=E)1~8n;$;&*2b~WME(@Ynx{4<}2fx(lTXn>(nJ+wG54u7DE^e3=9SJeFY8u zvsZ4Pv~Yd%lqEJz+xMQl^7ivrtHy1%Ejw)*x6M5N>HecfYi@l@+w$DL=Zam|rPEh# zfBgP))y|{O-h4cC@s@Sd_CptLt-Jl>%7bU|>z+P+{m!Ck=cPN3_ny8s>EKJd=IzBh zUpGydSKm8(>)s=Nv#%I6o!@`zvT@Tnm+4oO%g*dLa&E<@-TO{nIdJCsg#9ntrY&2y z=VWC6!S%b3-Fomsv;1Vo%#|H8S6sgRICSMx*Qzb;(^vS`ZtR`A=J46;`_JBRnSOc3 z@~!Lko@|}AJblux{srq#UAY&w^69Co_r#-xwlgp=NS6fpfdXuUb?C!4(@hLc21UMK zz`^uIRo{5irk5|7r|w+7j)8&Un5T$O48K_t)K*RL@-kr-G?*BnBQU7Oqp{0`0-1ZF7@^Gb$54f+_-W1^5vD4m6tDH?&|8g zb?errPoH+}+V$<*x05GNzJC4s*s)`cjg1QzE@-K79D<)vNyg{(JZC zoj!fKy}iA@zJB}m?S~E>%FfQdfB*iD9Xn>vo_*!Ym8DCU?%A_v`t<3=#l<^!?wmGl zT24;R)TvYF%$c)$_wKoK=l1mUoH%jf%$YLE%o2IoEzoVqjp{=jq}YA`xzT<|dPq1A{}LbN7TxamkB(m9(6y zx4i%R|NfGmcaJM~usAk|xESyp6j9j6C~+d%_x35qBMoQXC7DE(__iF}cC6rEOY-Ss z6Uy>mU;g)KcP3BC`uaJ3iTmA?6^vKBwD6sxsGK0Qw(R(4tG8iIKN(lQV0ISiZCtm( z{y~vAS(3f;&HmShCS|IM n^ToaTB|P>;Ff0govi}3)rMY5G+y2Wf14WmotDnm{r-UW|M^6G| literal 0 HcmV?d00001 diff --git a/assets/owner.png b/assets/owner.png new file mode 100644 index 0000000000000000000000000000000000000000..1569659d0b89825d0e3cf063969e1330e8e41b66 GIT binary patch literal 584 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QULaVmO|!nr0=MuFkM8OXXU%o{A{P%^H2MS`=bdnB4sd9(OzJZ&d5e z3YGi+`o#C;LI3xzIO4}}Bb@L5w36?)w*Q}5{%>{nA#aAmUJU>DuQ}wyaM*|8owwk7 zUy;MU3|~8~88|U{FzE zN}R{fF2vLv@nwqF^5EqeJ&tP8>-5#X7MUG(*m%!Aeeuz{wFwLO+TtdjFck?geo*r9 z;g7QZ*E{5AJoy{5FW|)UUq!+U4w0WiS8a{j`bb-oks-}@^Hn{u+iRB{`(wSPYUA~n zUtIQ1UHW8h99#4GbDlygp8az>yg%sg!L2v5w!UwVW;%Z{Vav^)w#_Vaj=obUx}ocG ZK>SOblt_DbjSDDtJzf1=);T3K0RTNW4U_-? literal 0 HcmV?d00001 diff --git a/gtk-helpers.go b/gtk-helpers.go index 2663bf8..232b90f 100644 --- a/gtk-helpers.go +++ b/gtk-helpers.go @@ -60,6 +60,7 @@ func switchToTab(jid string, w *gtk.Window) { u.Get(&ocu) nick_label := gtk.NewLabel(JidMustParse(u.From).Resource) + /* affil_label := gtk.NewLabel("") switch mu.MucUserItem.Affiliation { case "owner": @@ -71,14 +72,29 @@ func switchToTab(jid string, w *gtk.Window) { case "none": affil_label.SetText("-") } + */ + + nick_label.AddCSSClass(mu.MucUserItem.Role) + if mu.MucUserItem.Role == "visitor" { + nick_label.SetOpacity(0.5) + } + + /* affil_label.SetHAlign(gtk.AlignEnd) affil_label.SetHExpand(true) affil_label.AddCSSClass(mu.MucUserItem.Affiliation) + */ + userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\nRight-click for more information", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation)) userbox.Append(nick_label) - userbox.Append(affil_label) + // userbox.Append(affil_label) + + medal := gtk.NewImageFromPaintable(clientAssets[mu.MucUserItem.Affiliation]) + medal.SetHAlign(gtk.AlignEnd) + medal.SetHExpand(true) + userbox.Append(medal) gesture := gtk.NewGestureClick() gesture.SetButton(3) // Right click @@ -88,6 +104,8 @@ func switchToTab(jid string, w *gtk.Window) { win.SetDefaultSize(400, 400) profile_box := gtk.NewBox(gtk.OrientationVertical, 0) nick := gtk.NewLabel(JidMustParse(u.From).Resource) + + win.SetTitle(JidMustParse(u.From).Resource) nick.AddCSSClass("author") profile_box.Append(nick) profile_box.Append(gtk.NewLabel(u.From)) diff --git a/gtk-message.go b/gtk-message.go index ddc0e88..1293129 100644 --- a/gtk-message.go +++ b/gtk-message.go @@ -33,9 +33,9 @@ func generatePresenceWidget(p stanza.Packet) gtk.Widgetter { } } - return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " left the room") + return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " left the MUC") } else { - return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " joined the room") + return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " joined the MUC") } } @@ -49,7 +49,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { readmarker := Marker{} ok = m.Get(&readmarker) if ok { - return nil + return gtk.NewLabel(fmt.Sprintf("%s has read to this point", JidMustParse(m.From).Resource)) } @@ -151,13 +151,13 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { im.AddCSSClass("author_img") authorBox.Append(im) } else { - im := newImageFromPath("debug.png") + im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) im.SetPixelSize(40) im.AddCSSClass("author_img") authorBox.Append(im) } } else { - im := newImageFromPath("debug.png") + im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) im.SetPixelSize(40) im.AddCSSClass("author_img") authorBox.Append(im) @@ -206,12 +206,12 @@ func getVAdjustment(scrolledWindow *gtk.ScrolledWindow) *gtk.Adjustment { func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shouldn't be here, and should probably be in xmpp-helpers or somewhere similar. p, err := ensureCache() if err != nil { - return newImageFromPath("debug.png") + return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) } if hash == "" { fmt.Println("Hash is nil!") - return newImageFromPath("debug.png") + return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) } hash = filepath.Join(p, sanitizefilename.Sanitize(hash)) @@ -243,12 +243,12 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou result := <-mychan card, ok := result.Payload.(*VCard) if !ok { - return newImageFromPath("debug.png") + return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) } base64_data := card.Photo.Binval if card.Photo.Binval == "" || (card.Photo.Type == "image/svg+xml" && runtime.GOOS == "windows") { - return newImageFromPath("debug.png") + return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) } data, err := base64.StdEncoding.DecodeString(base64_data) diff --git a/main.go b/main.go index 14a532b..3628574 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "github.com/diamondburned/gotk4/pkg/gio/v2" "github.com/diamondburned/gotk4/pkg/glib/v2" "github.com/diamondburned/gotk4/pkg/gtk/v4" + "github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2" _ "github.com/kr/pretty" "path/filepath" @@ -23,6 +24,7 @@ import ( "encoding/xml" "math/rand/v2" "runtime" + "encoding/base64" ) var loadedConfig lambdaConfig @@ -55,6 +57,33 @@ var mucmembers sync.Map // stores devices of users var userdevices sync.Map +//go:embed debug.png +var defaultAvatarBytes []byte +var defaultAvatarB64 string = base64.StdEncoding.EncodeToString(defaultAvatarBytes) + +//go:embed assets/owner.png +var ownerMedalBytes []byte +var ownerMedalB64 string = base64.StdEncoding.EncodeToString(ownerMedalBytes) + +//go:embed assets/admin.png +var adminMedalBytes []byte +var adminMedalB64 string = base64.StdEncoding.EncodeToString(adminMedalBytes) + +//go:embed assets/member.png +var memberMedalBytes []byte +var memberMedalB64 string = base64.StdEncoding.EncodeToString(memberMedalBytes) + +//go:embed assets/noaffiliation.png +var noneMedalBytes []byte +var noneMedalB64 string = base64.StdEncoding.EncodeToString(noneMedalBytes) + +//go:embed assets/outcast.png +var outcastMedalBytes []byte +var outcastMedalB64 string = base64.StdEncoding.EncodeToString(outcastMedalBytes) + + +var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler) + func init() { go func() { for fn := range uiQueue { @@ -65,6 +94,54 @@ func init() { time.Sleep(10 * time.Millisecond) // Small delay between updates } }() + + loader := gdkpixbuf.NewPixbufLoader() + + defaultAvatarData, _ := base64.StdEncoding.DecodeString(defaultAvatarB64) + loader.Write(defaultAvatarData) + loader.Close() + clientAssets["DefaultAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf()) + + + loader = gdkpixbuf.NewPixbufLoader() + + ownerMedalData, _ := base64.StdEncoding.DecodeString(ownerMedalB64) + loader.Write(ownerMedalData) + loader.Close() + + clientAssets["owner"] = gdk.NewTextureForPixbuf(loader.Pixbuf()) + + loader = gdkpixbuf.NewPixbufLoader() + + adminMedalData, _ := base64.StdEncoding.DecodeString(adminMedalB64) + loader.Write(adminMedalData) + loader.Close() + + clientAssets["admin"] = gdk.NewTextureForPixbuf(loader.Pixbuf()) + + loader = gdkpixbuf.NewPixbufLoader() + + memberMedalData, _ := base64.StdEncoding.DecodeString(memberMedalB64) + loader.Write(memberMedalData) + loader.Close() + + clientAssets["member"] = gdk.NewTextureForPixbuf(loader.Pixbuf()) + + loader = gdkpixbuf.NewPixbufLoader() + + noneMedalData, _ := base64.StdEncoding.DecodeString(noneMedalB64) + loader.Write(noneMedalData) + loader.Close() + + clientAssets["none"] = gdk.NewTextureForPixbuf(loader.Pixbuf()) + + loader = gdkpixbuf.NewPixbufLoader() + + outcastMedalData, _ := base64.StdEncoding.DecodeString(outcastMedalB64) + loader.Write(outcastMedalData) + loader.Close() + + clientAssets["outcast"] = gdk.NewTextureForPixbuf(loader.Pixbuf()) } func main() { @@ -85,7 +162,7 @@ func main() { panic(err) } - // Put 4 random characters in front of lambda + // Put 4 random characters at the end chars := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZÎğ" str := "" for range 4 { @@ -334,7 +411,7 @@ func activate(app *gtk.Application) { the_menu := gio.NewMenu() fileMenu := gio.NewMenu() - fileMenu.Append("Join room", "app.join") + fileMenu.Append("Join MUC", "app.join") fileMenu.Append("Start DM", "app.dm") joinAction := gio.NewSimpleAction("join", nil) @@ -369,6 +446,7 @@ func activate(app *gtk.Application) { box.Append(btn) win := gtk.NewWindow() + win.SetTitle("Join MUC") win.SetDefaultSize(200, 200) win.SetChild(box) diff --git a/style.css b/style.css index 768d44e..0cc414a 100644 --- a/style.css +++ b/style.css @@ -25,3 +25,11 @@ background-color: lime; color: white; } + +.moderator { + color: magenta; +} + +.visitor { + color: grey; +} diff --git a/xmpp-displayed_markers.go b/xmpp-displayed_markers.go index 260539d..447b5c7 100644 --- a/xmpp-displayed_markers.go +++ b/xmpp-displayed_markers.go @@ -15,5 +15,5 @@ type Marker struct { } func init() { - stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:reply:0", Local: "displayed"}, Marker{}) + stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:chat-markers:0", Local: "displayed"}, Marker{}) }