Compare commits
10 Commits
63bb323ac3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6626d35920 | |||
| 5c76729a6b | |||
| c260b8b231 | |||
| 971147dcb8 | |||
| 777df725b6 | |||
| 6cb8771994 | |||
| ac013e7969 | |||
| 067a74e157 | |||
| 1dcd55d5ff | |||
| e87369912d |
BIN
assets/admin.png
Normal file
|
After Width: | Height: | Size: 600 B |
BIN
assets/cancel.png
Normal file
|
After Width: | Height: | Size: 607 B |
BIN
assets/icon.ico
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
assets/icon.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/member.png
Normal file
|
After Width: | Height: | Size: 597 B |
BIN
assets/noaffiliation.png
Normal file
|
After Width: | Height: | Size: 591 B |
BIN
assets/outcast.png
Normal file
|
After Width: | Height: | Size: 538 B |
BIN
assets/owner.png
Normal file
|
After Width: | Height: | Size: 584 B |
@@ -5,8 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/pango"
|
||||||
"gosrc.io/xmpp/stanza"
|
"gosrc.io/xmpp/stanza"
|
||||||
Jid "mellium.im/xmpp/jid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
||||||
@@ -17,10 +17,12 @@ func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTab(jid string, isMuc bool) {
|
func createTab(jid string, isMuc bool) bool {
|
||||||
fmt.Println("Creating tab", jid, "isMuc:", isMuc)
|
fmt.Println("Creating tab", jid, "isMuc:", isMuc)
|
||||||
_, ok := tabs.Load(jid)
|
_, ok := tabs.Load(jid)
|
||||||
if !ok {
|
_, uok := userdevices.Load(jid)
|
||||||
|
_, mok := mucmembers.Load(jid)
|
||||||
|
if !ok && !uok && !mok {
|
||||||
newTab := new(chatTab)
|
newTab := new(chatTab)
|
||||||
newTab.isMuc = isMuc
|
newTab.isMuc = isMuc
|
||||||
newTab.msgs = gtk.NewListBox()
|
newTab.msgs = gtk.NewListBox()
|
||||||
@@ -29,7 +31,10 @@ func createTab(jid string, isMuc bool) {
|
|||||||
|
|
||||||
newTab.msgs.Append(gtk.NewButtonWithLabel("Get past messages..."))
|
newTab.msgs.Append(gtk.NewButtonWithLabel("Get past messages..."))
|
||||||
tabs.Store(jid, newTab)
|
tabs.Store(jid, newTab)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func switchToTab(jid string, w *gtk.Window) {
|
func switchToTab(jid string, w *gtk.Window) {
|
||||||
@@ -43,8 +48,14 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
|
|
||||||
scroller.SetChild(typed_tab.msgs)
|
scroller.SetChild(typed_tab.msgs)
|
||||||
if typed_tab.isMuc {
|
if typed_tab.isMuc {
|
||||||
m, _ := mucmembers.Load(jid)
|
m, ok := mucmembers.Load(jid)
|
||||||
ma := m.(mucUnit)
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ma, ok := m.(mucUnit)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
mm := ma.Members
|
mm := ma.Members
|
||||||
gen := gtk.NewBox(gtk.OrientationVertical, 0)
|
gen := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
|
||||||
@@ -57,9 +68,43 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
u.Get(&mu)
|
u.Get(&mu)
|
||||||
u.Get(&ocu)
|
u.Get(&ocu)
|
||||||
|
|
||||||
nick_label := gtk.NewLabel(Jid.MustParse(u.From).Resourcepart())
|
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("-")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
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(nick_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 := gtk.NewGestureClick()
|
||||||
gesture.SetButton(3) // Right click
|
gesture.SetButton(3) // Right click
|
||||||
@@ -68,7 +113,9 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
win := gtk.NewWindow()
|
win := gtk.NewWindow()
|
||||||
win.SetDefaultSize(400, 400)
|
win.SetDefaultSize(400, 400)
|
||||||
profile_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
profile_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
nick := gtk.NewLabel(Jid.MustParse(u.From).Resourcepart())
|
nick := gtk.NewLabel(JidMustParse(u.From).Resource)
|
||||||
|
|
||||||
|
win.SetTitle(JidMustParse(u.From).Resource)
|
||||||
nick.AddCSSClass("author")
|
nick.AddCSSClass("author")
|
||||||
profile_box.Append(nick)
|
profile_box.Append(nick)
|
||||||
profile_box.Append(gtk.NewLabel(u.From))
|
profile_box.Append(gtk.NewLabel(u.From))
|
||||||
@@ -96,6 +143,16 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mu MucUser
|
||||||
|
ok = u.Get(&mu)
|
||||||
|
if ok {
|
||||||
|
if mu.MucUserItem.JID != "" {
|
||||||
|
profile_box.Append(gtk.NewLabel(mu.MucUserItem.JID))
|
||||||
|
}
|
||||||
|
profile_box.Append(gtk.NewLabel("Connected with role " + mu.MucUserItem.Role))
|
||||||
|
profile_box.Append(gtk.NewLabel("Affiliated as " + mu.MucUserItem.Affiliation))
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
mychan, err := client.SendIQ(ctx, iqResp)
|
mychan, err := client.SendIQ(ctx, iqResp)
|
||||||
@@ -116,7 +173,7 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
mo, _ := mucmembers.Load(Jid.MustParse(u.From).Bare().String())
|
mo, _ := mucmembers.Load(JidMustParse(u.From).Bare())
|
||||||
mm := mo.(mucUnit)
|
mm := mo.(mucUnit)
|
||||||
mmm := mm.Members
|
mmm := mm.Members
|
||||||
mmmm, ok := mmm.Load(ocu.ID)
|
mmmm, ok := mmm.Load(ocu.ID)
|
||||||
@@ -149,8 +206,11 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
win.Present()
|
win.Present()
|
||||||
})
|
})
|
||||||
userbox.AddController(gesture)
|
userbox.AddController(gesture)
|
||||||
|
if mu.MucUserItem.Role == "moderator" {
|
||||||
|
gen.Prepend(userbox)
|
||||||
|
} else {
|
||||||
gen.Append(userbox)
|
gen.Append(userbox)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jacoblockett/sanitizefilename"
|
"github.com/jacoblockett/sanitizefilename"
|
||||||
"github.com/jasonlovesdoggo/gopen"
|
"github.com/jasonlovesdoggo/gopen"
|
||||||
@@ -32,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 {
|
} else {
|
||||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " joined the room")
|
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " joined the MUC")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +44,35 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return gtk.NewLabel("Unsupported message.")
|
return gtk.NewLabel("Unsupported message.")
|
||||||
}
|
}
|
||||||
|
fmt.Println(m.Body)
|
||||||
|
|
||||||
|
readmarker := Marker{}
|
||||||
|
ok = m.Get(&readmarker)
|
||||||
|
if ok {
|
||||||
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
b.Append(gtk.NewLabel(fmt.Sprintf("%s has read to this point", JidMustParse(m.From).Resource)))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
indicator := stanza.StateComposing{}
|
||||||
|
ok = m.Get(&indicator)
|
||||||
|
if ok { // TODO: Display typing indicator in a stat bar or something similar
|
||||||
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
b.Append(gtk.NewLabel(fmt.Sprintf("%s is typing...", JidMustParse(m.From).Resource)))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
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_box.Append(cancel_img)
|
||||||
|
error_box.Append(error_label)
|
||||||
|
return error_box
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sid := StanzaID{}
|
sid := StanzaID{}
|
||||||
m.Get(&sid)
|
m.Get(&sid)
|
||||||
|
|
||||||
@@ -50,17 +80,21 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
gesture := gtk.NewGestureClick()
|
gesture := gtk.NewGestureClick()
|
||||||
gesture.SetButton(3) // Right click
|
gesture.SetButton(3) // Right click
|
||||||
|
|
||||||
vis := false
|
|
||||||
reactions := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
|
||||||
reactions.SetVisible(false)
|
|
||||||
|
|
||||||
|
popover := gtk.NewPopover()
|
||||||
|
popover.SetParent(mainBox)
|
||||||
|
popover.SetHasArrow(false)
|
||||||
|
|
||||||
|
rc_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
|
||||||
|
reactions := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
reaction := []string{"👍", "👎", "♥️", "🤣", "😭"}
|
reaction := []string{"👍", "👎", "♥️", "🤣", "😭"}
|
||||||
for _, v := range reaction {
|
for _, v := range reaction {
|
||||||
like := gtk.NewButton()
|
like := gtk.NewButton()
|
||||||
like.SetLabel(v)
|
like.SetLabel(v)
|
||||||
like.SetHExpand(true)
|
like.SetHExpand(true)
|
||||||
like.ConnectClicked(func() {
|
like.ConnectClicked(func() {
|
||||||
fmt.Println("licked")
|
fmt.Println("licked") // TODO: Implement proper support for reactions via extension
|
||||||
client.SendRaw(fmt.Sprintf(`
|
client.SendRaw(fmt.Sprintf(`
|
||||||
<message from='%s' to='%s' id='%s' type='%s'>
|
<message from='%s' to='%s' id='%s' type='%s'>
|
||||||
<reactions id='%s' xmlns='urn:xmpp:reactions:0'>
|
<reactions id='%s' xmlns='urn:xmpp:reactions:0'>
|
||||||
@@ -72,14 +106,30 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
reactions.Append(like)
|
reactions.Append(like)
|
||||||
}
|
}
|
||||||
|
|
||||||
gesture.Connect("pressed", func(n_press, x, y int) {
|
rc_box.Append(reactions)
|
||||||
if !vis {
|
|
||||||
vis = true
|
if m.Type == stanza.MessageTypeGroupchat {
|
||||||
reactions.SetVisible(true)
|
moderate := gtk.NewButtonWithLabel("Moderate") // TODO: Implement proper support for moderations via extension
|
||||||
} else {
|
moderate.ConnectClicked(func() {
|
||||||
vis = false
|
client.SendRaw(fmt.Sprintf(`
|
||||||
reactions.SetVisible(false)
|
<iq type='set' to='%s' id='%s'>
|
||||||
|
<moderate id='%s' xmlns='urn:xmpp:message-moderate:1'>
|
||||||
|
<retract xmlns='urn:xmpp-message-retract:1'/>
|
||||||
|
<reason>Retracted</reason>
|
||||||
|
</moderate>
|
||||||
|
</iq>
|
||||||
|
`, jid.MustParse(m.From).Bare().String(), uuid.New().String(), sid.ID))
|
||||||
|
})
|
||||||
|
rc_box.Append(moderate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
popover.SetChild(rc_box)
|
||||||
|
|
||||||
|
gesture.Connect("pressed", func(n_press, x, y int) {
|
||||||
|
rect := gdk.NewRectangle(x, y, 1, 1)
|
||||||
|
popover.SetPointingTo(&rect)
|
||||||
|
popover.Popup()
|
||||||
})
|
})
|
||||||
|
|
||||||
mainBox.AddController(gesture)
|
mainBox.AddController(gesture)
|
||||||
@@ -102,6 +152,9 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
|
|
||||||
// authorBox.Append(im)
|
// authorBox.Append(im)
|
||||||
|
|
||||||
|
al := gtk.NewLabel(jid.MustParse(m.From).Resourcepart())
|
||||||
|
al.AddCSSClass("author")
|
||||||
|
|
||||||
if m.Type == stanza.MessageTypeGroupchat {
|
if m.Type == stanza.MessageTypeGroupchat {
|
||||||
mo, _ := mucmembers.Load(jid.MustParse(m.From).Bare().String())
|
mo, _ := mucmembers.Load(jid.MustParse(m.From).Bare().String())
|
||||||
mm := mo.(mucUnit)
|
mm := mo.(mucUnit)
|
||||||
@@ -118,25 +171,24 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
authorBox.Append(im)
|
authorBox.Append(im)
|
||||||
} else {
|
} else {
|
||||||
im := newImageFromPath("debug.png")
|
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
im.SetPixelSize(40)
|
im.SetPixelSize(40)
|
||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
authorBox.Append(im)
|
authorBox.Append(im)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
im := newImageFromPath("debug.png")
|
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
im.SetPixelSize(40)
|
im.SetPixelSize(40)
|
||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
authorBox.Append(im)
|
authorBox.Append(im)
|
||||||
}
|
}
|
||||||
|
} else if m.Type == stanza.MessageTypeChat {
|
||||||
|
al.SetText(al.Text() + " whispers")
|
||||||
}
|
}
|
||||||
|
|
||||||
al := gtk.NewLabel(jid.MustParse(m.From).Resourcepart())
|
|
||||||
al.AddCSSClass("author")
|
|
||||||
|
|
||||||
authorBox.Append(al)
|
authorBox.Append(al)
|
||||||
mlabel := gtk.NewLabel(m.Body)
|
mlabel := gtk.NewLabel(m.Body)
|
||||||
// mlabel.SetMarkup(convertXEPToPango(m.Body))
|
|
||||||
mlabel.SetWrap(true)
|
mlabel.SetWrap(true)
|
||||||
mlabel.SetSelectable(true)
|
mlabel.SetSelectable(true)
|
||||||
mlabel.SetHAlign(gtk.AlignFill)
|
mlabel.SetHAlign(gtk.AlignFill)
|
||||||
@@ -174,12 +226,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.
|
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()
|
p, err := ensureCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newImageFromPath("debug.png")
|
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if hash == "" {
|
if hash == "" {
|
||||||
fmt.Println("Hash is nil!")
|
fmt.Println("Hash is nil!")
|
||||||
return newImageFromPath("debug.png")
|
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
}
|
}
|
||||||
|
|
||||||
hash = filepath.Join(p, sanitizefilename.Sanitize(hash))
|
hash = filepath.Join(p, sanitizefilename.Sanitize(hash))
|
||||||
@@ -211,12 +263,12 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou
|
|||||||
result := <-mychan
|
result := <-mychan
|
||||||
card, ok := result.Payload.(*VCard)
|
card, ok := result.Payload.(*VCard)
|
||||||
if !ok {
|
if !ok {
|
||||||
return newImageFromPath("debug.png")
|
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
}
|
}
|
||||||
|
|
||||||
base64_data := card.Photo.Binval
|
base64_data := card.Photo.Binval
|
||||||
if card.Photo.Binval == "" || (card.Photo.Type == "image/svg+xml" && runtime.GOOS == "windows") {
|
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)
|
data, err := base64.StdEncoding.DecodeString(base64_data)
|
||||||
|
|||||||
@@ -26,25 +26,31 @@ func dropToSignInPage(err error) {
|
|||||||
insecure_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
insecure_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
server_label := gtk.NewLabel("Server: ")
|
server_label := gtk.NewLabel("Server: ")
|
||||||
username_label := gtk.NewLabel("Username: ")
|
username_label := gtk.NewLabel("JID: ")
|
||||||
password_label := gtk.NewLabel("Password: ")
|
password_label := gtk.NewLabel("Password: ")
|
||||||
nickname_label := gtk.NewLabel("Nickname: ")
|
nickname_label := gtk.NewLabel("Nickname: ")
|
||||||
insecure_label := gtk.NewLabel("Insecure: ")
|
insecure_label := gtk.NewLabel("Insecure: (?)")
|
||||||
|
insecure_label.SetTooltipText("Tick this if you need to connect without TLS, usually for connecting to Tor XMPP servers")
|
||||||
|
|
||||||
server_entry := gtk.NewEntry()
|
server_entry := gtk.NewEntry()
|
||||||
server_entry.SetHAlign(gtk.AlignEnd)
|
server_entry.SetHAlign(gtk.AlignEnd)
|
||||||
|
server_entry.SetHExpand(true)
|
||||||
|
|
||||||
username_entry := gtk.NewEntry()
|
username_entry := gtk.NewEntry()
|
||||||
username_entry.SetHAlign(gtk.AlignEnd)
|
username_entry.SetHAlign(gtk.AlignEnd)
|
||||||
|
username_entry.SetHExpand(true)
|
||||||
|
|
||||||
password_entry := gtk.NewPasswordEntry()
|
password_entry := gtk.NewPasswordEntry()
|
||||||
password_entry.SetHAlign(gtk.AlignEnd)
|
password_entry.SetHAlign(gtk.AlignEnd)
|
||||||
|
password_entry.SetHExpand(true)
|
||||||
|
|
||||||
nickname_entry := gtk.NewEntry()
|
nickname_entry := gtk.NewEntry()
|
||||||
nickname_entry.SetHAlign(gtk.AlignEnd)
|
nickname_entry.SetHAlign(gtk.AlignEnd)
|
||||||
|
nickname_entry.SetHExpand(true)
|
||||||
|
|
||||||
insecure_check := gtk.NewCheckButton()
|
insecure_check := gtk.NewCheckButton()
|
||||||
insecure_check.SetHAlign(gtk.AlignEnd)
|
insecure_check.SetHAlign(gtk.AlignEnd)
|
||||||
|
insecure_check.SetHExpand(true)
|
||||||
|
|
||||||
|
|
||||||
server_box.Append(server_label)
|
server_box.Append(server_label)
|
||||||
|
|||||||
250
main.go
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/diamondburned/gotk4/pkg/gio/v2"
|
"github.com/diamondburned/gotk4/pkg/gio/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
"github.com/kr/pretty"
|
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"encoding/base64"
|
||||||
)
|
)
|
||||||
|
|
||||||
var loadedConfig lambdaConfig
|
var loadedConfig lambdaConfig
|
||||||
@@ -55,6 +56,37 @@ var mucmembers sync.Map
|
|||||||
// stores devices of users
|
// stores devices of users
|
||||||
var userdevices sync.Map
|
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)
|
||||||
|
|
||||||
|
//go:embed assets/cancel.png
|
||||||
|
var cancelBytes []byte
|
||||||
|
var cancelB64 string = base64.StdEncoding.EncodeToString(cancelBytes)
|
||||||
|
|
||||||
|
var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
|
||||||
|
var lockedJIDs map[string]bool = make(map[string]bool)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
go func() {
|
go func() {
|
||||||
for fn := range uiQueue {
|
for fn := range uiQueue {
|
||||||
@@ -65,8 +97,63 @@ func init() {
|
|||||||
time.Sleep(10 * time.Millisecond) // Small delay between updates
|
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()
|
||||||
|
|
||||||
|
cancelData, _ := base64.StdEncoding.DecodeString(cancelB64)
|
||||||
|
loader.Write(cancelData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["cancel"] = 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() {
|
func main() {
|
||||||
p, err := ensureConfig()
|
p, err := ensureConfig()
|
||||||
@@ -86,7 +173,7 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put 4 random characters in front of lambda
|
// Put 4 random characters at the end
|
||||||
chars := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZλ"
|
chars := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZλ"
|
||||||
str := ""
|
str := ""
|
||||||
for range 4 {
|
for range 4 {
|
||||||
@@ -97,7 +184,7 @@ func main() {
|
|||||||
TransportConfiguration: xmpp.TransportConfiguration{
|
TransportConfiguration: xmpp.TransportConfiguration{
|
||||||
Address: loadedConfig.Server,
|
Address: loadedConfig.Server,
|
||||||
},
|
},
|
||||||
Jid: loadedConfig.Username + "/lambda."+str,
|
Jid: loadedConfig.Username + "/lambda." + str,
|
||||||
Credential: xmpp.Password(loadedConfig.Password),
|
Credential: xmpp.Password(loadedConfig.Password),
|
||||||
Insecure: loadedConfig.Insecure,
|
Insecure: loadedConfig.Insecure,
|
||||||
// StreamLogger: os.Stdout,
|
// StreamLogger: os.Stdout,
|
||||||
@@ -165,6 +252,12 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e := stanza.PubSubEvent{}
|
||||||
|
ok = m.Get(&e)
|
||||||
|
if ok {
|
||||||
|
fmt.Println(e)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if m.Body == "" {
|
if m.Body == "" {
|
||||||
return
|
return
|
||||||
@@ -174,7 +267,7 @@ func main() {
|
|||||||
originator := jid.MustParse(m.From).Bare().String()
|
originator := jid.MustParse(m.From).Bare().String()
|
||||||
|
|
||||||
glib.IdleAdd(func() {
|
glib.IdleAdd(func() {
|
||||||
uiQueue <- func() {
|
//uiQueue <- func() {
|
||||||
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
ba, ok := generateMessageWidget(p).(*gtk.Box)
|
ba, ok := generateMessageWidget(p).(*gtk.Box)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -190,7 +283,7 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println("Got message when the tab does not exist!")
|
fmt.Println("Got message when the tab does not exist!")
|
||||||
}
|
}
|
||||||
}
|
//}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -200,8 +293,6 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty.Println(presence)
|
|
||||||
|
|
||||||
if presence.Error != *new(stanza.Err) {
|
if presence.Error != *new(stanza.Err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -213,7 +304,8 @@ func main() {
|
|||||||
|
|
||||||
if ok { // This is a presence stanza from a user in a MUC
|
if ok { // This is a presence stanza from a user in a MUC
|
||||||
presence.Get(&ocu)
|
presence.Get(&ocu)
|
||||||
muc := jid.MustParse(presence.From).Bare().String()
|
from, _ := stanza.NewJid(presence.From)
|
||||||
|
muc := from.Bare()
|
||||||
_, ok = mucmembers.Load(muc)
|
_, ok = mucmembers.Load(muc)
|
||||||
if !ok {
|
if !ok {
|
||||||
mucmembers.Store(muc, mucUnit{})
|
mucmembers.Store(muc, mucUnit{})
|
||||||
@@ -231,7 +323,6 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
typed_unit.Members.Delete(ocu.ID)
|
typed_unit.Members.Delete(ocu.ID)
|
||||||
glib.IdleAdd(func() {
|
glib.IdleAdd(func() {
|
||||||
uiQueue <- func() {
|
|
||||||
b := gtk.NewLabel("")
|
b := gtk.NewLabel("")
|
||||||
ba, ok := generatePresenceWidget(p).(*gtk.Label)
|
ba, ok := generatePresenceWidget(p).(*gtk.Label)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -247,7 +338,6 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println("Got message when the tab does not exist!")
|
fmt.Println("Got message when the tab does not exist!")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,8 +349,9 @@ func main() {
|
|||||||
_, ok := userdevices.Load(user)
|
_, ok := userdevices.Load(user)
|
||||||
_, mok := mucmembers.Load(user)
|
_, mok := mucmembers.Load(user)
|
||||||
if !ok && !mok { // FIXME: The initial muc presence gets picked up from this check
|
if !ok && !mok { // FIXME: The initial muc presence gets picked up from this check
|
||||||
|
ok := createTab(user, false)
|
||||||
|
if ok && !lockedJIDs[user]{
|
||||||
userdevices.Store(user, userUnit{})
|
userdevices.Store(user, userUnit{})
|
||||||
createTab(user, false)
|
|
||||||
|
|
||||||
b := gtk.NewButtonWithLabel(user)
|
b := gtk.NewButtonWithLabel(user)
|
||||||
b.ConnectClicked(func() {
|
b.ConnectClicked(func() {
|
||||||
@@ -269,10 +360,10 @@ func main() {
|
|||||||
})
|
})
|
||||||
menu.Append(b)
|
menu.Append(b)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unit, ok := userdevices.Load(user)
|
unit, ok := userdevices.Load(user)
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Println("Could not load user presence even after recreating it! Something weird is going on!")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +378,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userdevices.Store(user, typed_unit)
|
userdevices.Store(user, typed_unit)
|
||||||
|
lockedJIDs[user] = true
|
||||||
}
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
})
|
})
|
||||||
|
|
||||||
c, err := xmpp.NewClient(&config, router, func(err error) {
|
c, err := xmpp.NewClient(&config, router, func(err error) {
|
||||||
@@ -332,38 +425,83 @@ func activate(app *gtk.Application) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
window = gtk.NewApplicationWindow(app)
|
window = gtk.NewApplicationWindow(app)
|
||||||
|
the_menu := gio.NewMenu()
|
||||||
|
|
||||||
|
fileMenu := gio.NewMenu()
|
||||||
|
fileMenu.Append("Join MUC", "app.join")
|
||||||
|
fileMenu.Append("Start DM", "app.dm")
|
||||||
|
|
||||||
|
joinAction := gio.NewSimpleAction("join", nil)
|
||||||
|
joinAction.ConnectActivate(func(p *glib.Variant) {
|
||||||
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
jid_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
nick_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
|
jid_entry := gtk.NewEntry()
|
||||||
|
nick_entry := gtk.NewEntry()
|
||||||
|
|
||||||
|
jid_entry.SetHAlign(gtk.AlignEnd)
|
||||||
|
jid_entry.SetHExpand(true)
|
||||||
|
|
||||||
|
nick_entry.SetHAlign(gtk.AlignEnd)
|
||||||
|
nick_entry.SetHExpand(true)
|
||||||
|
|
||||||
|
nick_entry.SetText(loadedConfig.Nick)
|
||||||
|
|
||||||
|
jid_box.Append(gtk.NewLabel("MUC JID:"))
|
||||||
|
jid_box.Append(jid_entry)
|
||||||
|
|
||||||
|
nick_box.Append(gtk.NewLabel("Nick:"))
|
||||||
|
nick_box.Append(nick_entry)
|
||||||
|
|
||||||
|
box.Append(jid_box)
|
||||||
|
box.Append(nick_box)
|
||||||
|
|
||||||
|
btn := gtk.NewButtonWithLabel("Submit")
|
||||||
|
btn.SetVAlign(gtk.AlignBaseline)
|
||||||
|
|
||||||
|
box.Append(btn)
|
||||||
|
|
||||||
|
win := gtk.NewWindow()
|
||||||
|
win.SetTitle("Join MUC")
|
||||||
|
win.SetDefaultSize(200, 200)
|
||||||
|
win.SetChild(box)
|
||||||
|
|
||||||
|
btn.ConnectClicked(func() {
|
||||||
|
t := jid_entry.Text()
|
||||||
|
_, ok := tabs.Load(t)
|
||||||
|
if !ok {
|
||||||
|
err := joinMuc(client, clientroot.Session.BindJid, t, nick_entry.Text())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
createTab(t, true)
|
||||||
|
b := gtk.NewButtonWithLabel(t)
|
||||||
|
b.ConnectClicked(func() {
|
||||||
|
b.AddCSSClass("accent")
|
||||||
|
switchToTab(t, &window.Window)
|
||||||
|
})
|
||||||
|
menu.Append(b)
|
||||||
|
}
|
||||||
|
win.SetVisible(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
win.SetTransientFor(win)
|
||||||
|
win.Present()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.AddAction(joinAction)
|
||||||
|
|
||||||
|
the_menu.AppendSubmenu("File", fileMenu)
|
||||||
|
|
||||||
|
the_menuBar := gtk.NewPopoverMenuBarFromModel(the_menu)
|
||||||
app.SetMenubar(gio.NewMenu())
|
app.SetMenubar(gio.NewMenu())
|
||||||
|
|
||||||
window.SetTitle("Lambda")
|
window.SetTitle("Lambda")
|
||||||
window.Window.AddCSSClass("ssd")
|
window.Window.AddCSSClass("ssd")
|
||||||
|
window.Window.SetDefaultSize(500, 500)
|
||||||
menu = gtk.NewBox(gtk.OrientationVertical, 0)
|
menu = gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
/*
|
|
||||||
f_menu := gtk.NewMenuButton()
|
|
||||||
f_menu.SetLabel("File")
|
|
||||||
f_menu.SetAlwaysShowArrow(false)
|
|
||||||
|
|
||||||
e_menu := gtk.NewMenuButton()
|
|
||||||
e_menu.SetLabel("Edit")
|
|
||||||
e_menu.SetAlwaysShowArrow(false)
|
|
||||||
|
|
||||||
v_menu := gtk.NewMenuButton()
|
|
||||||
v_menu.SetLabel("View")
|
|
||||||
v_menu.SetAlwaysShowArrow(false)
|
|
||||||
|
|
||||||
b_menu := gtk.NewMenuButton()
|
|
||||||
b_menu.SetLabel("Bookmarks")
|
|
||||||
b_menu.SetAlwaysShowArrow(false)
|
|
||||||
|
|
||||||
h_menu := gtk.NewMenuButton()
|
|
||||||
h_menu.SetLabel("Help")
|
|
||||||
h_menu.SetAlwaysShowArrow(false)
|
|
||||||
|
|
||||||
menu.Append(f_menu)
|
|
||||||
menu.Append(e_menu)
|
|
||||||
menu.Append(v_menu)
|
|
||||||
menu.Append(b_menu)
|
|
||||||
menu.Append(h_menu)
|
|
||||||
*/
|
|
||||||
|
|
||||||
empty_dialog = gtk.NewLabel("You are not focused on any chats.")
|
empty_dialog = gtk.NewLabel("You are not focused on any chats.")
|
||||||
empty_dialog.SetVExpand(true)
|
empty_dialog.SetVExpand(true)
|
||||||
@@ -375,6 +513,8 @@ func activate(app *gtk.Application) {
|
|||||||
memberList.SetHExpand(true)
|
memberList.SetHExpand(true)
|
||||||
|
|
||||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
box.Append(the_menuBar)
|
||||||
|
|
||||||
// scroller.SetChild(empty_dialog)
|
// scroller.SetChild(empty_dialog)
|
||||||
scroller.SetChild(empty_dialog)
|
scroller.SetChild(empty_dialog)
|
||||||
menu_scroll := gtk.NewScrolledWindow()
|
menu_scroll := gtk.NewScrolledWindow()
|
||||||
@@ -388,10 +528,12 @@ func activate(app *gtk.Application) {
|
|||||||
chat_pane := gtk.NewPaned(gtk.OrientationHorizontal)
|
chat_pane := gtk.NewPaned(gtk.OrientationHorizontal)
|
||||||
chat_pane.SetStartChild(scroller)
|
chat_pane.SetStartChild(scroller)
|
||||||
chat_pane.SetEndChild(memberList)
|
chat_pane.SetEndChild(memberList)
|
||||||
|
chat_pane.SetPosition(225)
|
||||||
|
|
||||||
main_pane := gtk.NewPaned(gtk.OrientationHorizontal)
|
main_pane := gtk.NewPaned(gtk.OrientationHorizontal)
|
||||||
main_pane.SetStartChild(menu_scroll)
|
main_pane.SetStartChild(menu_scroll)
|
||||||
main_pane.SetEndChild(chat_pane)
|
main_pane.SetEndChild(chat_pane)
|
||||||
|
main_pane.SetPosition(135)
|
||||||
|
|
||||||
chatbox.Append(main_pane)
|
chatbox.Append(main_pane)
|
||||||
box.Append(chatbox)
|
box.Append(chatbox)
|
||||||
@@ -437,37 +579,9 @@ func activate(app *gtk.Application) {
|
|||||||
|
|
||||||
en.SetHExpand(true)
|
en.SetHExpand(true)
|
||||||
|
|
||||||
m_entry := gtk.NewEntry()
|
|
||||||
|
|
||||||
entry_box.Append(en)
|
entry_box.Append(en)
|
||||||
entry_box.Append(b)
|
entry_box.Append(b)
|
||||||
|
|
||||||
entry_box.Append(m_entry)
|
|
||||||
|
|
||||||
debug_btn := gtk.NewButtonWithLabel("Join muc")
|
|
||||||
|
|
||||||
debug_btn.ConnectClicked(func() {
|
|
||||||
t := en.Text()
|
|
||||||
_, ok := tabs.Load(t)
|
|
||||||
if !ok {
|
|
||||||
err := joinMuc(client, clientroot.Session.BindJid, t, m_entry.Text())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
createTab(t, true)
|
|
||||||
b := gtk.NewButtonWithLabel(t)
|
|
||||||
b.ConnectClicked(func() {
|
|
||||||
b.AddCSSClass("accent")
|
|
||||||
switchToTab(t, &window.Window)
|
|
||||||
})
|
|
||||||
menu.Append(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
entry_box.Append(debug_btn)
|
|
||||||
|
|
||||||
box.Append(entry_box)
|
box.Append(entry_box)
|
||||||
|
|
||||||
window.SetChild(box)
|
window.SetChild(box)
|
||||||
|
|||||||
BIN
rsrc_windows_amd64.syso
Normal file
23
style.css
@@ -10,3 +10,26 @@
|
|||||||
.author_img {
|
.author_img {
|
||||||
border-radius 100%;
|
border-radius 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.owner {
|
||||||
|
background-color: red;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin {
|
||||||
|
background-color: orange;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member {
|
||||||
|
background-color: lime;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moderator {
|
||||||
|
color: magenta;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visitor {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
|||||||
19
xmpp-displayed_markers.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Partial implementation of XEP-0333: Displayed Markers
|
||||||
|
// https://xmpp.org/extensions/xep-0333.html
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Marker struct {
|
||||||
|
stanza.MsgExtension
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:chat-markers:0 displayed"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:chat-markers:0", Local: "displayed"}, Marker{})
|
||||||
|
}
|
||||||
@@ -46,3 +46,14 @@ func joinMuc(c xmpp.Sender, jid string, muc string, nick string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jid MustParse but using gosrc's instead of mellium
|
||||||
|
// This function will panic if its an invalid JID
|
||||||
|
|
||||||
|
func JidMustParse(s string) (*stanza.Jid) {
|
||||||
|
j, err := stanza.NewJid(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
|||||||