Fix lag issues

This commit is contained in:
2026-04-30 14:55:17 +01:00
parent 7b63799f0b
commit 0bfb140dc7
5 changed files with 223 additions and 70 deletions
+4
View File
@@ -137,6 +137,9 @@ var mucTemporaryBytes []byte
//go:embed assets/moderate.png //go:embed assets/moderate.png
var moderateBytes []byte var moderateBytes []byte
//go:embed assets/jabber.png
var jabberBytes []byte
func loadAsset(key string, data []byte) { func loadAsset(key string, data []byte) {
loader := gdkpixbuf.NewPixbufLoader() loader := gdkpixbuf.NewPixbufLoader()
loader.Write(data) loader.Write(data)
@@ -189,6 +192,7 @@ func init() {
"muc_persistent": mucPersistentBytes, "muc_persistent": mucPersistentBytes,
"muc_temporary": mucTemporaryBytes, "muc_temporary": mucTemporaryBytes,
"moderate": moderateBytes, "moderate": moderateBytes,
"jabber": jabberBytes,
} { } {
loadAsset(key, data) loadAsset(key, data)
} }
+73 -2
View File
@@ -73,9 +73,13 @@ func switchToTab(jid string, w *gtk.Window) {
i := 0 i := 0
rangeOrdered(&mm, (func(k, v any) bool { rangeOrdered(&mm, (func(k, v any) bool {
i++ i++
u, ok := v.(stanza.Presence)
if !ok {
return true
}
userbox := gtk.NewBox(gtk.OrientationHorizontal, 2) userbox := gtk.NewBox(gtk.OrientationHorizontal, 2)
u := v.(stanza.Presence)
var mu MucUser var mu MucUser
var ocu OccupantID var ocu OccupantID
u.Get(&mu) u.Get(&mu)
@@ -103,7 +107,7 @@ func switchToTab(jid string, w *gtk.Window) {
userbox.Append(nick_label) userbox.Append(nick_label)
var hats Hats var hats Hats
ok := u.Get(&hats) ok = u.Get(&hats)
if ok { if ok {
for _, hat := range hats.Hats { for _, hat := range hats.Hats {
var val float64 var val float64
@@ -531,3 +535,70 @@ func createIdenticon(word string) *gtk.Image { // This function generates an ide
return i return i
} }
func jidBuilder(en *gtk.Entry) { // This function spawns a window that allows the user to interactively build a JID
// TODO: Localise this
win := gtk.NewWindow()
win.SetTitle("Build-A-JID")
win.SetDefaultSize(400, 1)
win.SetResizable(false)
box := gtk.NewBox(gtk.OrientationVertical, 2)
header := gtk.NewLabel("Build-A-JID")
header.AddCSSClass("author")
box.Append(header)
box.Append(gtk.NewLabel("All fields except for domain are optional"))
jid_builder := gtk.NewBox(gtk.OrientationHorizontal, 2)
localPartEntry := gtk.NewEntry()
localPartEntry.SetPlaceholderText("localpart")
jid_builder.Append(localPartEntry)
at_sign := gtk.NewLabel("@")
at_sign.AddCSSClass("author")
jid_builder.Append(at_sign)
domainEntry := gtk.NewEntry()
domainEntry.SetPlaceholderText("domain")
jid_builder.Append(domainEntry)
resource_sign := gtk.NewLabel("/")
resource_sign.AddCSSClass("author")
jid_builder.Append(resource_sign)
resourceEntry := gtk.NewEntry()
resourceEntry.SetPlaceholderText("resource")
jid_builder.Append(resourceEntry)
box.Append(jid_builder)
submit := gtk.NewButtonWithLabel(loadedLocale["submit"])
submit.ConnectClicked(func() {
localPart := localPartEntry.Text()
domain := domainEntry.Text()
resource := resourceEntry.Text()
at := "@"
slash := "/"
if localPart == "" {
at = ""
}
if resource == "" {
slash = ""
}
jid := localPart + at + domain + slash + resource
en.SetText(jid)
win.SetVisible(false)
})
box.Append(submit)
win.SetChild(box)
win.SetVisible(true)
}
+15 -10
View File
@@ -7,6 +7,7 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"github.com/diamondburned/gotk4/pkg/gdk/v4" "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/gtk/v4"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/jacoblockett/sanitizefilename" "github.com/jacoblockett/sanitizefilename"
@@ -155,35 +156,39 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
// authorBox.Append(im) // authorBox.Append(im)
n := jid.MustParse(m.From).Resourcepart() n := JidMustParse(m.From).Resource
if n == "" { if n == "" {
n = jid.MustParse(m.From).String() n = JidMustParse(m.From).Resource
} }
al := gtk.NewLabel(n) al := gtk.NewLabel(n)
al.AddCSSClass("author") al.AddCSSClass("author")
al.SetSelectable(true) al.SetSelectable(true)
if m.Type == stanza.MessageTypeGroupchat { if m.Type == stanza.MessageTypeGroupchat {
mo, _ := mucmembers.Load(jid.MustParse(m.From).Bare().String()) mo, _ := mucmembers.Load(JidMustParse(m.From).Bare())
mm := mo.(mucUnit) mm := mo.(mucUnit)
mmm := mm.Members mmm := mm.Members
mmmm, ok := mmm.Load(id) mmmm, ok := mmm.Load(id)
if ok { if ok {
pres := mmmm.(stanza.Presence) pres := mmmm.(stanza.Presence)
var vu VCardUpdate var vu VCardUpdate
pres.Get(&vu) pres.Get(&vu)
if vu.Photo != "" {
im := getAvatar(m.From, vu.Photo)
im.SetPixelSize(40)
im.AddCSSClass("author_img")
authorBox.Append(im)
} else {
im := createIdenticon(m.From) im := createIdenticon(m.From)
im.SetPixelSize(40) im.SetPixelSize(40)
im.AddCSSClass("author_img") im.AddCSSClass("author_img")
authorBox.Append(im) authorBox.Append(im)
if vu.Photo != "" {
go func() {
new_im := getAvatar(m.From, vu.Photo)
glib.IdleAdd(func() {
new_im.SetPixelSize(40)
new_im.AddCSSClass("author_img")
authorBox.Remove(im)
authorBox.Prepend(new_im)
})
}()
} }
} else { } else {
im := createIdenticon(m.From) im := createIdenticon(m.From)
im.SetPixelSize(40) im.SetPixelSize(40)
+71 -41
View File
@@ -219,7 +219,9 @@ func main() {
*/ */
originator := JidMustParse(m.From).Bare() originator := JidMustParse(m.From).Bare()
glib.IdleAdd(func() {
mStatus.SetText(originator) mStatus.SetText(originator)
})
at := new(Attention) at := new(Attention)
ok = m.Get(at) ok = m.Get(at)
@@ -280,7 +282,6 @@ func main() {
} }
glib.IdleAdd(func() { glib.IdleAdd(func() {
//uiQueue <- func() {
b := gtk.NewBox(gtk.OrientationVertical, 0) b := gtk.NewBox(gtk.OrientationVertical, 0)
tab, ok := tabs.Load(originator) tab, ok := tabs.Load(originator)
@@ -303,7 +304,6 @@ func main() {
if ok { if ok {
b.Append(ba) b.Append(ba)
} }
//}
}) })
}) })
@@ -464,11 +464,13 @@ func main() {
return return
} }
_ = <-mychan _ = <-mychan
pingStatus.RemoveCSSClass("pending")
delay := time.Since(before) / time.Millisecond delay := time.Since(before) / time.Millisecond
pingStatus.SetText(fmt.Sprintf("%d %s", delay, loadedLocale["milliseconds"]))
pingTimes[0] = append(pingTimes[0], float64(delay)) pingTimes[0] = append(pingTimes[0], float64(delay))
glib.IdleAdd(func() {
pingStatus.RemoveCSSClass("pending")
pingStatus.SetText(fmt.Sprintf("%d %s", delay, loadedLocale["milliseconds"]))
})
}() }()
} }
@@ -480,6 +482,7 @@ func main() {
go func() { go func() {
for { for {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
glib.IdleAdd(func() {
stat, err := xmlLog.Stat() stat, err := xmlLog.Stat()
if err != nil { if err != nil {
panic(err) panic(err)
@@ -488,20 +491,21 @@ func main() {
newsize = stat.Size() newsize = stat.Size()
diff := float64(newsize-oldsize) / 1000 diff := float64(newsize-oldsize) / 1000
if diff > 100 { ic := clientAssets["car"]
sIcon.SetFromPaintable(clientAssets["car_high"]) if diff >= 25 {
} else { ic = clientAssets["car_high"]
sIcon.SetFromPaintable(clientAssets["car"])
} }
sStatus.SetText(fmt.Sprintf("%.2f%s", diff, loadedLocale["KBPerSecond"])) sStatus.SetText(fmt.Sprintf("%.2f%s", diff, loadedLocale["KBPerSecond"]))
oldsize = stat.Size() sIcon.SetFromPaintable(ic)
})
} }
}() }()
glib.IdleAdd(func() {
connectionStatus.SetText(fmt.Sprintf("%s%s", loadedLocale["connectedAs"], JidMustParse(clientroot.Session.BindJid).Bare())) connectionStatus.SetText(fmt.Sprintf("%s%s", loadedLocale["connectedAs"], JidMustParse(clientroot.Session.BindJid).Bare()))
connectionStatus.SetTooltipText(fmt.Sprintf("%s%s\n%s%t", loadedLocale["bindedJid"], clientroot.Session.BindJid, loadedLocale["usingTLS"], clientroot.Session.TlsEnabled)) connectionStatus.SetTooltipText(fmt.Sprintf("%s%s\n%s%t", loadedLocale["bindedJid"], clientroot.Session.BindJid, loadedLocale["usingTLS"], clientroot.Session.TlsEnabled))
connectionIcon.SetFromPaintable(clientAssets["connect"]) connectionIcon.SetFromPaintable(clientAssets["connect"])
})
// Enable carbons // Enable carbons
client.SendRaw(fmt.Sprintf( client.SendRaw(fmt.Sprintf(
`<iq xmlns='jabber:client' `<iq xmlns='jabber:client'
@@ -526,6 +530,8 @@ func main() {
node := item.Any node := item.Any
autojoin := false autojoin := false
name := "" name := ""
password := ""
nick := loadedConfig.Nick
for _, attr := range node.Attrs { for _, attr := range node.Attrs {
if attr.Name.Local == "autojoin" { if attr.Name.Local == "autojoin" {
autojoin = attr.Value == "true" autojoin = attr.Value == "true"
@@ -540,27 +546,6 @@ func main() {
} }
} }
_, ok := tabs.Load(jid)
if !ok && autojoin {
createTab(jid, true, name)
b := gtk.NewLabel(jid)
gesture1 := gtk.NewGestureClick()
gesture1.SetButton(1)
gesture1.Connect("pressed", func() {
switchToTab(jid, &window.Window)
})
b.AddController(gesture1)
menu.Append(b)
}
}
for _, item := range res.Items.List {
jid := item.Id
node := item.Any
autojoin := false
nick := loadedConfig.Nick
for _, attr := range node.Attrs { for _, attr := range node.Attrs {
if attr.Name.Local == "autojoin" { if attr.Name.Local == "autojoin" {
autojoin = attr.Value == "true" autojoin = attr.Value == "true"
@@ -575,22 +560,40 @@ func main() {
} }
} }
if autojoin { for _, node := range node.Nodes {
err := joinMuc(client, clientroot.Session.BindJid, jid, nick, "") if node.XMLName.Local == "password" {
if err != nil { password = node.Content
panic(err) break
} }
} }
_, ok := tabs.Load(jid)
if !ok && autojoin {
joinMuc(client, clientroot.Session.BindJid, jid, nick, password)
createTab(jid, true, name)
glib.IdleAdd(func() {
b := gtk.NewLabel(jid)
gesture1 := gtk.NewGestureClick()
gesture1.SetButton(1)
gesture1.Connect("pressed", func() {
switchToTab(jid, &window.Window)
})
b.AddController(gesture1)
menu.Append(b)
})
}
} }
} }
} }
} }
} }
}) })
conc := func() { conc := func() {
time.Sleep(3 * time.Second) // time.Sleep(3 * time.Second)
connectionStatus.SetText(loadedLocale["connecting"]) connectionStatus.SetText(loadedLocale["connecting"])
connectionIcon.SetFromPaintable(clientAssets["hourglass"]) connectionIcon.SetFromPaintable(clientAssets["hourglass"])
@@ -604,8 +607,8 @@ func main() {
app := gtk.NewApplication("net.sunglocto.lambda", gio.ApplicationFlagsNone) app := gtk.NewApplication("net.sunglocto.lambda", gio.ApplicationFlagsNone)
app.ConnectActivate(func() { app.ConnectActivate(func() {
go conc()
activate(app) activate(app)
go conc()
}) })
if code := app.Run(os.Args); code > 0 { if code := app.Run(os.Args); code > 0 {
@@ -633,8 +636,26 @@ func activate(app *gtk.Application) {
aboutAction := gio.NewSimpleAction("about", nil) aboutAction := gio.NewSimpleAction("about", nil)
aboutAction.ConnectActivate(func(p *glib.Variant) { aboutAction.ConnectActivate(func(p *glib.Variant) {
a := gtk.AboutDialog{} a := gtk.NewAboutDialog()
a.SetVisible(true) about_window := gtk.NewWindow()
about_window.SetTransientFor(&window.Window)
about_window.SetTitle(fmt.Sprintf("%s %s", "About", loadedLocale["appName"]))
a.SetProgramName("Lambda")
a.SetVersion(lambda_version)
a.SetComments("yet another XMPP client")
a.SetAuthors([]string{"Sunglocto"})
a.SetLicense("GPL3")
a.SetWebsite("https://forge.sunglocto.net/sunglocto/lambda")
a.SetWebsiteLabel("Website")
/*
a.ConnectResponse(func() {
about_window.SetVisible(false)
})
*/
about_window.SetChild(a)
about_window.SetDefaultSize(400, 300)
about_window.SetVisible(true)
}) })
destroymucAction := gio.NewSimpleAction("destroymuc", nil) destroymucAction := gio.NewSimpleAction("destroymuc", nil)
@@ -743,7 +764,16 @@ func activate(app *gtk.Application) {
nick_entry.SetText(loadedConfig.Nick) nick_entry.SetText(loadedConfig.Nick)
create_jid := gtk.NewImageFromPaintable(clientAssets["jabber"])
gesture := gtk.NewGestureClick()
gesture.SetButton(1)
gesture.Connect("pressed", func() {
jidBuilder(jid_entry)
})
create_jid.AddController(gesture)
jid_box.Append(gtk.NewLabel(loadedLocale["joinMUCJIDEntry"])) jid_box.Append(gtk.NewLabel(loadedLocale["joinMUCJIDEntry"]))
jid_box.Append(create_jid)
jid_box.Append(jid_entry) jid_box.Append(jid_entry)
nick_box.Append(gtk.NewLabel(loadedLocale["joinMUCNickEntry"])) nick_box.Append(gtk.NewLabel(loadedLocale["joinMUCNickEntry"]))
@@ -946,7 +976,7 @@ func activate(app *gtk.Application) {
app.AddAction(aboutAction) app.AddAction(aboutAction)
app.AddAction(destroymucAction) app.AddAction(destroymucAction)
the_menu.AppendSubmenu("File", fileMenu) the_menu.AppendSubmenu("MUC", fileMenu)
the_menu.AppendSubmenu("Help", helpMenu) the_menu.AppendSubmenu("Help", helpMenu)
the_menuBar := gtk.NewPopoverMenuBarFromModel(the_menu) the_menuBar := gtk.NewPopoverMenuBarFromModel(the_menu)
+45 -2
View File
@@ -1,7 +1,6 @@
package main package main
import ( import (
"fmt"
"gosrc.io/xmpp" "gosrc.io/xmpp"
"gosrc.io/xmpp/stanza" "gosrc.io/xmpp/stanza"
) )
@@ -31,7 +30,6 @@ func sendMessage(c xmpp.Sender, sendTo string, msgType stanza.StanzaType, body s
func joinMuc(c xmpp.Sender, jid string, muc string, nick string, password string) error { func joinMuc(c xmpp.Sender, jid string, muc string, nick string, password string) error {
var joinPresence stanza.Presence var joinPresence stanza.Presence
addr := muc + "/" + nick addr := muc + "/" + nick
fmt.Println(addr)
if password == "" { if password == "" {
joinPresence = stanza.Presence{ joinPresence = stanza.Presence{
Attrs: stanza.Attrs{ Attrs: stanza.Attrs{
@@ -63,6 +61,51 @@ func joinMuc(c xmpp.Sender, jid string, muc string, nick string, password string
return nil return nil
} }
func joinMucWithoutHistory(c xmpp.Sender, jid string, muc string, nick string, password string) error {
var joinPresence stanza.Presence
addr := muc + "/" + nick
if password == "" {
joinPresence = stanza.Presence{
Attrs: stanza.Attrs{
From: jid,
To: addr,
},
Extensions: []stanza.PresExtension{
&stanza.MucPresence{
History: stanza.History{
MaxChars: stanza.NewNullableInt(0),
MaxStanzas: stanza.NewNullableInt(0),
Seconds: stanza.NewNullableInt(0),
},
},
},
}
} else {
joinPresence = stanza.Presence{
Attrs: stanza.Attrs{
From: jid,
To: addr,
},
Extensions: []stanza.PresExtension{
&stanza.MucPresence{
Password: password,
History: stanza.History{
MaxChars: stanza.NewNullableInt(0),
MaxStanzas: stanza.NewNullableInt(0),
Seconds: stanza.NewNullableInt(0),
},
},
},
}
}
err := client.Send(joinPresence)
if err != nil {
return err
}
return nil
}
// jid MustParse but using gosrc's instead of mellium // jid MustParse but using gosrc's instead of mellium
// This function will panic if its an invalid JID // This function will panic if its an invalid JID