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))
|
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 := 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)))
|
headerBox.Append(gtk.NewLabel(fmt.Sprintf("%d participant(s)", i)))
|
||||||
gen.Prepend(headerBox)
|
gen.Prepend(headerBox)
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
||||||
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
presence, ok := p.(stanza.Presence)
|
presence, ok := p.(stanza.Presence)
|
||||||
if !ok {
|
if !ok {
|
||||||
return gtk.NewLabel("Unsupported message.")
|
return gtk.NewLabel("Unsupported message.")
|
||||||
@@ -29,14 +30,21 @@ func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
ok := presence.Get(&mu)
|
ok := presence.Get(&mu)
|
||||||
if ok {
|
if ok {
|
||||||
if mu.MucUserItem.Affiliation == "outcast" {
|
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 {
|
} 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 {
|
func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||||
@@ -65,7 +73,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
if m.Error.Type != "" {
|
if m.Error.Type != "" {
|
||||||
error_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
error_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
cancel_img := gtk.NewImageFromPaintable(clientAssets["cancel"])
|
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(cancel_img)
|
||||||
error_box.Append(error_label)
|
error_box.Append(error_label)
|
||||||
@@ -147,7 +155,11 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
|
|
||||||
// authorBox.Append(im)
|
// 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.AddCSSClass("author")
|
||||||
al.SetSelectable(true)
|
al.SetSelectable(true)
|
||||||
|
|
||||||
@@ -195,6 +207,12 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
mlabel.SetSelectable(true)
|
mlabel.SetSelectable(true)
|
||||||
mlabel.SetHAlign(gtk.AlignFill)
|
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)
|
contentBox.Append(mlabel)
|
||||||
|
|
||||||
mainBox.Append(authorBox)
|
mainBox.Append(authorBox)
|
||||||
|
|||||||
107
main.go
107
main.go
@@ -90,7 +90,6 @@ var cancelB64 string = base64.StdEncoding.EncodeToString(cancelBytes)
|
|||||||
var tagBytes []byte
|
var tagBytes []byte
|
||||||
var tagB64 string = base64.StdEncoding.EncodeToString(tagBytes)
|
var tagB64 string = base64.StdEncoding.EncodeToString(tagBytes)
|
||||||
|
|
||||||
|
|
||||||
//go:embed assets/lambda-disabled.png
|
//go:embed assets/lambda-disabled.png
|
||||||
var logoDisabledBytes []byte
|
var logoDisabledBytes []byte
|
||||||
var logoDisabledB64 string = base64.StdEncoding.EncodeToString(logoDisabledBytes)
|
var logoDisabledB64 string = base64.StdEncoding.EncodeToString(logoDisabledBytes)
|
||||||
@@ -99,10 +98,28 @@ var logoDisabledB64 string = base64.StdEncoding.EncodeToString(logoDisabledBytes
|
|||||||
var groupBytes []byte
|
var groupBytes []byte
|
||||||
var groupB64 string = base64.StdEncoding.EncodeToString(groupBytes)
|
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 clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
|
||||||
var lockedJIDs map[string]bool = make(map[string]bool)
|
var lockedJIDs map[string]bool = make(map[string]bool)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
beeep.AppName = "Lambda"
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for fn := range uiQueue {
|
for fn := range uiQueue {
|
||||||
glib.IdleAdd(func() bool {
|
glib.IdleAdd(func() bool {
|
||||||
@@ -176,7 +193,6 @@ func init() {
|
|||||||
|
|
||||||
clientAssets["outcast"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
clientAssets["outcast"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
disabledLogoData, _ := base64.StdEncoding.DecodeString(logoDisabledB64)
|
disabledLogoData, _ := base64.StdEncoding.DecodeString(logoDisabledB64)
|
||||||
@@ -192,6 +208,38 @@ func init() {
|
|||||||
loader.Close()
|
loader.Close()
|
||||||
|
|
||||||
clientAssets["group"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
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() {
|
func main() {
|
||||||
@@ -368,8 +416,8 @@ func main() {
|
|||||||
_, ok := typed_unit.Members.Load(id)
|
_, ok := typed_unit.Members.Load(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
glib.IdleAdd(func() {
|
glib.IdleAdd(func() {
|
||||||
b := gtk.NewLabel("")
|
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
ba, ok := generatePresenceWidget(p).(*gtk.Label)
|
ba, ok := generatePresenceWidget(p).(*gtk.Box)
|
||||||
if ok {
|
if ok {
|
||||||
b = ba
|
b = ba
|
||||||
}
|
}
|
||||||
@@ -389,8 +437,8 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
typed_unit.Members.Delete(id)
|
typed_unit.Members.Delete(id)
|
||||||
glib.IdleAdd(func() {
|
glib.IdleAdd(func() {
|
||||||
b := gtk.NewLabel("")
|
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
ba, ok := generatePresenceWidget(p).(*gtk.Label)
|
ba, ok := generatePresenceWidget(p).(*gtk.Box)
|
||||||
if ok {
|
if ok {
|
||||||
b = ba
|
b = ba
|
||||||
}
|
}
|
||||||
@@ -462,8 +510,49 @@ func main() {
|
|||||||
|
|
||||||
cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) {
|
cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) {
|
||||||
fmt.Println("XMPP client connected")
|
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() {
|
go func() {
|
||||||
@@ -509,7 +598,7 @@ func activate(app *gtk.Application) {
|
|||||||
|
|
||||||
destroymucAction := gio.NewSimpleAction("destroymuc", nil)
|
destroymucAction := gio.NewSimpleAction("destroymuc", nil)
|
||||||
destroymucAction.ConnectActivate(func(p *glib.Variant) {
|
destroymucAction.ConnectActivate(func(p *glib.Variant) {
|
||||||
cur, ok := tabs.Load(current)
|
cur, ok := tabs.Load(current)
|
||||||
if ok {
|
if ok {
|
||||||
cur := cur.(*chatTab)
|
cur := cur.(*chatTab)
|
||||||
if cur.isMuc {
|
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 {
|
type MucUser struct {
|
||||||
stanza.PresExtension
|
stanza.PresExtension
|
||||||
|
stanza.MsgExtension
|
||||||
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
||||||
MucUserItem MucUserItem `xml:"item,omitempty"`
|
MucUserItem MucUserItem `xml:"item,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -19,9 +20,16 @@ type MucUserItem struct {
|
|||||||
Affiliation string `xml:"affiliation,attr,omitempty"` // TODO: Use enum
|
Affiliation string `xml:"affiliation,attr,omitempty"` // TODO: Use enum
|
||||||
Role string `xml:"role,attr,omitempty"` // TODO: Use enum
|
Role string `xml:"role,attr,omitempty"` // TODO: Use enum
|
||||||
JID string `xml:"jid,attr,omitempty"`
|
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() {
|
func init() {
|
||||||
stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{Space: "http://jabber.org/protocol/muc#user", Local: "x"}, MucUser{})
|
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