format + allow closing

This commit is contained in:
2026-05-09 07:33:47 +01:00
parent 9bbefbcea8
commit 39cdd5e6c1
5 changed files with 112 additions and 54 deletions
-1
View File
@@ -140,7 +140,6 @@ var moderateBytes []byte
//go:embed assets/jabber.png //go:embed assets/jabber.png
var jabberBytes []byte var jabberBytes []byte
//go:embed assets/fail.png //go:embed assets/fail.png
var failBytes []byte var failBytes []byte
+32 -3
View File
@@ -14,9 +14,9 @@ import (
"github.com/rrivera/identicon" "github.com/rrivera/identicon"
"gosrc.io/xmpp/stanza" "gosrc.io/xmpp/stanza"
"image" "image"
"image/color"
"image/png" "image/png"
xmpp_color "mellium.im/xmpp/color" xmpp_color "mellium.im/xmpp/color"
"image/color"
"strconv" "strconv"
) )
@@ -54,6 +54,34 @@ func createTab(jid string, isMuc bool, name string) bool {
return false return false
} }
func removeTab(jid string, w *gtk.Window) {
t, ok := tabs.Load(jid)
if ok {
tab := t.(*chatTab)
tab.msgs.RemoveAll()
if tab.isMuc {
client.SendRaw(fmt.Sprintf(`
<presence from='%s' to='%s' type='unavailable'>
<x xmlns='http://jabber.org/protocol/muc'/>
</presence>
`, clientroot.Session.BindJid, jid+"/"+tab.current_nick))
}
tabs.Delete(jid)
mucmembers.Delete(jid)
userdevices.Delete(jid)
if current == jid {
current = ""
scroller.SetChild(empty_dialog)
typingStatus.SetText("")
memberList.SetChild(gtk.NewLabel(""))
}
mucmembers.Delete(jid)
}
}
func switchToTab(jid string, w *gtk.Window) { func switchToTab(jid string, w *gtk.Window) {
current = jid current = jid
tab, ok := tabs.Load(current) tab, ok := tabs.Load(current)
@@ -160,7 +188,6 @@ func switchToTab(jid string, w *gtk.Window) {
} }
} }
status := gtk.NewImageFromPaintable(clientAssets["status_"+string(u.Show)]) status := gtk.NewImageFromPaintable(clientAssets["status_"+string(u.Show)])
status.SetTooltipText(string(u.Show)) status.SetTooltipText(string(u.Show))
@@ -521,6 +548,7 @@ func switchToTab(jid string, w *gtk.Window) {
gen.Prepend(muci) gen.Prepend(muci)
muc_name := gtk.NewLabel(typed_tab.name) muc_name := gtk.NewLabel(typed_tab.name)
muc_name.AddCSSClass("author") muc_name.AddCSSClass("author")
muc_name.SetWrap(true)
gen.Prepend(muc_name) gen.Prepend(muc_name)
memberList.SetChild(gen) memberList.SetChild(gen)
} else { } else {
@@ -529,7 +557,7 @@ func switchToTab(jid string, w *gtk.Window) {
} }
func showErrorDialog(err error) { func showErrorDialog(err error, w *gtk.Window) {
err_win := gtk.NewWindow() err_win := gtk.NewWindow()
err_win.SetTitle(loadedLocale["error"]) err_win.SetTitle(loadedLocale["error"])
err_win.SetDefaultSize(400, 200) err_win.SetDefaultSize(400, 200)
@@ -546,6 +574,7 @@ func showErrorDialog(err error) {
}) })
box.Append(close_btn) box.Append(close_btn)
err_win.SetChild(box) err_win.SetChild(box)
err_win.SetTransientFor(w)
err_win.Present() err_win.Present()
} }
+28 -31
View File
@@ -92,46 +92,45 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
popover.SetParent(mainBox) popover.SetParent(mainBox)
popover.SetHasArrow(false) popover.SetHasArrow(false)
gesture.Connect("pressed", func(n_press, x, y int) { gesture.Connect("pressed", func(n_press, x, y int) {
rc_box := gtk.NewBox(gtk.OrientationVertical, 0) rc_box := gtk.NewBox(gtk.OrientationVertical, 0)
reactions := gtk.NewBox(gtk.OrientationHorizontal, 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") // TODO: Implement proper support for reactions via extension 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'>
<reaction>%s</reaction> <reaction>%s</reaction>
</reactions> </reactions>
</message> </message>
`, m.To, jid.MustParse(m.From).Bare().String(), uuid.New().String(), m.Type, sid.ID, v)) `, m.To, jid.MustParse(m.From).Bare().String(), uuid.New().String(), m.Type, sid.ID, v))
}) })
reactions.Append(like) reactions.Append(like)
}
rc_box.Append(reactions)
quote := gtk.NewButtonWithLabel("Quote")
quote.ConnectClicked(func() {
lines := strings.Split(m.Body, "\n")
for i, line := range lines {
quoteline := "> " + line
lines[i] = quoteline
} }
newstr := strings.Join(lines, "\n") + "\n\n" rc_box.Append(reactions)
message_en.SetText(newstr) quote := gtk.NewButtonWithLabel("Quote")
}) quote.ConnectClicked(func() {
rc_box.Append(quote) lines := strings.Split(m.Body, "\n")
for i, line := range lines {
quoteline := "> " + line
lines[i] = quoteline
}
popover.SetChild(rc_box) newstr := strings.Join(lines, "\n") + "\n\n"
message_en.SetText(newstr)
})
rc_box.Append(quote)
popover.SetChild(rc_box)
rect := gdk.NewRectangle(x, y, 1, 1) rect := gdk.NewRectangle(x, y, 1, 1)
popover.SetPointingTo(&rect) popover.SetPointingTo(&rect)
popover.Popup() popover.Popup()
@@ -233,7 +232,6 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
mainBox.Append(authorBox) mainBox.Append(authorBox)
mainBox.Append(contentBox) mainBox.Append(contentBox)
oob := stanza.OOB{} oob := stanza.OOB{}
ok = m.Get(&oob) ok = m.Get(&oob)
if ok { if ok {
@@ -315,7 +313,7 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou
invalidImages.Store(oghash, true) invalidImages.Store(oghash, true)
return createIdenticon(j, false) return createIdenticon(j, false)
} }
i.AddCSSClass(loadedConfig.CVD.String() + "_CVD") // i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
return i return i
} }
@@ -346,7 +344,6 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou
base64_data := card.Photo.Binval base64_data := card.Photo.Binval
if card.Photo.Binval == "" || ((card.Photo.Type == "image/svg+xml" || card.Photo.Type == "image/webp") && (runtime.GOOS == "windows" || runtime.GOOS == "netbsd")) { if card.Photo.Binval == "" || ((card.Photo.Type == "image/svg+xml" || card.Photo.Type == "image/webp") && (runtime.GOOS == "windows" || runtime.GOOS == "netbsd")) {
fmt.Println("Blocking image")
invalidImages.Store(oghash, true) invalidImages.Store(oghash, true)
return createIdenticon(j, false) return createIdenticon(j, false)
} }
+38 -6
View File
@@ -325,6 +325,7 @@ func main() {
ok = presence.Get(&mu) ok = presence.Get(&mu)
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)
// id := ocu.ID // id := ocu.ID
// if id == "" { // if id == "" {
@@ -333,7 +334,7 @@ func main() {
from, _ := stanza.NewJid(presence.From) from, _ := stanza.NewJid(presence.From)
muc := from.Bare() muc := from.Bare()
_, ok = mucmembers.Load(muc) _, ok = mucmembers.Load(muc)
if !ok { if !ok && presence.Type != "unavailable" {
mucmembers.Store(muc, mucUnit{}) mucmembers.Store(muc, mucUnit{})
} }
@@ -344,6 +345,14 @@ func main() {
typed_unit := unit.(mucUnit) typed_unit := unit.(mucUnit)
if mu.MucUserItem.JID == clientroot.Session.BindJid {
tab, ok := tabs.Load(muc)
if ok {
typed_tab := tab.(*chatTab)
typed_tab.current_nick = id
}
}
if presence.Type != "unavailable" { if presence.Type != "unavailable" {
_, ok := typed_unit.Members.Load(id) _, ok := typed_unit.Members.Load(id)
if !ok && loadedConfig.ShowPresenceUpdates { if !ok && loadedConfig.ShowPresenceUpdates {
@@ -408,7 +417,7 @@ func main() {
}) })
if err != nil { if err != nil {
showErrorDialog(err) showErrorDialog(err, &window.Window)
panic(err) panic(err)
} }
client = c client = c
@@ -524,6 +533,13 @@ func main() {
gesture1.Connect("pressed", func() { gesture1.Connect("pressed", func() {
switchToTab(jid, &window.Window) switchToTab(jid, &window.Window)
}) })
gesture2 := gtk.NewGestureClick()
gesture2.SetButton(3)
gesture2.Connect("pressed", func() {
removeTab(jid, &window.Window)
box.SetVisible(false)
})
box.Append(b) box.Append(b)
go func() { go func() {
new_im := getAvatar(jid, jid) // TODO: Use PEP avatar and do not use JID as hash new_im := getAvatar(jid, jid) // TODO: Use PEP avatar and do not use JID as hash
@@ -604,6 +620,13 @@ func main() {
gesture1.Connect("pressed", func() { gesture1.Connect("pressed", func() {
switchToTab(jid, &window.Window) switchToTab(jid, &window.Window)
}) })
gesture2 := gtk.NewGestureClick()
gesture2.SetButton(3)
gesture2.Connect("pressed", func() {
removeTab(jid, &window.Window)
box.SetVisible(false)
})
box.Append(b) box.Append(b)
go func() { go func() {
new_im := getAvatar(jid, jid) new_im := getAvatar(jid, jid)
@@ -614,6 +637,7 @@ func main() {
}() }()
box.AddController(gesture1) box.AddController(gesture1)
box.AddController(gesture2)
menu.Append(box) menu.Append(box)
menu.Append(gtk.NewSeparator(gtk.OrientationHorizontal)) menu.Append(gtk.NewSeparator(gtk.OrientationHorizontal))
}) })
@@ -840,7 +864,7 @@ func activate(app *gtk.Application) {
jm := func(n string, pw string) { jm := func(n string, pw string) {
err := joinMuc(client, clientroot.Session.BindJid, t, nick_entry.Text(), pw) err := joinMuc(client, clientroot.Session.BindJid, t, nick_entry.Text(), pw)
if err != nil { if err != nil {
showErrorDialog(err) showErrorDialog(err, win)
return return
} }
@@ -861,7 +885,15 @@ func activate(app *gtk.Application) {
switchToTab(t, &window.Window) switchToTab(t, &window.Window)
}) })
gesture2 := gtk.NewGestureClick()
gesture2.SetButton(3)
gesture2.Connect("pressed", func() {
removeTab(t, &window.Window)
box.SetVisible(false)
})
box.AddController(gesture1) box.AddController(gesture1)
box.AddController(gesture2)
menu.Append(box) menu.Append(box)
menu.Append(gtk.NewSeparator(gtk.OrientationHorizontal)) menu.Append(gtk.NewSeparator(gtk.OrientationHorizontal))
} }
@@ -999,9 +1031,9 @@ func activate(app *gtk.Application) {
} else { } else {
allowed = false allowed = false
if result.Error != nil { if result.Error != nil {
showErrorDialog(fmt.Errorf("%s: %s - %s", loadedLocale["discoFail"], result.Error.Reason, result.Error.Text)) showErrorDialog(fmt.Errorf("%s: %s - %s", loadedLocale["discoFail"], result.Error.Reason, result.Error.Text), win)
} else { } else {
showErrorDialog(fmt.Errorf(loadedLocale["discoFail"])) showErrorDialog(fmt.Errorf(loadedLocale["discoFail"]), win)
} }
} }
} }
@@ -1227,7 +1259,7 @@ func activate(app *gtk.Application) {
err := sendMessage(client, current, message_type, t, "", "", exts) err := sendMessage(client, current, message_type, t, "", "", exts)
if err != nil { if err != nil {
showErrorDialog(err) showErrorDialog(err, &window.Window)
} }
message_en.SetText("") message_en.SetText("")
scrollToBottomAfterUpdate(scroller) scrollToBottomAfterUpdate(scroller)
+14 -13
View File
@@ -7,22 +7,23 @@ import (
) )
type chatTab struct { type chatTab struct {
isMuc bool isMuc bool
msgs *gtk.ListBox msgs *gtk.ListBox
name string name string
current_nick string
} }
type lambdaConfig struct { type lambdaConfig struct {
Server string Server string
Username string Username string
Resource string Resource string
Password string Password string
Insecure bool Insecure bool
Nick string Nick string
JoinBookmarks bool JoinBookmarks bool
CVD color.CVD CVD color.CVD
Identicons bool Identicons bool
Debug bool Debug bool
ShowPresenceUpdates bool ShowPresenceUpdates bool
} }