From 713cb24508a53013ca8c2a981a564640c23c12a2 Mon Sep 17 00:00:00 2001 From: sunglocto Date: Sun, 8 Feb 2026 09:44:45 +0000 Subject: [PATCH] Add hat icon, change some CSS and remove all reply support --- README.md | 2 ++ assets/tag.png | Bin 0 -> 666 bytes gtk-helpers.go | 94 +++++++++++++++++++++++++++++++++++++++---------- gtk-message.go | 36 +++++++------------ main.go | 34 ++++++++++++------ style.css | 6 ++++ xmpp-reply.go | 20 ----------- 7 files changed, 118 insertions(+), 74 deletions(-) create mode 100644 assets/tag.png delete mode 100644 xmpp-reply.go diff --git a/README.md b/README.md index 8f21e73..3dd08c0 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,5 @@ an XMPP client icons are from Psi+ ([https://github.com/psi-im](https://github.com/psi-im)) + +additional icons are by Mark James's Silk Icon Set [https://peacocksoftware.com/sites/peacocksoftware/silk_icons/comment.png](https://peacocksoftware.com/sites/peacocksoftware/silk_icons/comment.png) diff --git a/assets/tag.png b/assets/tag.png new file mode 100644 index 0000000000000000000000000000000000000000..c8edfc7474bc6cd292cc866071dfa69c649c9289 GIT binary patch literal 666 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s77>k44ofy`glX=O&z`&C3 z==F#>= z=j~SgJ-*_ncs#>oL%;0b+id*&rLufES1H?>(3GR4e5Hp?D`d03Pi2dUQnwOZ9PvaZ z!#((Zv4mXvf>VBe1I}7#1ipQE&FJ9|@$}=iC88I0oj-UZmcxj#q=six#M18Ffrm_FU7f$LcqZ(l zZ+!7%pTjo2fYcw898WxvmVGlrxu;(7Xi(bXEWVfrS$U$_x&Dyzw*Lbg*{5vSKXFnK@0P0bFLOMD>iW5Ey$h~f zb@|LeffbF1eUlfsE%@YWS0bw#QToyB#R-o1BfGA&?{SL#5xL*PcICAC*3lk~Kwo|>=RcWX+z(Iw6N2CY2`uchZyB($cK zE3eCAN_kQ!m-pW*y6bo5$~|3wPsVIonIhp^`~L7P_4yNvwL4m`$~&a-z4*J}&TPk9 zaaWA1J)36c?%wgtpS4asExNDF_n6`DQ&VbwH!V5C*43w<8hKXqwCULw9s3x=T^J7* Uyqs>qz`(%Z>FVdQ&MBb@0Bjv0-v9sr literal 0 HcmV?d00001 diff --git a/gtk-helpers.go b/gtk-helpers.go index 2d5d331..ff3ae8d 100644 --- a/gtk-helpers.go +++ b/gtk-helpers.go @@ -2,6 +2,8 @@ package main import ( "context" + "crypto/sha1" + "encoding/hex" "fmt" "github.com/diamondburned/gotk4/pkg/glib/v2" "github.com/diamondburned/gotk4/pkg/gtk/v4" @@ -75,17 +77,17 @@ func switchToTab(jid string, w *gtk.Window) { nick_label := gtk.NewLabel(JidMustParse(u.From).Resource) nick_label.SetEllipsize(pango.EllipsizeEnd) /* - affil_label := gtk.NewLabel("") - switch mu.MucUserItem.Affiliation { - case "owner": - affil_label.SetText("O") - case "admin": - affil_label.SetText("A") - case "member": - affil_label.SetText("M") - case "none": - affil_label.SetText("-") - } + affil_label := gtk.NewLabel("") + switch mu.MucUserItem.Affiliation { + case "owner": + affil_label.SetText("O") + case "admin": + affil_label.SetText("A") + case "member": + affil_label.SetText("M") + case "none": + affil_label.SetText("-") + } */ nick_label.AddCSSClass(mu.MucUserItem.Role) @@ -94,22 +96,39 @@ func switchToTab(jid string, w *gtk.Window) { } /* - affil_label.SetHAlign(gtk.AlignEnd) - affil_label.SetHExpand(true) + affil_label.SetHAlign(gtk.AlignEnd) + affil_label.SetHExpand(true) - affil_label.AddCSSClass(mu.MucUserItem.Affiliation) + 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) + var hats Hats + ok := u.Get(&hats) + if ok { + for _, hat := range hats.Hats { + tag := gtk.NewImageFromPaintable(clientAssets["tag"]) + tag.SetTooltipText(hat.Title) + // tag.SetHAlign(gtk.AlignEnd) + // tag.SetHExpand(true) + userbox.Prepend(tag) + } + } + medal := gtk.NewImageFromPaintable(clientAssets[mu.MucUserItem.Affiliation]) + medal.SetTooltipText(mu.MucUserItem.Affiliation) + medal.SetHAlign(gtk.AlignEnd) medal.SetHExpand(true) userbox.Append(medal) - + + + + gesture := gtk.NewGestureClick() gesture.SetButton(3) // Right click @@ -143,7 +162,9 @@ func switchToTab(jid string, w *gtk.Window) { ok := u.Get(&hats) if ok { for _, hat := range hats.Hats { - profile_box.Append(gtk.NewLabel(hat.Title)) + l := gtk.NewLabel(hat.Title) + l.AddCSSClass("hat") + profile_box.Append(l) } } @@ -157,6 +178,43 @@ func switchToTab(jid string, w *gtk.Window) { profile_box.Append(gtk.NewLabel("Affiliated as " + mu.MucUserItem.Affiliation)) } + go func() { + fmt.Println("Attempting to get Disco info") + + myIQ, err := stanza.NewIQ(stanza.Attrs{ + Type: "get", + From: clientroot.Session.BindJid, + To: u.From, + Id: "dicks", + Lang: "en", + }) + + if err != nil { + panic(err) + } + + myIQ.Payload = &stanza.DiscoInfo{} + + ctx := context.TODO() + mychan, err := client.SendIQ(ctx, myIQ) + if err == nil { + result := <-mychan + res, ok := result.Payload.(*stanza.DiscoInfo) + if ok { + idents := res.Identity + for i, ident := range idents { + profile_box.Append(gtk.NewLabel(fmt.Sprintf("Identity %d: Name: %s, Category: %s, Type: %s", i+1, ident.Name, ident.Category, ident.Type))) + } + + s := fmt.Sprintf("%v", res.Features) + h := sha1.New() + h.Write([]byte(s)) + sha1_hash := hex.EncodeToString(h.Sum(nil)) + profile_box.Append(gtk.NewLabel(fmt.Sprintf("The hash of this user's Disco features is:\n%s\nUse the disco feature to view them", sha1_hash))) + } + } + }() + go func() { ctx := context.TODO() mychan, err := client.SendIQ(ctx, iqResp) @@ -169,9 +227,7 @@ func switchToTab(jid string, w *gtk.Window) { version := ver.Version os := ver.OS - profile_box.Append(gtk.NewLabel(name)) - profile_box.Append(gtk.NewLabel(version)) - profile_box.Append(gtk.NewLabel(os)) + profile_box.Append(gtk.NewLabel(fmt.Sprintf("%s %s %s", name, version, os))) } } }() diff --git a/gtk-message.go b/gtk-message.go index 7f70c71..17bfa03 100644 --- a/gtk-message.go +++ b/gtk-message.go @@ -6,8 +6,8 @@ import ( "context" "encoding/base64" "fmt" - "github.com/diamondburned/gotk4/pkg/gtk/v4" "github.com/diamondburned/gotk4/pkg/gdk/v4" + "github.com/diamondburned/gotk4/pkg/gtk/v4" "github.com/google/uuid" "github.com/jacoblockett/sanitizefilename" "github.com/jasonlovesdoggo/gopen" @@ -54,9 +54,9 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { return b } - indicator := stanza.StateComposing{} - ok = m.Get(&indicator) - if ok { // TODO: Display typing indicator in a stat bar or something similar + composing := stanza.StateComposing{} + ok = m.Get(&composing) + if ok { b := gtk.NewBox(gtk.OrientationHorizontal, 0) b.Append(gtk.NewLabel(fmt.Sprintf("%s is typing...", JidMustParse(m.From).Resource))) return b @@ -72,7 +72,6 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { return error_box } - sid := StanzaID{} m.Get(&sid) @@ -80,7 +79,6 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { gesture := gtk.NewGestureClick() gesture.SetButton(3) // Right click - popover := gtk.NewPopover() popover.SetParent(mainBox) popover.SetHasArrow(false) @@ -94,7 +92,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { like.SetLabel(v) like.SetHExpand(true) like.ConnectClicked(func() { - fmt.Println("licked") // TODO: Implement proper support for reactions via extension + fmt.Println("licked") // TODO: Implement proper support for reactions via extension client.SendRaw(fmt.Sprintf(` @@ -109,9 +107,9 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { rc_box.Append(reactions) if m.Type == stanza.MessageTypeGroupchat { - moderate := gtk.NewButtonWithLabel("Moderate") // TODO: Implement proper support for moderations via extension - moderate.ConnectClicked(func() { - client.SendRaw(fmt.Sprintf(` + moderate := gtk.NewButtonWithLabel("Moderate") // TODO: Implement proper support for moderations via extension + moderate.ConnectClicked(func() { + client.SendRaw(fmt.Sprintf(` @@ -119,11 +117,10 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { `, jid.MustParse(m.From).Bare().String(), uuid.New().String(), sid.ID)) - }) - rc_box.Append(moderate) + }) + rc_box.Append(moderate) } - popover.SetChild(rc_box) gesture.Connect("pressed", func(n_press, x, y int) { @@ -134,14 +131,6 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { mainBox.AddController(gesture) - reply := Reply{} - ok = m.Get(&reply) - if ok { - replyBox := gtk.NewBox(gtk.OrientationHorizontal, 0) - replyBox.Append(gtk.NewLabel("↱ " + jid.MustParse(reply.To).Resourcepart())) - mainBox.Append(replyBox) - } - ocu := OccupantID{} m.Get(&ocu) @@ -190,7 +179,6 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { al.SetText(al.Text() + " whispers") } - authorBox.Append(al) mlabel := gtk.NewLabel(m.Body) mlabel.SetWrap(true) @@ -230,7 +218,7 @@ 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 gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) + return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) } if hash == "" { @@ -267,7 +255,7 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou result := <-mychan card, ok := result.Payload.(*VCard) if !ok { - return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) + return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"]) } base64_data := card.Photo.Binval diff --git a/main.go b/main.go index 69de626..d46e703 100644 --- a/main.go +++ b/main.go @@ -7,10 +7,10 @@ import ( "context" "fmt" "github.com/diamondburned/gotk4/pkg/gdk/v4" + "github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2" "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" "path/filepath" "github.com/BurntSushi/toml" @@ -20,10 +20,11 @@ import ( "time" _ "embed" + "encoding/base64" "encoding/xml" "math/rand/v2" "runtime" - "encoding/base64" + "github.com/kr/pretty" ) var loadedConfig lambdaConfig @@ -84,6 +85,10 @@ var outcastMedalB64 string = base64.StdEncoding.EncodeToString(outcastMedalBytes var cancelBytes []byte var cancelB64 string = base64.StdEncoding.EncodeToString(cancelBytes) +//go:embed assets/tag.png +var tagBytes []byte +var tagB64 string = base64.StdEncoding.EncodeToString(tagBytes) + var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler) var lockedJIDs map[string]bool = make(map[string]bool) @@ -105,7 +110,6 @@ func init() { loader.Close() clientAssets["DefaultAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf()) - loader = gdkpixbuf.NewPixbufLoader() ownerMedalData, _ := base64.StdEncoding.DecodeString(ownerMedalB64) @@ -124,6 +128,14 @@ func init() { loader = gdkpixbuf.NewPixbufLoader() + tagData, _ := base64.StdEncoding.DecodeString(tagB64) + loader.Write(tagData) + loader.Close() + + clientAssets["tag"] = gdk.NewTextureForPixbuf(loader.Pixbuf()) + + loader = gdkpixbuf.NewPixbufLoader() + adminMedalData, _ := base64.StdEncoding.DecodeString(adminMedalB64) loader.Write(adminMedalData) loader.Close() @@ -173,7 +185,7 @@ func main() { panic(err) } - // Put 4 random characters at the end + // Put 4 random characters at the end chars := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZλ" str := "" for range 4 { @@ -184,9 +196,9 @@ func main() { TransportConfiguration: xmpp.TransportConfiguration{ Address: loadedConfig.Server, }, - Jid: loadedConfig.Username + "/lambda." + str, - Credential: xmpp.Password(loadedConfig.Password), - Insecure: loadedConfig.Insecure, + Jid: loadedConfig.Username + "/lambda." + str, + Credential: xmpp.Password(loadedConfig.Password), + Insecure: loadedConfig.Insecure, StreamLogger: os.Stdout, } router := xmpp.NewRouter() @@ -219,7 +231,6 @@ func main() { {Var: "jabber:iq:version"}, {Var: "urn:xmpp:delegation:1"}, {Var: "http://jabber.org/protocol/muc"}, - {Var: "urn:xmpp:reply:0"}, {Var: "λ"}, }, } @@ -252,6 +263,8 @@ func main() { return } + pretty.Println(m) + e := stanza.PubSubEvent{} ok = m.Get(&e) if ok { @@ -354,7 +367,7 @@ func main() { _, mok := mucmembers.Load(user) if !ok && !mok { // FIXME: The initial muc presence gets picked up from this check ok := createTab(user, false) - if ok && !lockedJIDs[user]{ + if ok && !lockedJIDs[user] { userdevices.Store(user, userUnit{}) b := gtk.NewButtonWithLabel(user) @@ -476,7 +489,7 @@ func activate(app *gtk.Application) { box.Append(btn) win := gtk.NewWindow() - win.SetTitle("Join MUC") + win.SetTitle("Join MUC") win.SetDefaultSize(400, 1) win.SetChild(box) @@ -510,7 +523,6 @@ func activate(app *gtk.Application) { the_menu.AppendSubmenu("File", fileMenu) the_menu.AppendSubmenu("Help", helpMenu) - the_menuBar := gtk.NewPopoverMenuBarFromModel(the_menu) app.SetMenubar(gio.NewMenu()) diff --git a/style.css b/style.css index 0cc414a..c0c4475 100644 --- a/style.css +++ b/style.css @@ -33,3 +33,9 @@ .visitor { color: grey; } + +.hat { + background-color: orange; + color: black; + font-family: monospace; +} diff --git a/xmpp-reply.go b/xmpp-reply.go deleted file mode 100644 index fc19f60..0000000 --- a/xmpp-reply.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -// Implementation of XEP-0461 -// https://xmpp.org/extensions/xep-0461.html#business-id - -import ( - "encoding/xml" - "gosrc.io/xmpp/stanza" -) - -type Reply struct { - stanza.MsgExtension - XMLName xml.Name `xml:"urn:xmpp:reply:0 reply"` - To string `xml:"to,attr"` - ID string `xml:"id,attr"` -} - -func init() { - stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:reply:0", Local: "reply"}, Reply{}) -}