BOOKMARKS! BOOKMARKS! WE GOT BOOKMARKS PEOPLE
This commit is contained in:
@@ -4,4 +4,4 @@ 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)
|
||||
additional icons are by Mark James's Silk Icon Set [https://github.com/markjames/famfamfam-silk-icons](https://github.com/markjames/famfamfam-silk-icons)
|
||||
|
||||
BIN
assets/ban.png
Normal file
BIN
assets/ban.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 762 B |
BIN
assets/door_in.png
Normal file
BIN
assets/door_in.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 693 B |
BIN
assets/door_out.png
Normal file
BIN
assets/door_out.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 643 B |
BIN
assets/large_group.png
Normal file
BIN
assets/large_group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 747 B |
BIN
assets/world.png
Normal file
BIN
assets/world.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 923 B |
@@ -253,7 +253,13 @@ func switchToTab(jid string, w *gtk.Window) {
|
||||
})
|
||||
|
||||
headerBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["group"]))
|
||||
if i >= 500 {
|
||||
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["world"]))
|
||||
} else if i >= 50 {
|
||||
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["large_group"]))
|
||||
} else {
|
||||
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["group"]))
|
||||
}
|
||||
headerBox.Append(gtk.NewLabel(fmt.Sprintf("%d participant(s)", i)))
|
||||
gen.Prepend(headerBox)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
)
|
||||
|
||||
func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
||||
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
presence, ok := p.(stanza.Presence)
|
||||
if !ok {
|
||||
return gtk.NewLabel("Unsupported message.")
|
||||
@@ -29,14 +30,21 @@ func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
||||
ok := presence.Get(&mu)
|
||||
if ok {
|
||||
if mu.MucUserItem.Affiliation == "outcast" {
|
||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " has been banned!")
|
||||
b.Append(gtk.NewImageFromPaintable(clientAssets["outcast"]))
|
||||
b.Append(gtk.NewLabel(JidMustParse(presence.From).Resource + " has been banned by " + mu.MucUserItem.Actor.Nick + "!"))
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
return gtk.NewLabel(JidMustParse(presence.From).Resource + " left the MUC")
|
||||
b.Append(gtk.NewImageFromPaintable(clientAssets["door_out"]))
|
||||
b.Append(gtk.NewLabel(JidMustParse(presence.From).Resource))
|
||||
} else {
|
||||
return gtk.NewLabel(JidMustParse(presence.From).Resource + " joined the MUC")
|
||||
b.Append(gtk.NewImageFromPaintable(clientAssets["door_in"]))
|
||||
b.Append(gtk.NewLabel(JidMustParse(presence.From).Resource))
|
||||
}
|
||||
|
||||
b.SetTooltipText(presence.Status)
|
||||
return b
|
||||
}
|
||||
|
||||
func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
@@ -65,7 +73,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
if m.Error.Type != "" {
|
||||
error_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
cancel_img := gtk.NewImageFromPaintable(clientAssets["cancel"])
|
||||
error_label := gtk.NewLabel(m.Error.Text+ ": ")
|
||||
error_label := gtk.NewLabel(m.Error.Text + ": ")
|
||||
|
||||
error_box.Append(cancel_img)
|
||||
error_box.Append(error_label)
|
||||
@@ -147,7 +155,11 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
|
||||
// authorBox.Append(im)
|
||||
|
||||
al := gtk.NewLabel(jid.MustParse(m.From).Resourcepart())
|
||||
n := jid.MustParse(m.From).Resourcepart()
|
||||
if n == "" {
|
||||
n = jid.MustParse(m.From).String()
|
||||
}
|
||||
al := gtk.NewLabel(n)
|
||||
al.AddCSSClass("author")
|
||||
al.SetSelectable(true)
|
||||
|
||||
@@ -195,6 +207,12 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
mlabel.SetSelectable(true)
|
||||
mlabel.SetHAlign(gtk.AlignFill)
|
||||
|
||||
mum := MucUser{}
|
||||
ok = m.Get(&mum)
|
||||
if ok {
|
||||
mlabel.SetText(fmt.Sprintf("%s's affiliation has been changed to %s", mum.MucUserItem.JID, mum.MucUserItem.Affiliation))
|
||||
}
|
||||
|
||||
contentBox.Append(mlabel)
|
||||
|
||||
mainBox.Append(authorBox)
|
||||
|
||||
107
main.go
107
main.go
@@ -90,7 +90,6 @@ var cancelB64 string = base64.StdEncoding.EncodeToString(cancelBytes)
|
||||
var tagBytes []byte
|
||||
var tagB64 string = base64.StdEncoding.EncodeToString(tagBytes)
|
||||
|
||||
|
||||
//go:embed assets/lambda-disabled.png
|
||||
var logoDisabledBytes []byte
|
||||
var logoDisabledB64 string = base64.StdEncoding.EncodeToString(logoDisabledBytes)
|
||||
@@ -99,10 +98,28 @@ var logoDisabledB64 string = base64.StdEncoding.EncodeToString(logoDisabledBytes
|
||||
var groupBytes []byte
|
||||
var groupB64 string = base64.StdEncoding.EncodeToString(groupBytes)
|
||||
|
||||
//go:embed assets/door_in.png
|
||||
var doorInBytes []byte
|
||||
var doorInB64 string = base64.StdEncoding.EncodeToString(doorInBytes)
|
||||
|
||||
//go:embed assets/door_out.png
|
||||
var doorOutBytes []byte
|
||||
var doorOutB64 string = base64.StdEncoding.EncodeToString(doorOutBytes)
|
||||
|
||||
//go:embed assets/large_group.png
|
||||
var largeGroupBytes []byte
|
||||
var largeGroupB64 string = base64.StdEncoding.EncodeToString(largeGroupBytes)
|
||||
|
||||
//go:embed assets/world.png
|
||||
var worldBytes []byte
|
||||
var worldB64 string = base64.StdEncoding.EncodeToString(worldBytes)
|
||||
|
||||
var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
|
||||
var lockedJIDs map[string]bool = make(map[string]bool)
|
||||
|
||||
func init() {
|
||||
beeep.AppName = "Lambda"
|
||||
|
||||
go func() {
|
||||
for fn := range uiQueue {
|
||||
glib.IdleAdd(func() bool {
|
||||
@@ -176,7 +193,6 @@ func init() {
|
||||
|
||||
clientAssets["outcast"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
disabledLogoData, _ := base64.StdEncoding.DecodeString(logoDisabledB64)
|
||||
@@ -192,6 +208,38 @@ func init() {
|
||||
loader.Close()
|
||||
|
||||
clientAssets["group"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
doorInData, _ := base64.StdEncoding.DecodeString(doorInB64)
|
||||
loader.Write(doorInData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["door_in"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
doorOutData, _ := base64.StdEncoding.DecodeString(doorOutB64)
|
||||
loader.Write(doorOutData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["door_out"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
largeGroupData, _ := base64.StdEncoding.DecodeString(largeGroupB64)
|
||||
loader.Write(largeGroupData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["large_group"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
worldData, _ := base64.StdEncoding.DecodeString(worldB64)
|
||||
loader.Write(worldData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["world"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -368,8 +416,8 @@ func main() {
|
||||
_, ok := typed_unit.Members.Load(id)
|
||||
if !ok {
|
||||
glib.IdleAdd(func() {
|
||||
b := gtk.NewLabel("")
|
||||
ba, ok := generatePresenceWidget(p).(*gtk.Label)
|
||||
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
ba, ok := generatePresenceWidget(p).(*gtk.Box)
|
||||
if ok {
|
||||
b = ba
|
||||
}
|
||||
@@ -389,8 +437,8 @@ func main() {
|
||||
} else {
|
||||
typed_unit.Members.Delete(id)
|
||||
glib.IdleAdd(func() {
|
||||
b := gtk.NewLabel("")
|
||||
ba, ok := generatePresenceWidget(p).(*gtk.Label)
|
||||
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
ba, ok := generatePresenceWidget(p).(*gtk.Box)
|
||||
if ok {
|
||||
b = ba
|
||||
}
|
||||
@@ -462,8 +510,49 @@ func main() {
|
||||
|
||||
cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) {
|
||||
fmt.Println("XMPP client connected")
|
||||
/*
|
||||
*/
|
||||
books, err := stanza.NewItemsRequest("", "urn:xmpp:bookmarks:1", 0)
|
||||
if err == nil {
|
||||
mychan, err := c.SendIQ(context.TODO(), books)
|
||||
result := <-mychan
|
||||
if err == nil {
|
||||
res, ok := result.Payload.(*stanza.PubSubGeneric)
|
||||
if ok {
|
||||
for _, item := range res.Items.List {
|
||||
jid := item.Id
|
||||
node := item.Any
|
||||
autojoin := false
|
||||
nick := loadedConfig.Nick
|
||||
for _, attr := range node.Attrs {
|
||||
if attr.Name.Local == "autojoin" {
|
||||
autojoin = attr.Value == "true"
|
||||
}
|
||||
}
|
||||
|
||||
for _, node := range node.Nodes {
|
||||
if node.XMLName.Local == "nick" {
|
||||
nick = node.Content
|
||||
}
|
||||
}
|
||||
|
||||
_, ok := tabs.Load(jid)
|
||||
if !ok && autojoin {
|
||||
err := joinMuc(client, clientroot.Session.BindJid, jid, nick)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
createTab(jid, true)
|
||||
b := gtk.NewButtonWithLabel(jid)
|
||||
b.ConnectClicked(func() {
|
||||
b.AddCSSClass("accent")
|
||||
switchToTab(jid, &window.Window)
|
||||
})
|
||||
menu.Append(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
go func() {
|
||||
@@ -509,7 +598,7 @@ func activate(app *gtk.Application) {
|
||||
|
||||
destroymucAction := gio.NewSimpleAction("destroymuc", nil)
|
||||
destroymucAction.ConnectActivate(func(p *glib.Variant) {
|
||||
cur, ok := tabs.Load(current)
|
||||
cur, ok := tabs.Load(current)
|
||||
if ok {
|
||||
cur := cur.(*chatTab)
|
||||
if cur.isMuc {
|
||||
|
||||
22
xmpp-bookmarks.go
Normal file
22
xmpp-bookmarks.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
)
|
||||
|
||||
// Implementation of XEP-0402: PEP Native Bookmarks
|
||||
// https://xmpp.org/extensions/xep-0402.html
|
||||
|
||||
type Bookmark struct {
|
||||
XMLName xml.Name `xml:"urn:xmpp:bookmarks:1 conference"`
|
||||
Name string `xml:"name,attr,omitempty"`
|
||||
Autojoin bool `xml:"autojoin,attr,omitempty"`
|
||||
Nick string `xml:"nick,omitempty"`
|
||||
Password string `xml:"password,omitempty"`
|
||||
Extensions []any `xml:"extensions,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{Space: "urn:xmpp:bookmarks:1", Local: "conference"}, Bookmark{})
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
type MucUser struct {
|
||||
stanza.PresExtension
|
||||
stanza.MsgExtension
|
||||
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
||||
MucUserItem MucUserItem `xml:"item,omitempty"`
|
||||
}
|
||||
@@ -19,9 +20,16 @@ type MucUserItem struct {
|
||||
Affiliation string `xml:"affiliation,attr,omitempty"` // TODO: Use enum
|
||||
Role string `xml:"role,attr,omitempty"` // TODO: Use enum
|
||||
JID string `xml:"jid,attr,omitempty"`
|
||||
Reason string `xml:"reason,omitempty"`
|
||||
Reason string `xml:"reason,omitempty"`
|
||||
Actor Actor `xml:"actor,omitempty"`
|
||||
}
|
||||
|
||||
type Actor struct {
|
||||
JID string `xml:"jid,attr"`
|
||||
Nick string `xml:"nick,attr"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{Space: "http://jabber.org/protocol/muc#user", Local: "x"}, MucUser{})
|
||||
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "http://jabber.org/protocol/muc#user", Local: "x"}, MucUser{})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user