Rest in peace Dolly Patron

This commit is contained in:
2026-08-25 20:17:23 +01:00
parent 2717137270
commit a6e74ec808
52 changed files with 675 additions and 462 deletions
+167 -79
View File
@@ -4,6 +4,11 @@ import (
"bytes"
"context"
"fmt"
"image"
"image/color"
"image/png"
"strconv"
"github.com/boxes-ltd/imaging"
"github.com/crazy3lf/colorconv"
"github.com/diamondburned/gotk4/pkg/gdk/v4"
@@ -14,20 +19,45 @@ import (
"github.com/google/uuid"
"github.com/rrivera/identicon"
"gosrc.io/xmpp/stanza"
"image"
"image/color"
"image/png"
xmpp_color "mellium.im/xmpp/color"
"strconv"
)
func init() {
func createTabBox(name, jid, av_hash string, removable bool) *gtk.Box {
box := gtk.NewBox(gtk.OrientationHorizontal, 10)
name_label := gtk.NewLabel(name)
left_click_gesture := gtk.NewGestureClick()
left_click_gesture.SetButton(1)
left_click_gesture.Connect("pressed", func() {
switchToTab(jid, &window.Window)
})
box.AddController(left_click_gesture)
if removable {
right_click_gesture := gtk.NewGestureClick()
right_click_gesture.SetButton(3)
right_click_gesture.Connect("pressed", func() {
removeTab(jid, &window.Window)
box.SetVisible(false)
})
box.AddController(right_click_gesture)
}
box.Append(name_label)
image := gtk.NewImage()
image.SetPixelSize(40)
go getAvatar(jid, av_hash, image)
box.Prepend(image)
return box
}
func showUserWindow(u stanza.Presence, custom_nick string) {
var ocu OccupantID
u.Get(&ocu)
var caps stanza.Caps
u.Get(&caps)
var id string
id = JidMustParse(u.From).Resource
@@ -67,7 +97,7 @@ func showUserWindow(u stanza.Presence, custom_nick string) {
}
if loadedConfig.BlockingOI[ocu.ID] {
blocked_note := gtk.NewLabel("You have blocked this user!")
blocked_note := gtk.NewLabel(loadedLocale["blockedUserNotice"])
blocked_note.AddCSSClass("blocked")
profile_box.Prepend(blocked_note)
}
@@ -82,7 +112,7 @@ func showUserWindow(u stanza.Presence, custom_nick string) {
Type: "get",
From: clientroot.Session.BindJid,
To: u.From,
Id: uuid.New().String(),
Id: uuid.NewString(),
Lang: "en",
})
@@ -135,7 +165,7 @@ func showUserWindow(u stanza.Presence, custom_nick string) {
Type: "get",
From: clientroot.Session.BindJid,
To: u.From,
Id: uuid.New().String(),
Id: uuid.NewString(),
Lang: "en",
})
@@ -210,30 +240,43 @@ func showUserWindow(u stanza.Presence, custom_nick string) {
mmmm, ok := mmm.Load(id)
im := gtk.NewImage()
im.SetPixelSize(160)
im.AddCSSClass("author_img")
profile_box.Prepend(im)
createIdenticon(u.From, false, im)
if ok {
pres := mmmm.(stanza.Presence)
var vu VCardUpdate
pres.Get(&vu)
if vu.Photo != "" {
getAvatar(u.From, vu.Photo, im)
im.SetPixelSize(80)
im.AddCSSClass("author_img")
profile_box.Prepend(im)
} else {
createIdenticon(u.From, false, im)
im.SetPixelSize(80)
im.AddCSSClass("author_img")
profile_box.Prepend(im)
}
} else {
createIdenticon(u.From, false, im)
im.SetPixelSize(80)
im.AddCSSClass("author_img")
profile_box.Prepend(im)
}
}()
if caps.Hash != "" {
cap_box := gtk.NewBox(gtk.OrientationHorizontal, 2)
cap_box.SetHAlign(gtk.AlignCenter)
icon := gtk.NewImageFromPaintable(clientAssetsLoad("cap"))
icon.SetCursor(gdk.NewCursorFromName("pointer", nil))
cap_box.Append(icon)
cap_box.Append(gtk.NewLabel(fmt.Sprintf("Entity capabilities: Node %s", caps.Node)))
hasher := gtk.NewLabel(fmt.Sprintf("Using %s hash: %s", caps.Hash, caps.Ver))
hasher.SetVisible(false)
cap_box.Append(hasher)
left_click_gesture := gtk.NewGestureClick()
left_click_gesture.SetButton(1)
left_click_gesture.Connect("pressed", func() {
hasher.SetVisible(!(hasher.Visible()))
})
icon.AddController(left_click_gesture)
profile_box.Append(cap_box)
}
win.SetChild(profile_box)
win.SetTransientFor(win)
win.Present()
@@ -311,37 +354,30 @@ func switchToTab(jid string, w *gtk.Window) {
typed_tab := tab.(*chatTab)
scroller.SetChild(typed_tab.msgs)
typingStatus.SetText("")
createFailBox := func() {
box := gtk.NewBox(gtk.OrientationVertical, 10)
failed_icon := gtk.NewImageFromPaintable(clientAssetsLoad("fail"))
failed_icon.SetPixelSize(100)
box.Append(failed_icon)
jid_label := gtk.NewLabel(jid)
jid_label.AddCSSClass("author")
jid_label.AddCSSClass("jid")
box.Append(jid_label)
label := gtk.NewLabel(loadedLocale["mucLoadError"])
label.SetWrap(true)
box.Append(label)
memberList.SetChild(box)
}
if typed_tab.isMuc {
m, ok := mucmembers.Load(jid)
if !ok {
box := gtk.NewBox(gtk.OrientationVertical, 10)
failed_icon := gtk.NewImageFromPaintable(clientAssetsLoad("fail"))
failed_icon.SetPixelSize(100)
box.Append(failed_icon)
jid_label := gtk.NewLabel(jid)
jid_label.AddCSSClass("author")
jid_label.AddCSSClass("jid")
box.Append(jid_label)
label := gtk.NewLabel("There was an error loading the members of this room. Maybe the MUC does not give user presence, it does not exist, you have been banned from it, or you have to fill a CAPTCHA to access it. Try joining the room again or check if the room exists.")
label.SetWrap(true)
box.Append(label)
memberList.SetChild(box)
createFailBox()
return
}
ma, ok := m.(mucUnit)
if !ok {
box := gtk.NewBox(gtk.OrientationVertical, 10)
failed_icon := gtk.NewImageFromPaintable(clientAssetsLoad("fail"))
failed_icon.SetPixelSize(100)
box.Append(failed_icon)
jid_label := gtk.NewLabel(jid)
jid_label.AddCSSClass("author")
jid_label.AddCSSClass("jid")
box.Append(jid_label)
label := gtk.NewLabel("There was an error loading the members of this room. Maybe the MUC does not give user presence, it does not exist, you have been banned from it, or you have to fill a CAPTCHA to access it. Try joining the room again or check if the room exists.")
label.SetWrap(true)
box.Append(label)
memberList.SetChild(box)
createFailBox()
return
}
mm := ma.Members
@@ -370,11 +406,6 @@ func switchToTab(jid string, w *gtk.Window) {
gen.Append(gtk.NewSeparator(gtk.OrientationHorizontal))
}
//id := ocu.ID
//if id == "" {
// id := JidMustParse(u.From).Resource
//}
nick_label := gtk.NewLabel(JidMustParse(u.From).Resource)
var custom_nick string
@@ -403,7 +434,7 @@ func switchToTab(jid string, w *gtk.Window) {
nick_label.SetOpacity(0.5)
}
userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\n%s", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation, loadedLocale["clickForMoreInfo"]))
// userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\n%s", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation, loadedLocale["clickForMoreInfo"]))
userbox.Append(nick_label)
var hats Hats
@@ -418,7 +449,6 @@ func switchToTab(jid string, w *gtk.Window) {
xc := xmpp_color.String(hat.URI, 255, loadedConfig.CVD)
r, g, b, _ := xc.RGBA()
val, _, _ = colorconv.RGBToHSV(uint8(r), uint8(g), uint8(b))
}
tB := tagBytes
img, _, _ := image.Decode(bytes.NewReader(tB))
@@ -452,12 +482,26 @@ func switchToTab(jid string, w *gtk.Window) {
userbox.Append(status_message)
}
end_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
var caps stanza.Caps
ok = u.Get(&caps)
if ok {
client_icon := gtk.NewImageFromPaintable(clientAssetsLoad(caps.Node))
//client_icon.SetHAlign(gtk.AlignEnd)
//client_icon.SetHExpand(true)
end_box.Append(client_icon)
}
medal := gtk.NewImageFromPaintable(clientAssetsLoad(mu.MucUserItem.Affiliation))
medal.SetTooltipText(mu.MucUserItem.Affiliation)
medal.SetHAlign(gtk.AlignEnd)
medal.SetHExpand(true)
userbox.Append(medal)
//medal.SetHAlign(gtk.AlignEnd)
//medal.SetHExpand(true)
end_box.Append(medal)
end_box.SetHAlign(gtk.AlignEnd)
end_box.SetHExpand(true)
userbox.Append(end_box)
if loadedConfig.ShowAvatarsInMemberList {
default_av := gtk.NewImage()
@@ -479,6 +523,52 @@ func switchToTab(jid string, w *gtk.Window) {
}
}
userbox.SetHasTooltip(true)
userbox.Connect("query-tooltip", func(
x int,
y int,
keyboardMode bool,
tooltip *gtk.Tooltip,
) bool {
box := gtk.NewBox(gtk.OrientationHorizontal, 4)
av_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
if loadedConfig.ShowAvatarsInMemberList {
default_av := gtk.NewImage()
default_av.SetPixelSize(100)
createIdenticon(u.From, false, default_av)
av_box.Prepend(default_av)
var vcu VCardUpdate
ok = u.Get(&vcu)
if ok {
photo := vcu.Photo
go func() {
glib.IdleAdd(func() {
new_im := gtk.NewImage()
new_im.SetPixelSize(100)
av_box.Remove(default_av)
av_box.Append(new_im)
go getAvatar(u.From, photo, new_im)
})
}()
}
}
box.Append(av_box)
content_box := gtk.NewBox(gtk.OrientationVertical, 4)
content_box.Append(gtk.NewLabel(u.From))
content_box.Append(gtk.NewLabel(mu.MucUserItem.Role))
content_box.Append(gtk.NewLabel(mu.MucUserItem.Affiliation))
content_box.Append(gtk.NewLabel(mu.MucUserItem.JID))
box.Append(content_box)
tooltip.SetCustom(box)
return true
})
gesture := gtk.NewGestureClick()
gesture.SetButton(1)
@@ -498,7 +588,7 @@ func switchToTab(jid string, w *gtk.Window) {
kb.ConnectClicked(func() {
client.SendRaw(fmt.Sprintf(`
<iq from='%s'
id='kick1'
id='%s'
to='%s'
type='set'>
<query xmlns='http://jabber.org/protocol/muc#admin'>
@@ -506,7 +596,7 @@ func switchToTab(jid string, w *gtk.Window) {
</item>
</query>
</iq>
`, clientroot.Session.BindJid, jid, JidMustParse(u.From).Resource))
`, clientroot.Session.BindJid, uuid.NewString(), jid, JidMustParse(u.From).Resource))
})
bb.ConnectClicked(func() {
@@ -516,14 +606,14 @@ func switchToTab(jid string, w *gtk.Window) {
if mu.MucUserItem.JID != "" {
client.SendRaw(fmt.Sprintf(`
<iq from='%s'
id='ban1'
id='%s'
to='%s'
type='set'>
<query xmlns='http://jabber.org/protocol/muc#admin'>
<item affiliation='outcast' jid='%s'/>
</query>
</iq>
`, clientroot.Session.BindJid, jid, JidMustParse(mu.MucUserItem.JID).Bare()))
`, clientroot.Session.BindJid, uuid.NewString(), jid, JidMustParse(mu.MucUserItem.JID).Bare()))
}
}
})
@@ -547,14 +637,14 @@ func switchToTab(jid string, w *gtk.Window) {
submit.ConnectClicked(func() {
client.SendRaw(fmt.Sprintf(`
<iq from='%s'
id='ba1'
id='%s'
to='%s'
type='set'>
<query xmlns='http://jabber.org/protocol/muc#admin'>
<item affiliation='%s' jid='%s'/>
</query>
</iq>
`, clientroot.Session.BindJid, jid, the_entry.Text(), JidMustParse(mu.MucUserItem.JID).Bare()))
`, clientroot.Session.BindJid, uuid.NewString(), jid, the_entry.Text(), JidMustParse(mu.MucUserItem.JID).Bare()))
win.Destroy()
})
@@ -588,7 +678,7 @@ func switchToTab(jid string, w *gtk.Window) {
client.SendRaw(fmt.Sprintf(`
<iq from='%s'
id='kick1'
id='%s'
to='%s'
type='set'>
<query xmlns='http://jabber.org/protocol/muc#admin'>
@@ -596,7 +686,7 @@ func switchToTab(jid string, w *gtk.Window) {
</item>
</query>
</iq>
`, clientroot.Session.BindJid, jid, JidMustParse(u.From).Resource, the_entry.Text()))
`, clientroot.Session.BindJid, uuid.NewString(), jid, JidMustParse(u.From).Resource, the_entry.Text()))
})
box.Append(the_entry)
@@ -642,7 +732,13 @@ func switchToTab(jid string, w *gtk.Window) {
} else {
headerBox.Append(gtk.NewImageFromPaintable(clientAssetsLoad("group")))
}
headerBox.Append(gtk.NewLabel(fmt.Sprintf("%d %s", i, loadedLocale["participants"])))
pword := loadedLocale["participants"]
if i == 1 {
pword = loadedLocale["participant"]
}
headerBox.Append(gtk.NewLabel(fmt.Sprintf("%d %s", i, pword)))
gen.Prepend(headerBox)
muc_presence := typed_tab.muc_presence
@@ -717,37 +813,29 @@ func createIdenticon(word string, always_create bool, i *gtk.Image) { // This fu
loader.Close()
p := loader.Pixbuf()
gt := gdk.NewTextureForPixbuf(p)
/*
i := gtk.NewImageFromPaintable(gt)
i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
return i
*/
glib.IdleAdd(func() {
i.SetFromPaintable(gt)
i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
})
}
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.SetTitle(loadedLocale["buildAJID"])
win.SetDefaultSize(400, 1)
win.SetResizable(false)
box := gtk.NewBox(gtk.OrientationVertical, 2)
header := gtk.NewLabel("Build-A-JID")
header := gtk.NewLabel(loadedLocale["buildAJID"])
header.AddCSSClass("author")
box.Append(header)
box.Append(gtk.NewLabel("All fields except for domain are optional"))
box.Append(gtk.NewLabel(loadedLocale["buildAJIDOptionalFieldsNote"]))
jid_builder := gtk.NewBox(gtk.OrientationHorizontal, 2)
localPartEntry := gtk.NewEntry()
localPartEntry.SetPlaceholderText("localpart")
localPartEntry.SetPlaceholderText(loadedLocale["BAJlocalpart"])
jid_builder.Append(localPartEntry)
at_sign := gtk.NewLabel("@")
@@ -755,7 +843,7 @@ func jidBuilder(en *gtk.Entry) { // This function spawns a window that allows th
jid_builder.Append(at_sign)
domainEntry := gtk.NewEntry()
domainEntry.SetPlaceholderText("domain")
domainEntry.SetPlaceholderText(loadedLocale["BAJdomain"])
jid_builder.Append(domainEntry)
resource_sign := gtk.NewLabel("/")
@@ -763,7 +851,7 @@ func jidBuilder(en *gtk.Entry) { // This function spawns a window that allows th
jid_builder.Append(resource_sign)
resourceEntry := gtk.NewEntry()
resourceEntry.SetPlaceholderText("resource")
resourceEntry.SetPlaceholderText(loadedLocale["BAJresource"])
jid_builder.Append(resourceEntry)
box.Append(jid_builder)