2026-01-29 21:35:36 +00:00
package main
import (
2026-04-06 11:04:53 +01:00
"bytes"
2026-01-31 15:08:54 +00:00
"context"
2026-01-30 15:56:58 +00:00
"fmt"
2026-04-06 11:04:53 +01:00
"github.com/boxes-ltd/imaging"
"github.com/crazy3lf/colorconv"
2026-03-15 09:55:26 +00:00
"github.com/diamondburned/gotk4/pkg/gdk/v4"
2026-04-06 11:04:53 +01:00
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
2026-01-29 21:35:36 +00:00
"github.com/diamondburned/gotk4/pkg/glib/v2"
"github.com/diamondburned/gotk4/pkg/gtk/v4"
2026-02-03 10:38:41 +00:00
"github.com/diamondburned/gotk4/pkg/pango"
2026-04-06 11:04:53 +01:00
"github.com/rrivera/identicon"
2026-01-30 10:40:38 +00:00
"gosrc.io/xmpp/stanza"
2026-04-06 11:04:53 +01:00
"image"
2026-05-09 07:33:47 +01:00
"image/color"
2026-04-06 11:04:53 +01:00
"image/png"
xmpp_color "mellium.im/xmpp/color"
"strconv"
2026-01-29 21:35:36 +00:00
)
2026-05-08 06:56:51 +01:00
func init ( ) {
}
2026-01-29 21:35:36 +00:00
func scrollToBottomAfterUpdate ( scrolledWindow * gtk . ScrolledWindow ) {
glib . IdleAdd ( func ( ) bool {
vAdj := scrolledWindow . VAdjustment ( )
vAdj . SetValue ( vAdj . Upper ( ) - vAdj . PageSize ( ) )
return false // Return false to run only once
} )
}
2026-04-26 10:40:13 +01:00
func createTab ( jid string , isMuc bool , name string ) bool {
2026-05-03 11:10:15 +01:00
if name == "" {
name = jid
}
2026-01-30 21:31:33 +00:00
_ , ok := tabs . Load ( jid )
2026-02-03 10:38:41 +00:00
_ , uok := userdevices . Load ( jid )
_ , mok := mucmembers . Load ( jid )
if ! ok && ! uok && ! mok {
2026-01-29 21:35:36 +00:00
newTab := new ( chatTab )
newTab . isMuc = isMuc
newTab . msgs = gtk . NewListBox ( )
2026-05-19 14:07:21 +01:00
glib . IdleAdd ( func ( ) {
newTab . msgs . SetVExpand ( true )
newTab . msgs . SetShowSeparators ( true )
newTab . msgs . Append ( gtk . NewButtonWithLabel ( loadedLocale [ "getPastMessages" ] ) )
} )
2026-04-26 10:40:13 +01:00
newTab . name = name
2026-01-29 21:35:36 +00:00
2026-01-30 21:31:33 +00:00
tabs . Store ( jid , newTab )
2026-02-01 13:49:30 +00:00
return true
2026-01-29 21:35:36 +00:00
}
2026-02-01 13:49:30 +00:00
return false
2026-01-29 21:35:36 +00:00
}
2026-05-09 07:33:47 +01:00
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 )
}
}
2026-01-31 15:38:02 +00:00
func switchToTab ( jid string , w * gtk . Window ) {
2026-01-29 21:35:36 +00:00
current = jid
2026-01-30 21:31:33 +00:00
tab , ok := tabs . Load ( current )
if ! ok {
return
}
2026-01-30 10:40:38 +00:00
2026-01-30 21:31:33 +00:00
typed_tab := tab . ( * chatTab )
scroller . SetChild ( typed_tab . msgs )
2026-04-18 10:29:07 +01:00
typingStatus . SetText ( "" )
2026-01-30 21:31:33 +00:00
if typed_tab . isMuc {
2026-02-04 10:12:49 +00:00
m , ok := mucmembers . Load ( jid )
if ! ok {
2026-05-08 06:56:51 +01:00
box := gtk . NewBox ( gtk . OrientationVertical , 10 )
2026-05-23 06:45:18 +01:00
failed_icon := gtk . NewImageFromPaintable ( clientAssetsLoad ( "fail" ) )
2026-05-08 06:56:51 +01:00
failed_icon . SetPixelSize ( 100 )
box . Append ( failed_icon )
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 )
2026-02-04 10:12:49 +00:00
return
}
ma , ok := m . ( mucUnit )
if ! ok {
2026-05-08 06:56:51 +01:00
box := gtk . NewBox ( gtk . OrientationVertical , 10 )
2026-05-23 06:45:18 +01:00
failed_icon := gtk . NewImageFromPaintable ( clientAssetsLoad ( "fail" ) )
2026-05-08 06:56:51 +01:00
failed_icon . SetPixelSize ( 100 )
box . Append ( failed_icon )
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 )
2026-02-04 10:12:49 +00:00
return
}
2026-01-30 21:31:33 +00:00
mm := ma . Members
2026-04-28 12:58:00 +01:00
gen := gtk . NewBox ( gtk . OrientationVertical , 10 )
2026-01-30 15:56:58 +00:00
2026-02-15 13:08:52 +00:00
i := 0
2026-04-06 11:04:53 +01:00
rangeOrdered ( & mm , ( func ( k , v any ) bool {
2026-02-15 13:08:52 +00:00
i ++
2026-04-30 14:55:17 +01:00
u , ok := v . ( stanza . Presence )
if ! ok {
return true
}
2026-04-28 12:58:00 +01:00
userbox := gtk . NewBox ( gtk . OrientationHorizontal , 2 )
2026-01-30 15:56:58 +00:00
2026-01-30 21:31:33 +00:00
var mu MucUser
var ocu OccupantID
u . Get ( & mu )
u . Get ( & ocu )
2026-04-06 11:04:53 +01:00
if mu . MucUserItem . Role == "moderator" {
2026-05-10 14:46:19 +01:00
gen . Prepend ( gtk . NewSeparator ( gtk . OrientationHorizontal ) )
2026-04-06 11:04:53 +01:00
gen . Prepend ( userbox )
} else {
gen . Append ( userbox )
2026-05-10 14:46:19 +01:00
gen . Append ( gtk . NewSeparator ( gtk . OrientationHorizontal ) )
2026-04-06 11:04:53 +01:00
}
2026-03-12 16:09:22 +00:00
//id := ocu.ID
//if id == "" {
id := JidMustParse ( u . From ) . Resource
//}
2026-01-30 15:56:58 +00:00
2026-02-01 13:49:30 +00:00
nick_label := gtk . NewLabel ( JidMustParse ( u . From ) . Resource )
2026-05-10 14:46:19 +01:00
custom_nick , ok := loadedConfig . CustomNicks [ ocu . ID ]
if ok {
nick_label . SetText ( custom_nick )
}
2026-02-03 10:38:41 +00:00
nick_label . SetEllipsize ( pango . EllipsizeEnd )
2026-02-02 13:23:08 +00:00
nick_label . AddCSSClass ( mu . MucUserItem . Role )
if mu . MucUserItem . Role == "visitor" {
nick_label . SetOpacity ( 0.5 )
}
2026-04-28 12:58:00 +01:00
userbox . SetTooltipText ( fmt . Sprintf ( "%s\n%s\n%s\n%s" , u . From , mu . MucUserItem . Role , mu . MucUserItem . Affiliation , loadedLocale [ "clickForMoreInfo" ] ) )
2026-01-30 21:31:33 +00:00
userbox . Append ( nick_label )
2026-02-02 13:23:08 +00:00
2026-02-08 09:44:45 +00:00
var hats Hats
2026-04-30 14:55:17 +01:00
ok = u . Get ( & hats )
2026-02-08 09:44:45 +00:00
if ok {
for _ , hat := range hats . Hats {
2026-04-06 11:04:53 +01:00
var val float64
if hat . Hue != "" {
tval , _ := strconv . Atoi ( hat . Hue )
val = float64 ( tval )
} else {
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 ) )
i_rgba := imaging . AdjustHue ( img , val )
var buf bytes . Buffer
png . Encode ( & buf , i_rgba )
tB = buf . Bytes ( )
loader := gdkpixbuf . NewPixbufLoader ( )
loader . Write ( tB )
loader . Close ( )
tag := gtk . NewPictureForPaintable ( gdk . NewTextureForPixbuf ( loader . Pixbuf ( ) ) )
2026-02-16 09:54:37 +00:00
tag . SetTooltipText ( hat . Title )
userbox . Prepend ( tag )
2026-02-08 09:44:45 +00:00
}
}
2026-05-31 16:44:04 +01:00
status := gtk . NewImageFromPaintable ( clientAssetsLoad ( "status_" + string ( u . Show ) ) )
2026-03-15 09:55:26 +00:00
status . SetTooltipText ( string ( u . Show ) )
status . SetHAlign ( gtk . AlignEnd )
// medal.SetHExpand(true)
userbox . Prepend ( status )
2026-05-18 06:27:55 +01:00
if u . Status != "" {
2026-05-23 06:45:18 +01:00
status_message := gtk . NewImageFromPaintable ( clientAssetsLoad ( "comment" ) )
2026-05-18 06:27:55 +01:00
status_message . SetTooltipText ( u . Status )
status_message . SetHAlign ( gtk . AlignEnd )
userbox . Append ( status_message )
}
2026-05-23 06:45:18 +01:00
medal := gtk . NewImageFromPaintable ( clientAssetsLoad ( mu . MucUserItem . Affiliation ) )
2026-02-08 09:44:45 +00:00
medal . SetTooltipText ( mu . MucUserItem . Affiliation )
2026-02-02 13:23:08 +00:00
medal . SetHAlign ( gtk . AlignEnd )
medal . SetHExpand ( true )
userbox . Append ( medal )
2026-02-08 09:44:45 +00:00
2026-05-31 16:44:04 +01:00
if loadedConfig . ShowAvatarsInMemberList {
default_av := createIdenticon ( u . From , false )
userbox . Prepend ( default_av )
var vcu VCardUpdate
ok = u . Get ( & vcu )
if ok {
photo := vcu . Photo
go func ( ) {
new_im := getAvatar ( u . From , photo )
2026-05-07 09:16:38 +01:00
2026-05-31 16:44:04 +01:00
glib . IdleAdd ( func ( ) {
userbox . Remove ( default_av )
userbox . Prepend ( new_im )
} )
} ( )
2026-05-03 11:10:15 +01:00
2026-05-31 16:44:04 +01:00
}
2026-05-03 11:10:15 +01:00
}
2026-01-31 15:08:54 +00:00
gesture := gtk . NewGestureClick ( )
2026-03-15 09:55:26 +00:00
gesture . SetButton ( 1 )
mod_gesture := gtk . NewGestureClick ( )
mod_gesture . SetButton ( 3 )
popover := gtk . NewPopover ( )
popover . SetHasArrow ( false )
popover . SetParent ( userbox )
rc_box := gtk . NewBox ( gtk . OrientationVertical , 0 )
2026-04-28 12:58:00 +01:00
bb := gtk . NewButtonWithLabel ( loadedLocale [ "ban" ] )
kb := gtk . NewButtonWithLabel ( loadedLocale [ "kick" ] )
ab := gtk . NewButtonWithLabel ( loadedLocale [ "setAffil" ] )
rb := gtk . NewButtonWithLabel ( loadedLocale [ "setRole" ] )
2026-03-15 09:55:26 +00:00
kb . ConnectClicked ( func ( ) {
client . SendRaw ( fmt . Sprintf ( `
<iq from='%s'
id='kick1'
to='%s'
type='set'>
<query xmlns='http://jabber.org/protocol/muc#admin'>
<item nick='%s' role='none'>
</item>
</query>
</iq>
` , clientroot . Session . BindJid , jid , JidMustParse ( u . From ) . Resource ) )
} )
bb . ConnectClicked ( func ( ) {
var mu MucUser
ok = u . Get ( & mu )
if ok {
if mu . MucUserItem . JID != "" {
client . SendRaw ( fmt . Sprintf ( `
<iq from='%s'
id='ban1'
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 ( ) ) )
}
}
} )
ab . ConnectClicked ( func ( ) {
var mu MucUser
ok = u . Get ( & mu )
if ok {
if mu . MucUserItem . JID != "" {
2026-03-15 09:57:08 +00:00
win := gtk . NewWindow ( )
win . SetDefaultSize ( 400 , 1 )
win . SetResizable ( false )
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
box := gtk . NewBox ( gtk . OrientationVertical , 0 )
2026-04-28 12:58:00 +01:00
box . Append ( gtk . NewLabel ( loadedLocale [ "setAffilDescPartOne" ] + JidMustParse ( u . From ) . Resource + loadedLocale [ "setAffilDescPartTwo" ] ) )
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
the_entry := gtk . NewEntry ( )
the_entry . SetText ( mu . MucUserItem . Affiliation )
2026-03-15 09:55:26 +00:00
2026-04-28 12:58:00 +01:00
submit := gtk . NewButtonWithLabel ( loadedLocale [ "submit" ] )
2026-03-15 09:57:08 +00:00
submit . ConnectClicked ( func ( ) {
client . SendRaw ( fmt . Sprintf ( `
2026-03-15 09:55:26 +00:00
<iq from='%s'
id='ba1'
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 ( ) ) )
2026-03-15 09:57:08 +00:00
win . SetVisible ( false )
} )
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
box . Append ( the_entry )
box . Append ( submit )
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
win . SetChild ( box )
win . SetVisible ( true )
}
}
2026-03-15 09:55:26 +00:00
} )
rb . ConnectClicked ( func ( ) {
var mu MucUser
ok = u . Get ( & mu )
if ok {
if mu . MucUserItem . JID != "" {
2026-03-15 09:57:08 +00:00
win := gtk . NewWindow ( )
win . SetDefaultSize ( 400 , 1 )
win . SetResizable ( false )
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
box := gtk . NewBox ( gtk . OrientationVertical , 0 )
2026-04-28 12:58:00 +01:00
box . Append ( gtk . NewLabel ( loadedLocale [ "setRoleDescPartOne" ] + JidMustParse ( u . From ) . Resource + loadedLocale [ "setRoleDescPartTwo" ] ) )
box . Append ( gtk . NewLabel ( loadedLocale [ "setRoleWarning" ] ) )
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
the_entry := gtk . NewEntry ( )
the_entry . SetText ( mu . MucUserItem . Role )
2026-03-15 09:55:26 +00:00
2026-04-28 12:58:00 +01:00
submit := gtk . NewButtonWithLabel ( loadedLocale [ "submit" ] )
2026-03-15 09:57:08 +00:00
submit . ConnectClicked ( func ( ) {
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
client . SendRaw ( fmt . Sprintf ( `
2026-03-15 09:55:26 +00:00
<iq from='%s'
id='kick1'
to='%s'
type='set'>
<query xmlns='http://jabber.org/protocol/muc#admin'>
<item nick='%s' role='%s'>
</item>
</query>
</iq>
` , clientroot . Session . BindJid , jid , JidMustParse ( u . From ) . Resource , the_entry . Text ( ) ) )
2026-03-15 09:57:08 +00:00
} )
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
box . Append ( the_entry )
box . Append ( submit )
2026-03-15 09:55:26 +00:00
2026-03-15 09:57:08 +00:00
win . SetChild ( box )
win . SetVisible ( true )
}
}
2026-03-15 09:55:26 +00:00
} )
rc_box . Append ( bb )
rc_box . Append ( kb )
rc_box . Append ( ab )
rc_box . Append ( rb )
popover . SetChild ( rc_box )
mod_gesture . Connect ( "pressed" , func ( n_press , x , y int ) {
rect := gdk . NewRectangle ( x , y , 1 , 1 )
popover . SetPointingTo ( & rect )
popover . Popup ( )
} )
2026-01-31 15:08:54 +00:00
gesture . Connect ( "pressed" , func ( n_press , x , y int ) {
win := gtk . NewWindow ( )
win . SetDefaultSize ( 400 , 400 )
2026-06-01 10:37:22 +01:00
win . SetResizable ( false )
2026-01-31 15:08:54 +00:00
profile_box := gtk . NewBox ( gtk . OrientationVertical , 0 )
2026-02-01 13:49:30 +00:00
nick := gtk . NewLabel ( JidMustParse ( u . From ) . Resource )
2026-05-10 14:46:19 +01:00
if custom_nick != "" {
nick . SetText ( custom_nick )
}
2026-04-28 12:58:00 +01:00
ver_text := gtk . NewLabel ( loadedLocale [ "gettingVersion" ] )
2026-03-10 16:35:56 +00:00
ver_text . AddCSSClass ( "visitor" )
2026-02-02 13:23:08 +00:00
win . SetTitle ( JidMustParse ( u . From ) . Resource )
2026-01-31 15:08:54 +00:00
nick . AddCSSClass ( "author" )
2026-04-06 11:04:53 +01:00
nick . SetSelectable ( true )
2026-01-31 15:08:54 +00:00
profile_box . Append ( nick )
2026-03-10 16:35:56 +00:00
profile_box . Append ( ver_text )
fr := gtk . NewLabel ( u . From )
fr . AddCSSClass ( "jid" )
2026-04-06 11:04:53 +01:00
fr . SetSelectable ( true )
2026-03-10 16:35:56 +00:00
profile_box . Append ( fr )
profile_box . Append ( ver_text )
2026-01-31 15:08:54 +00:00
iqResp , err := stanza . NewIQ ( stanza . Attrs {
Type : "get" ,
From : clientroot . Session . BindJid ,
To : u . From ,
Id : "vc2" ,
Lang : "en" ,
} )
if err != nil {
panic ( err )
}
iqResp . Payload = & stanza . Version { }
loading_version_text := gtk . NewLabel ( "..." )
var hats Hats
ok := u . Get ( & hats )
if ok {
for _ , hat := range hats . Hats {
2026-02-08 09:44:45 +00:00
l := gtk . NewLabel ( hat . Title )
l . AddCSSClass ( "hat" )
profile_box . Append ( l )
2026-01-31 15:08:54 +00:00
}
}
2026-02-01 13:49:30 +00:00
var mu MucUser
ok = u . Get ( & mu )
if ok {
if mu . MucUserItem . JID != "" {
2026-03-10 16:35:56 +00:00
ji := ( gtk . NewLabel ( mu . MucUserItem . JID ) )
ji . AddCSSClass ( "jid" )
2026-04-06 11:04:53 +01:00
ji . SetSelectable ( true )
2026-03-10 16:35:56 +00:00
profile_box . Append ( ji )
2026-02-01 13:49:30 +00:00
}
2026-04-28 12:58:00 +01:00
profile_box . Append ( gtk . NewLabel ( loadedLocale [ "connectedWithRole" ] + mu . MucUserItem . Role ) )
profile_box . Append ( gtk . NewLabel ( loadedLocale [ "affiliatedAs" ] + mu . MucUserItem . Affiliation ) )
2026-02-01 13:49:30 +00:00
}
2026-05-10 14:46:19 +01:00
if ocu . ID != "" {
ocu_label := gtk . NewLabel ( ocu . ID )
ocu_label . AddCSSClass ( "jid" )
ocu_label . SetSelectable ( true )
profile_box . Append ( ocu_label )
}
2026-02-08 09:44:45 +00:00
go func ( ) {
myIQ , err := stanza . NewIQ ( stanza . Attrs {
Type : "get" ,
From : clientroot . Session . BindJid ,
To : u . From ,
Id : "dicks" ,
Lang : "en" ,
} )
if err != nil {
panic ( err )
}
myIQ . Payload = & stanza . DiscoInfo { }
ctx := context . TODO ( )
mychan , err := client . SendIQ ( ctx , myIQ )
if err == nil {
result := <- mychan
res , ok := result . Payload . ( * stanza . DiscoInfo )
if ok {
idents := res . Identity
for i , ident := range idents {
2026-03-10 16:35:56 +00:00
profile_box . Append ( gtk . NewLabel ( fmt . Sprintf ( "%d: Name: %s, Category: %s, Type: %s" , i + 1 , ident . Name , ident . Category , ident . Type ) ) )
2026-02-08 09:44:45 +00:00
}
2026-03-10 16:35:56 +00:00
/*
2026-03-10 16:48:10 +00:00
s := fmt.Sprintf("%v", res.Features)
h := sha1.New()
h.Write([]byte(s))
sha1_hash := hex.EncodeToString(h.Sum(nil))
2026-03-10 16:35:56 +00:00
*/
sw := gtk . NewScrolledWindow ( )
s := ""
for _ , feature := range res . Features {
s = s + feature . Var + "\n"
}
sw . SetChild ( gtk . NewLabel ( s ) )
profile_box . Append ( sw )
2026-02-08 09:44:45 +00:00
}
}
} ( )
2026-01-31 15:08:54 +00:00
go func ( ) {
ctx := context . TODO ( )
mychan , err := client . SendIQ ( ctx , iqResp )
if err == nil {
result := <- mychan
ver , ok := result . Payload . ( * stanza . Version )
if ok {
loading_version_text . SetVisible ( false )
name := ver . Name
version := ver . Version
os := ver . OS
2026-03-15 09:55:26 +00:00
vr := fmt . Sprintf ( "%s %s %s" , name , version , os )
if name == "" && version == "" && os == "" {
2026-04-28 12:58:00 +01:00
ver_text . SetText ( loadedLocale [ "versionQueryEmpty" ] )
2026-03-15 09:55:26 +00:00
} else {
ver_text . SetText ( vr )
ver_text . RemoveCSSClass ( "visitor" )
}
2026-03-10 16:35:56 +00:00
} else if result . Error != nil && result . Error . Type != "" {
2026-04-28 12:58:00 +01:00
ver_text . SetText ( loadedLocale [ "versionQueryError" ] )
2026-03-10 16:48:10 +00:00
ver_text . SetTooltipText ( result . Error . Reason + ": " + result . Error . Text )
2026-03-10 16:35:56 +00:00
ver_text . RemoveCSSClass ( "visitor" )
ver_text . AddCSSClass ( "error" )
2026-03-10 16:48:10 +00:00
}
2026-01-31 15:08:54 +00:00
}
} ( )
go func ( ) {
2026-02-01 13:49:30 +00:00
mo , _ := mucmembers . Load ( JidMustParse ( u . From ) . Bare ( ) )
2026-01-31 15:08:54 +00:00
mm := mo . ( mucUnit )
mmm := mm . Members
2026-02-04 17:57:24 +00:00
mmmm , ok := mmm . Load ( id )
2026-01-31 15:08:54 +00:00
if ok {
pres := mmmm . ( stanza . Presence )
var vu VCardUpdate
pres . Get ( & vu )
if vu . Photo != "" {
im := getAvatar ( u . From , vu . Photo )
im . SetPixelSize ( 80 )
im . AddCSSClass ( "author_img" )
2026-03-10 16:35:56 +00:00
profile_box . Prepend ( im )
2026-01-31 15:08:54 +00:00
} else {
2026-05-08 06:56:51 +01:00
im := createIdenticon ( u . From , false )
2026-01-31 15:08:54 +00:00
im . SetPixelSize ( 80 )
im . AddCSSClass ( "author_img" )
2026-03-10 16:35:56 +00:00
profile_box . Prepend ( im )
2026-01-31 15:08:54 +00:00
}
} else {
2026-05-08 06:56:51 +01:00
im := createIdenticon ( u . From , false )
2026-01-31 15:08:54 +00:00
im . SetPixelSize ( 80 )
im . AddCSSClass ( "author_img" )
2026-03-10 16:35:56 +00:00
profile_box . Prepend ( im )
2026-01-31 15:08:54 +00:00
}
} ( )
win . SetChild ( profile_box )
2026-01-31 15:38:02 +00:00
win . SetTransientFor ( win )
2026-01-31 15:08:54 +00:00
win . Present ( )
} )
2026-03-15 09:55:26 +00:00
2026-01-31 15:08:54 +00:00
userbox . AddController ( gesture )
2026-03-15 09:55:26 +00:00
userbox . AddController ( mod_gesture )
2026-01-30 21:31:33 +00:00
return true
2026-04-06 11:04:53 +01:00
} ) )
2026-01-30 21:31:33 +00:00
2026-02-15 13:08:52 +00:00
headerBox := gtk . NewBox ( gtk . OrientationHorizontal , 0 )
2026-02-16 09:52:25 +00:00
if i >= 500 {
2026-05-23 06:45:18 +01:00
headerBox . Append ( gtk . NewImageFromPaintable ( clientAssetsLoad ( "world" ) ) )
2026-02-16 09:52:25 +00:00
} else if i >= 50 {
2026-05-23 06:45:18 +01:00
headerBox . Append ( gtk . NewImageFromPaintable ( clientAssetsLoad ( "large_group" ) ) )
2026-02-16 09:52:25 +00:00
} else {
2026-05-23 06:45:18 +01:00
headerBox . Append ( gtk . NewImageFromPaintable ( clientAssetsLoad ( "group" ) ) )
2026-02-16 09:52:25 +00:00
}
2026-04-28 12:58:00 +01:00
headerBox . Append ( gtk . NewLabel ( fmt . Sprintf ( "%d %s" , i , loadedLocale [ "participants" ] ) ) )
2026-02-15 13:08:52 +00:00
gen . Prepend ( headerBox )
2026-05-18 06:27:55 +01:00
muc_presence := typed_tab . muc_presence
if muc_presence != nil {
vc := & VCardUpdate { }
ok = muc_presence . Get ( vc )
if ok {
muci := getAvatar ( jid , vc . Photo )
muci . SetPixelSize ( 80 )
gen . Prepend ( muci )
}
}
2026-04-26 10:40:13 +01:00
muc_name := gtk . NewLabel ( typed_tab . name )
muc_name . AddCSSClass ( "author" )
2026-05-09 07:33:47 +01:00
muc_name . SetWrap ( true )
2026-04-26 10:40:13 +01:00
gen . Prepend ( muc_name )
2026-01-30 21:31:33 +00:00
memberList . SetChild ( gen )
} else {
memberList . SetChild ( gtk . NewLabel ( jid ) )
}
2026-01-30 10:40:38 +00:00
2026-01-29 21:35:36 +00:00
}
2026-05-09 07:33:47 +01:00
func showErrorDialog ( err error , w * gtk . Window ) {
2026-04-26 10:40:13 +01:00
err_win := gtk . NewWindow ( )
2026-04-28 12:58:00 +01:00
err_win . SetTitle ( loadedLocale [ "error" ] )
2026-04-26 10:40:13 +01:00
err_win . SetDefaultSize ( 400 , 200 )
err_win . SetResizable ( false )
box := gtk . NewBox ( gtk . OrientationVertical , 0 )
err_label := gtk . NewLabel ( err . Error ( ) )
err_label . SetSelectable ( true )
box . Append ( err_label )
2026-04-28 12:58:00 +01:00
close_btn := gtk . NewButtonWithLabel ( loadedLocale [ "close" ] )
2026-04-26 10:40:13 +01:00
close_btn . ConnectClicked ( func ( ) {
err_win . SetVisible ( false )
} )
box . Append ( close_btn )
err_win . SetChild ( box )
2026-05-09 07:33:47 +01:00
err_win . SetTransientFor ( w )
2026-04-26 10:40:13 +01:00
err_win . Present ( )
2026-01-29 21:35:36 +00:00
}
2026-04-06 11:04:53 +01:00
2026-05-08 06:56:51 +01:00
func createIdenticon ( word string , always_create bool ) * gtk . Image { // This function generates an identicon
if ! loadedConfig . Identicons && ! always_create {
2026-05-23 06:45:18 +01:00
return gtk . NewImageFromPaintable ( clientAssetsLoad ( "DefaultAvatar" ) )
2026-04-06 11:04:53 +01:00
}
2026-05-08 06:56:51 +01:00
identicon . SetBackgroundColorFunction ( func ( [ ] byte , color . Color ) color . Color {
return color . Transparent
} )
gen , _ := identicon . New ( "" , 5 , 3 )
2026-04-06 11:04:53 +01:00
ii , _ := gen . Draw ( word )
2026-05-03 11:10:15 +01:00
im := ii . Image ( 250 )
2026-04-06 11:04:53 +01:00
buf := new ( bytes . Buffer )
err := png . Encode ( buf , im )
if err != nil {
panic ( err )
}
loader := gdkpixbuf . NewPixbufLoader ( )
loader . Write ( buf . Bytes ( ) )
loader . Close ( )
2026-05-23 06:45:18 +01:00
p := loader . Pixbuf ( )
gt := gdk . NewTextureForPixbuf ( p )
i := gtk . NewImageFromPaintable ( gt )
2026-04-06 11:04:53 +01:00
i . AddCSSClass ( loadedConfig . CVD . String ( ) + "_CVD" )
return i
}
2026-04-30 14:55:17 +01:00
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 )
}