diff --git a/assets/admin.png b/assets/admin.png new file mode 100644 index 0000000..2034f84 Binary files /dev/null and b/assets/admin.png differ diff --git a/assets/member.png b/assets/member.png new file mode 100644 index 0000000..2bb1594 Binary files /dev/null and b/assets/member.png differ diff --git a/assets/noaffiliation.png b/assets/noaffiliation.png new file mode 100644 index 0000000..8b13e4b Binary files /dev/null and b/assets/noaffiliation.png differ diff --git a/assets/outcast.png b/assets/outcast.png new file mode 100644 index 0000000..e1fb0b7 Binary files /dev/null and b/assets/outcast.png differ diff --git a/assets/owner.png b/assets/owner.png new file mode 100644 index 0000000..1569659 Binary files /dev/null and b/assets/owner.png differ 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{}) }