diff --git a/assets.go b/assets.go
index a737c0f..16cbfa9 100644
--- a/assets.go
+++ b/assets.go
@@ -39,6 +39,27 @@ var outcastMedalB64 string = base64.StdEncoding.EncodeToString(outcastMedalBytes
var cancelBytes []byte
var cancelB64 string = base64.StdEncoding.EncodeToString(cancelBytes)
+
+//go:embed assets/status_away.png
+var sABytes []byte
+var sAB64 string = base64.StdEncoding.EncodeToString(sABytes)
+
+//go:embed assets/status_busy.png
+var sBBytes []byte
+var sBB64 string = base64.StdEncoding.EncodeToString(sBBytes)
+
+//go:embed assets/status_chatty.png
+var sCBytes []byte
+var sCB64 string = base64.StdEncoding.EncodeToString(sCBytes)
+
+//go:embed assets/status_online.png
+var sOBytes []byte
+var sOB64 string = base64.StdEncoding.EncodeToString(sOBytes)
+
+//go:embed assets/status_xa.png
+var xaBytes []byte
+var xaB64 string = base64.StdEncoding.EncodeToString(xaBytes)
+
//go:embed assets/tag.png
var tagBytes []byte
var tagB64 string = base64.StdEncoding.EncodeToString(tagBytes)
@@ -104,7 +125,6 @@ func init() {
loader.Close()
clientAssets["DefaultAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
-
loader = gdkpixbuf.NewPixbufLoader()
failedData, _ := base64.StdEncoding.DecodeString(failedB64)
@@ -272,4 +292,46 @@ func init() {
loader.Close()
clientAssets["information"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
+
+ loader = gdkpixbuf.NewPixbufLoader()
+
+ sAData, _ := base64.StdEncoding.DecodeString(sAB64)
+ loader.Write(sAData)
+ loader.Close()
+
+ clientAssets["status_away"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
+
+
+ loader = gdkpixbuf.NewPixbufLoader()
+
+ sBData, _ := base64.StdEncoding.DecodeString(sBB64)
+ loader.Write(sBData)
+ loader.Close()
+
+ clientAssets["status_dnd"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
+
+ loader = gdkpixbuf.NewPixbufLoader()
+
+ sCData, _ := base64.StdEncoding.DecodeString(sCB64)
+ loader.Write(sCData)
+ loader.Close()
+
+ clientAssets["status_chat"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
+
+ loader = gdkpixbuf.NewPixbufLoader()
+
+ xaData, _ := base64.StdEncoding.DecodeString(xaB64)
+ loader.Write(xaData)
+ loader.Close()
+
+ clientAssets["status_xa"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
+
+
+ loader = gdkpixbuf.NewPixbufLoader()
+
+ sOData, _ := base64.StdEncoding.DecodeString(sOB64)
+ loader.Write(sOData)
+ loader.Close()
+
+ clientAssets["status_"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
}
diff --git a/assets/status_away.png b/assets/status_away.png
new file mode 100644
index 0000000..70bcbcc
Binary files /dev/null and b/assets/status_away.png differ
diff --git a/assets/status_busy.png b/assets/status_busy.png
new file mode 100644
index 0000000..987c806
Binary files /dev/null and b/assets/status_busy.png differ
diff --git a/assets/status_chatty.png b/assets/status_chatty.png
new file mode 100644
index 0000000..ed631fb
Binary files /dev/null and b/assets/status_chatty.png differ
diff --git a/assets/status_online.png b/assets/status_online.png
new file mode 100644
index 0000000..947bd4b
Binary files /dev/null and b/assets/status_online.png differ
diff --git a/assets/status_xa.png b/assets/status_xa.png
new file mode 100644
index 0000000..0e3768f
Binary files /dev/null and b/assets/status_xa.png differ
diff --git a/cache.go b/cache.go
index 267ca68..73ff099 100644
--- a/cache.go
+++ b/cache.go
@@ -19,7 +19,7 @@ import (
var textureCache = make(map[string]gdk.Paintabler)
// Invalid images, if an image/avatar cannot be loaded on the system (e.g: incompatible format) it's put here
-var invalidImages = make(map[string]bool)
+var invalidImages = make(map[string]bool)
func ensureCache() (string, error) {
cachePath := configdir.LocalCache("lambda-im")
diff --git a/failed_load.png b/failed_load.png
index 291626c..24a42d3 100644
Binary files a/failed_load.png and b/failed_load.png differ
diff --git a/gtk-helpers.go b/gtk-helpers.go
index f6b5e11..033ebbb 100644
--- a/gtk-helpers.go
+++ b/gtk-helpers.go
@@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
+ "github.com/diamondburned/gotk4/pkg/gdk/v4"
"github.com/diamondburned/gotk4/pkg/glib/v2"
"github.com/diamondburned/gotk4/pkg/gtk/v4"
"github.com/diamondburned/gotk4/pkg/pango"
@@ -82,7 +83,7 @@ func switchToTab(jid string, w *gtk.Window) {
nick_label.SetOpacity(0.5)
}
- userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\nRight-click for more information", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation))
+ userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\nClick for more information", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation))
userbox.Append(nick_label)
var hats Hats
@@ -95,6 +96,13 @@ func switchToTab(jid string, w *gtk.Window) {
}
}
+ status := gtk.NewImageFromPaintable(clientAssets["status_"+string(u.Show)])
+ status.SetTooltipText(string(u.Show))
+
+ status.SetHAlign(gtk.AlignEnd)
+ // medal.SetHExpand(true)
+ userbox.Prepend(status)
+
medal := gtk.NewImageFromPaintable(clientAssets[mu.MucUserItem.Affiliation])
medal.SetTooltipText(mu.MucUserItem.Affiliation)
@@ -103,7 +111,145 @@ func switchToTab(jid string, w *gtk.Window) {
userbox.Append(medal)
gesture := gtk.NewGestureClick()
- gesture.SetButton(3) // Right click
+ gesture.SetButton(1)
+
+ mod_gesture := gtk.NewGestureClick()
+ mod_gesture.SetButton(3)
+
+ popover := gtk.NewPopover()
+ popover.SetHasArrow(false)
+ popover.SetParent(userbox)
+
+ rc_box := gtk.NewBox(gtk.OrientationVertical, 0)
+ bb := gtk.NewButtonWithLabel("Ban")
+ kb := gtk.NewButtonWithLabel("Kick")
+ ab := gtk.NewButtonWithLabel("Set affil")
+ rb := gtk.NewButtonWithLabel("Set role")
+
+ kb.ConnectClicked(func() {
+ client.SendRaw(fmt.Sprintf(`
+
+
+ -
+
+
+
+ `, clientroot.Session.BindJid, jid, JidMustParse(u.From).Resource))
+ })
+
+ bb.ConnectClicked(func() {
+ var mu MucUser
+ ok = u.Get(&mu)
+ if ok {
+ if mu.MucUserItem.JID != "" {
+ client.SendRaw(fmt.Sprintf(`
+
+
+
+
+
+ `, clientroot.Session.BindJid, jid, JidMustParse(mu.MucUserItem.JID).Bare()))
+ }
+ }
+ })
+
+ ab.ConnectClicked(func() {
+ var mu MucUser
+ ok = u.Get(&mu)
+ if ok {
+ if mu.MucUserItem.JID != "" {
+ win := gtk.NewWindow()
+ win.SetDefaultSize(400, 1)
+ win.SetResizable(false)
+
+ box := gtk.NewBox(gtk.OrientationVertical, 0)
+ box.Append(gtk.NewLabel("Set "+JidMustParse(u.From).Resource+"'s affiliation"))
+
+ the_entry := gtk.NewEntry()
+ the_entry.SetText(mu.MucUserItem.Affiliation)
+
+ submit := gtk.NewButtonWithLabel("Submit")
+ submit.ConnectClicked(func() {
+ client.SendRaw(fmt.Sprintf(`
+
+
+
+
+
+ `, clientroot.Session.BindJid, jid, the_entry.Text(), JidMustParse(mu.MucUserItem.JID).Bare()))
+ win.SetVisible(false)
+ })
+
+ box.Append(the_entry)
+ box.Append(submit)
+
+ win.SetChild(box)
+ win.SetVisible(true)
+ }}
+ })
+
+
+ rb.ConnectClicked(func() {
+ var mu MucUser
+ ok = u.Get(&mu)
+ if ok {
+ if mu.MucUserItem.JID != "" {
+ win := gtk.NewWindow()
+ win.SetDefaultSize(400, 1)
+ win.SetResizable(false)
+
+ box := gtk.NewBox(gtk.OrientationVertical, 0)
+ box.Append(gtk.NewLabel("Set "+JidMustParse(u.From).Resource+"'s role"))
+ box.Append(gtk.NewLabel("Important: if you want this to be permanent, set their affiliation instead"))
+
+ the_entry := gtk.NewEntry()
+ the_entry.SetText(mu.MucUserItem.Role)
+
+ submit := gtk.NewButtonWithLabel("Submit")
+ submit.ConnectClicked(func() {
+
+ client.SendRaw(fmt.Sprintf(`
+
+
+ -
+
+
+
+ `, clientroot.Session.BindJid, jid, JidMustParse(u.From).Resource, the_entry.Text()))
+ })
+
+ box.Append(the_entry)
+ box.Append(submit)
+
+ win.SetChild(box)
+ win.SetVisible(true)
+ }}
+ })
+
+ rc_box.Append(bb)
+ rc_box.Append(kb)
+ rc_box.Append(ab)
+ rc_box.Append(rb)
+
+ popover.SetChild(rc_box)
+
+ mod_gesture.Connect("pressed", func(n_press, x, y int) {
+ rect := gdk.NewRectangle(x, y, 1, 1)
+ popover.SetPointingTo(&rect)
+ popover.Popup()
+ })
gesture.Connect("pressed", func(n_press, x, y int) {
win := gtk.NewWindow()
@@ -218,8 +364,13 @@ func switchToTab(jid string, w *gtk.Window) {
version := ver.Version
os := ver.OS
- ver_text.SetText(fmt.Sprintf("%s %s %s", name, version, os))
- ver_text.RemoveCSSClass("visitor")
+ vr := fmt.Sprintf("%s %s %s", name, version, os)
+ if name == "" && version == "" && os == "" {
+ ver_text.SetText("Client responded with empty version")
+ } else {
+ ver_text.SetText(vr)
+ ver_text.RemoveCSSClass("visitor")
+ }
} else if result.Error != nil && result.Error.Type != "" {
ver_text.SetText("Got error trying to get version")
ver_text.SetTooltipText(result.Error.Reason + ": " + result.Error.Text)
@@ -262,7 +413,10 @@ func switchToTab(jid string, w *gtk.Window) {
win.SetTransientFor(win)
win.Present()
})
+
userbox.AddController(gesture)
+ userbox.AddController(mod_gesture)
+
if mu.MucUserItem.Role == "moderator" {
gen.Prepend(userbox)
} else {
diff --git a/main.go b/main.go
index 7c5401c..b285cd5 100644
--- a/main.go
+++ b/main.go
@@ -1,8 +1,8 @@
package main
import (
- "strings"
"os"
+ "strings"
"sync"
"context"
@@ -188,8 +188,6 @@ func main() {
return
}
- pretty.Println(m)
-
e := stanza.PubSubEvent{}
ok = m.Get(&e)
if ok {
@@ -211,6 +209,15 @@ func main() {
beeep.Notify("Attention", fmt.Sprintf("%s: %s", JidMustParse(m.From).Resource, m.Body), commentBytes) // TODO: Use localpart if DM
}
+ // Handle mentions
+ for _, ext := range m.Extensions {
+ mention, ok := ext.(*Mention)
+ if ok {
+ pretty.Println(mention)
+ }
+
+ }
+
sc := new(SentCarbon)
ok = m.Get(sc)
if ok {
@@ -492,7 +499,7 @@ func main() {
}
})
- go func() {
+ conc := func() {
time.Sleep(3 * time.Second)
connectionStatus.SetText("Connecting...")
connectionIcon.SetFromPaintable(clientAssets["hourglass"])
@@ -503,10 +510,13 @@ func main() {
connectionStatus.SetText(fmt.Sprintf("Disconnected: %s", err.Error()))
connectionIcon.SetFromPaintable(clientAssets["disconnect"])
}
- }()
+ }
app := gtk.NewApplication("net.sunglocto.lambda", gio.ApplicationFlagsNone)
- app.ConnectActivate(func() { activate(app) })
+ app.ConnectActivate(func() {
+ go conc()
+ activate(app)
+ })
if code := app.Run(os.Args); code > 0 {
os.Exit(code)
@@ -870,8 +880,15 @@ func activate(app *gtk.Application) {
}
if strings.Contains(t, "@everyone") {
+ start := strings.Index(t, "@everyone")
+ end := start + len("@everyone")
+
new_mention := new(Mention)
- new_mention.Mentions = "urn:xmpp:mentions:0#channel"
+ new_mention.Type = "urn:xmpp:mentions:0#channel"
+
+ new_mention.Begin = start
+ new_mention.End = end
+
exts = append(exts, new_mention)
} else if strings.Contains(t, "@here") {
new_attention := new(Attention)
diff --git a/types.go b/types.go
index f4204d6..4500488 100644
--- a/types.go
+++ b/types.go
@@ -2,6 +2,7 @@ package main
import (
"github.com/diamondburned/gotk4/pkg/gtk/v4"
+ "mellium.im/xmpp/color"
"sync"
)
@@ -18,10 +19,11 @@ type lambdaConfig struct {
Insecure bool
Nick string
JoinBookmarks bool
+ CVD color.CVD
}
type mucUnit struct {
- // key: OccupantID
+ // key: Resource
// value: last user presence
Members sync.Map
}
diff --git a/version.go b/version.go
index 069a1a6..a838089 100644
--- a/version.go
+++ b/version.go
@@ -1,3 +1,3 @@
package main
-var lambda_version string = "0.1.0"
+var lambda_version string = "26w11a"
diff --git a/xmpp-mentions.go b/xmpp-mentions.go
index 7344a18..29dde9e 100644
--- a/xmpp-mentions.go
+++ b/xmpp-mentions.go
@@ -10,13 +10,12 @@ import (
type Mention struct {
stanza.MsgExtension
- XMLName xml.Name `xml:"urn:xmpp:mentions:0 mention"`
- Mentions string `xml:"mentions,attr,omitempty"`
- URI string `xml:"uri,attr,omitempty"`
- Begin int `xml:"begin,attr,omitempty"`
- End int `xml:"end,attr,omitempty"`
- OccupantID string `xml:"occupantid,attr,omitempty"`
- JID string `xml:"ji,attr,omitempty"`
+ XMLName xml.Name `xml:"urn:xmpp:mentions:0 mention"`
+ URI string `xml:"uri,attr,omitempty"`
+ Begin int `xml:"begin,attr,omitempty"`
+ End int `xml:"end,attr,omitempty"`
+ Type string `xml:"type,attr"`
+ Target string `xml:"target,attr,omitempty"`
}
func init() {