Compare commits
11 Commits
359e8ed63e
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 82aa2abfbd | |||
| 58c7165ce5 | |||
| 5bcf0eda0b | |||
| 0bfb140dc7 | |||
| 7b63799f0b | |||
| a97c42323c | |||
| fc0ed5ac2c | |||
| 69994d9856 | |||
| 843687ff2b | |||
| 7416aa37e0 | |||
| ce83000e0c |
@@ -2,333 +2,198 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/base64"
|
|
||||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed debug.png
|
//go:embed debug.png
|
||||||
var defaultAvatarBytes []byte
|
var defaultAvatarBytes []byte
|
||||||
var defaultAvatarB64 string = base64.StdEncoding.EncodeToString(defaultAvatarBytes)
|
|
||||||
|
|
||||||
//go:embed failed_load.png
|
//go:embed failed_load.png
|
||||||
var failedBytes []byte
|
var failedBytes []byte
|
||||||
var failedB64 string = base64.StdEncoding.EncodeToString(failedBytes)
|
|
||||||
|
|
||||||
//go:embed assets/owner.png
|
//go:embed assets/owner.png
|
||||||
var ownerMedalBytes []byte
|
var ownerMedalBytes []byte
|
||||||
var ownerMedalB64 string = base64.StdEncoding.EncodeToString(ownerMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/admin.png
|
//go:embed assets/admin.png
|
||||||
var adminMedalBytes []byte
|
var adminMedalBytes []byte
|
||||||
var adminMedalB64 string = base64.StdEncoding.EncodeToString(adminMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/member.png
|
//go:embed assets/member.png
|
||||||
var memberMedalBytes []byte
|
var memberMedalBytes []byte
|
||||||
var memberMedalB64 string = base64.StdEncoding.EncodeToString(memberMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/noaffiliation.png
|
//go:embed assets/noaffiliation.png
|
||||||
var noneMedalBytes []byte
|
var noneMedalBytes []byte
|
||||||
var noneMedalB64 string = base64.StdEncoding.EncodeToString(noneMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/outcast.png
|
//go:embed assets/outcast.png
|
||||||
var outcastMedalBytes []byte
|
var outcastMedalBytes []byte
|
||||||
var outcastMedalB64 string = base64.StdEncoding.EncodeToString(outcastMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/cancel.png
|
//go:embed assets/cancel.png
|
||||||
var cancelBytes []byte
|
var cancelBytes []byte
|
||||||
var cancelB64 string = base64.StdEncoding.EncodeToString(cancelBytes)
|
|
||||||
|
|
||||||
//go:embed assets/status_away.png
|
//go:embed assets/status_away.png
|
||||||
var sABytes []byte
|
var sABytes []byte
|
||||||
var sAB64 string = base64.StdEncoding.EncodeToString(sABytes)
|
|
||||||
|
|
||||||
//go:embed assets/status_busy.png
|
//go:embed assets/status_busy.png
|
||||||
var sBBytes []byte
|
var sBBytes []byte
|
||||||
var sBB64 string = base64.StdEncoding.EncodeToString(sBBytes)
|
|
||||||
|
|
||||||
//go:embed assets/status_chatty.png
|
//go:embed assets/status_chatty.png
|
||||||
var sCBytes []byte
|
var sCBytes []byte
|
||||||
var sCB64 string = base64.StdEncoding.EncodeToString(sCBytes)
|
|
||||||
|
|
||||||
//go:embed assets/status_online.png
|
//go:embed assets/status_online.png
|
||||||
var sOBytes []byte
|
var sOBytes []byte
|
||||||
var sOB64 string = base64.StdEncoding.EncodeToString(sOBytes)
|
|
||||||
|
|
||||||
//go:embed assets/status_xa.png
|
//go:embed assets/status_xa.png
|
||||||
var xaBytes []byte
|
var xaBytes []byte
|
||||||
var xaB64 string = base64.StdEncoding.EncodeToString(xaBytes)
|
|
||||||
|
|
||||||
//go:embed assets/tag.png
|
//go:embed assets/tag.png
|
||||||
var tagBytes []byte
|
var tagBytes []byte
|
||||||
var tagB64 string = base64.StdEncoding.EncodeToString(tagBytes)
|
|
||||||
|
|
||||||
//go:embed assets/lambda-disabled.png
|
//go:embed assets/lambda-disabled.png
|
||||||
var logoDisabledBytes []byte
|
var logoDisabledBytes []byte
|
||||||
var logoDisabledB64 string = base64.StdEncoding.EncodeToString(logoDisabledBytes)
|
|
||||||
|
|
||||||
//go:embed assets/group.png
|
//go:embed assets/group.png
|
||||||
var groupBytes []byte
|
var groupBytes []byte
|
||||||
var groupB64 string = base64.StdEncoding.EncodeToString(groupBytes)
|
|
||||||
|
|
||||||
//go:embed assets/door_in.png
|
//go:embed assets/door_in.png
|
||||||
var doorInBytes []byte
|
var doorInBytes []byte
|
||||||
var doorInB64 string = base64.StdEncoding.EncodeToString(doorInBytes)
|
|
||||||
|
|
||||||
//go:embed assets/door_out.png
|
//go:embed assets/door_out.png
|
||||||
var doorOutBytes []byte
|
var doorOutBytes []byte
|
||||||
var doorOutB64 string = base64.StdEncoding.EncodeToString(doorOutBytes)
|
|
||||||
|
|
||||||
//go:embed assets/large_group.png
|
//go:embed assets/large_group.png
|
||||||
var largeGroupBytes []byte
|
var largeGroupBytes []byte
|
||||||
var largeGroupB64 string = base64.StdEncoding.EncodeToString(largeGroupBytes)
|
|
||||||
|
|
||||||
//go:embed assets/world.png
|
//go:embed assets/world.png
|
||||||
var worldBytes []byte
|
var worldBytes []byte
|
||||||
var worldB64 string = base64.StdEncoding.EncodeToString(worldBytes)
|
|
||||||
|
|
||||||
//go:embed assets/disconnect.png
|
//go:embed assets/disconnect.png
|
||||||
var disconnectBytes []byte
|
var disconnectBytes []byte
|
||||||
var disconnectB64 string = base64.StdEncoding.EncodeToString(disconnectBytes)
|
|
||||||
|
|
||||||
//go:embed assets/chart_bar.png
|
//go:embed assets/chart_bar.png
|
||||||
var barBytes []byte
|
var barBytes []byte
|
||||||
var barB64 string = base64.StdEncoding.EncodeToString(barBytes)
|
|
||||||
|
//go:embed assets/chart_bar_laggy.png
|
||||||
|
var barLaggyBytes []byte
|
||||||
|
|
||||||
//go:embed assets/ok.png
|
//go:embed assets/ok.png
|
||||||
var okBytes []byte
|
var okBytes []byte
|
||||||
var okB64 string = base64.StdEncoding.EncodeToString(okBytes)
|
|
||||||
|
|
||||||
//go:embed assets/hourglass.png
|
//go:embed assets/hourglass.png
|
||||||
var hourglassBytes []byte
|
var hourglassBytes []byte
|
||||||
var hourglassB64 string = base64.StdEncoding.EncodeToString(hourglassBytes)
|
|
||||||
|
|
||||||
//go:embed assets/connect_tls.png
|
//go:embed assets/connect_tls.png
|
||||||
var connectBytes []byte
|
var connectBytes []byte
|
||||||
var connectB64 string = base64.StdEncoding.EncodeToString(connectBytes)
|
|
||||||
|
|
||||||
//go:embed assets/comment.png
|
//go:embed assets/comment.png
|
||||||
var commentBytes []byte
|
var commentBytes []byte
|
||||||
var commentB64 string = base64.StdEncoding.EncodeToString(commentBytes)
|
|
||||||
|
|
||||||
//go:embed assets/information.png
|
//go:embed assets/information.png
|
||||||
var informationBytes []byte
|
var informationBytes []byte
|
||||||
var informationB64 string = base64.StdEncoding.EncodeToString(informationBytes)
|
|
||||||
|
//go:embed assets/car.png
|
||||||
|
var carBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/car_high.png
|
||||||
|
var carHighBytes []byte
|
||||||
|
|
||||||
|
// muc icons
|
||||||
|
|
||||||
|
//go:embed assets/muc_open.png
|
||||||
|
var mucOpenBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_membersonly.png
|
||||||
|
var mucMembersOnlyBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_passwordprotected.png
|
||||||
|
var mucPasswordProtectedBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_unsecured.png
|
||||||
|
var mucUnsecuredBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_hidden.png
|
||||||
|
var mucHiddenBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_public.png
|
||||||
|
var mucPublicBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_unmoderated.png
|
||||||
|
var mucUnmoderatedBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_moderated.png
|
||||||
|
var mucModeratedBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_nonanonymous.png
|
||||||
|
var mucNonAnonymousBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_semianonymous.png
|
||||||
|
var mucSemiAnonymousBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_persistent.png
|
||||||
|
var mucPersistentBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/muc_temporary.png
|
||||||
|
var mucTemporaryBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/moderate.png
|
||||||
|
var moderateBytes []byte
|
||||||
|
|
||||||
|
//go:embed assets/jabber.png
|
||||||
|
var jabberBytes []byte
|
||||||
|
|
||||||
|
func loadAsset(key string, data []byte) {
|
||||||
|
loader := gdkpixbuf.NewPixbufLoader()
|
||||||
|
loader.Write(data)
|
||||||
|
loader.Close()
|
||||||
|
clientAssets[key] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
for key, data := range map[string][]byte{
|
||||||
loader := gdkpixbuf.NewPixbufLoader()
|
"DefaultAvatar": defaultAvatarBytes,
|
||||||
|
"FailedAvatar": failedBytes,
|
||||||
defaultAvatarData, _ := base64.StdEncoding.DecodeString(defaultAvatarB64)
|
"owner": ownerMedalBytes,
|
||||||
loader.Write(defaultAvatarData)
|
"admin": adminMedalBytes,
|
||||||
loader.Close()
|
"member": memberMedalBytes,
|
||||||
clientAssets["DefaultAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
"none": noneMedalBytes,
|
||||||
|
"outcast": outcastMedalBytes,
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
"cancel": cancelBytes,
|
||||||
|
"tag": tagBytes,
|
||||||
failedData, _ := base64.StdEncoding.DecodeString(failedB64)
|
"disabled_logo": logoDisabledBytes,
|
||||||
loader.Write(failedData)
|
"group": groupBytes,
|
||||||
loader.Close()
|
"door_in": doorInBytes,
|
||||||
|
"door_out": doorOutBytes,
|
||||||
clientAssets["FailedAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
"large_group": largeGroupBytes,
|
||||||
|
"world": worldBytes,
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
"disconnect": disconnectBytes,
|
||||||
|
"chart_bar": barBytes,
|
||||||
ownerMedalData, _ := base64.StdEncoding.DecodeString(ownerMedalB64)
|
"chart_bar_laggy": barLaggyBytes,
|
||||||
loader.Write(ownerMedalData)
|
"ok": okBytes,
|
||||||
loader.Close()
|
"hourglass": hourglassBytes,
|
||||||
|
"connect": connectBytes,
|
||||||
clientAssets["owner"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
"comment": commentBytes,
|
||||||
|
"information": informationBytes,
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
"status_away": sABytes,
|
||||||
|
"status_dnd": sBBytes,
|
||||||
cancelData, _ := base64.StdEncoding.DecodeString(cancelB64)
|
"status_chat": sCBytes,
|
||||||
loader.Write(cancelData)
|
"status_xa": xaBytes,
|
||||||
loader.Close()
|
"status_": sOBytes,
|
||||||
|
"car": carBytes,
|
||||||
clientAssets["cancel"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
"car_high": carHighBytes,
|
||||||
|
"muc_open": mucOpenBytes,
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
"muc_membersonly": mucMembersOnlyBytes,
|
||||||
|
"muc_passwordprotected": mucPasswordProtectedBytes,
|
||||||
tagData, _ := base64.StdEncoding.DecodeString(tagB64)
|
"muc_unsecured": mucUnsecuredBytes,
|
||||||
loader.Write(tagData)
|
"muc_hidden": mucHiddenBytes,
|
||||||
loader.Close()
|
"muc_public": mucPublicBytes,
|
||||||
|
"muc_unmoderated": mucUnmoderatedBytes,
|
||||||
clientAssets["tag"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
"muc_moderated": mucModeratedBytes,
|
||||||
|
"muc_nonanonymous": mucNonAnonymousBytes,
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
"muc_semianonymous": mucSemiAnonymousBytes,
|
||||||
|
"muc_persistent": mucPersistentBytes,
|
||||||
adminMedalData, _ := base64.StdEncoding.DecodeString(adminMedalB64)
|
"muc_temporary": mucTemporaryBytes,
|
||||||
loader.Write(adminMedalData)
|
"moderate": moderateBytes,
|
||||||
loader.Close()
|
"jabber": jabberBytes,
|
||||||
|
} {
|
||||||
clientAssets["admin"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
loadAsset(key, data)
|
||||||
|
}
|
||||||
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())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
disabledLogoData, _ := base64.StdEncoding.DecodeString(logoDisabledB64)
|
|
||||||
loader.Write(disabledLogoData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["disabled_logo"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
groupData, _ := base64.StdEncoding.DecodeString(groupB64)
|
|
||||||
loader.Write(groupData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["group"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
doorInData, _ := base64.StdEncoding.DecodeString(doorInB64)
|
|
||||||
loader.Write(doorInData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["door_in"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
doorOutData, _ := base64.StdEncoding.DecodeString(doorOutB64)
|
|
||||||
loader.Write(doorOutData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["door_out"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
largeGroupData, _ := base64.StdEncoding.DecodeString(largeGroupB64)
|
|
||||||
loader.Write(largeGroupData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["large_group"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
worldData, _ := base64.StdEncoding.DecodeString(worldB64)
|
|
||||||
loader.Write(worldData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["world"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
disconnectData, _ := base64.StdEncoding.DecodeString(disconnectB64)
|
|
||||||
loader.Write(disconnectData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["disconnect"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
barData, _ := base64.StdEncoding.DecodeString(barB64)
|
|
||||||
loader.Write(barData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["chart_bar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
okData, _ := base64.StdEncoding.DecodeString(okB64)
|
|
||||||
loader.Write(okData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["ok"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
hourglassData, _ := base64.StdEncoding.DecodeString(hourglassB64)
|
|
||||||
loader.Write(hourglassData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["hourglass"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
connectData, _ := base64.StdEncoding.DecodeString(connectB64)
|
|
||||||
loader.Write(connectData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["connect"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
commentData, _ := base64.StdEncoding.DecodeString(commentB64)
|
|
||||||
loader.Write(commentData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["comment"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
informationData, _ := base64.StdEncoding.DecodeString(informationB64)
|
|
||||||
loader.Write(informationData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["information"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
sAData, _ := base64.StdEncoding.DecodeString(sAB64)
|
|
||||||
loader.Write(sAData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["status_away"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
sBData, _ := base64.StdEncoding.DecodeString(sBB64)
|
|
||||||
loader.Write(sBData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["status_dnd"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
sCData, _ := base64.StdEncoding.DecodeString(sCB64)
|
|
||||||
loader.Write(sCData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["status_chat"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
xaData, _ := base64.StdEncoding.DecodeString(xaB64)
|
|
||||||
loader.Write(xaData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["status_xa"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
sOData, _ := base64.StdEncoding.DecodeString(sOB64)
|
|
||||||
loader.Write(sOData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["status_"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 671 B |
|
After Width: | Height: | Size: 656 B |
|
After Width: | Height: | Size: 456 B |
|
After Width: | Height: | Size: 641 B |
|
After Width: | Height: | Size: 434 B |
|
After Width: | Height: | Size: 793 B |
|
After Width: | Height: | Size: 593 B |
|
After Width: | Height: | Size: 935 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 970 B |
|
After Width: | Height: | Size: 508 B |
|
After Width: | Height: | Size: 612 B |
|
After Width: | Height: | Size: 806 B |
|
After Width: | Height: | Size: 622 B |
|
After Width: | Height: | Size: 924 B |
|
After Width: | Height: | Size: 882 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 903 B |
|
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 910 B |
@@ -13,10 +13,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// global or app-level map/cache
|
// global or app-level map/cache
|
||||||
var textureCache = make(map[string]gdk.Paintabler)
|
// var textureCache = make(map[string]gdk.Paintabler)
|
||||||
|
var textureCache sync.Map
|
||||||
|
|
||||||
// Invalid images, if an image/avatar cannot be loaded on the system (e.g: incompatible format) it's put here
|
// Invalid images, if an image/avatar cannot be loaded on the system (e.g: incompatible format) it's put here
|
||||||
var invalidImages = make(map[string]bool)
|
var invalidImages = make(map[string]bool)
|
||||||
@@ -31,49 +33,57 @@ func ensureCache() (string, error) {
|
|||||||
return cachePath, nil
|
return cachePath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTexture(path string) gdk.Paintabler {
|
func getTexture(path string) (gdk.Paintabler, error) {
|
||||||
if tex, exists := textureCache[path]; exists {
|
tex, exists := textureCache.Load(path)
|
||||||
return tex
|
if exists {
|
||||||
|
return tex.(gdk.Paintabler), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tex, err := gdk.NewTextureFromFilename(path) // load once
|
tex, err := gdk.NewTextureFromFilename(path) // load once
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
textureCache[path] = tex
|
textureCache.Store(path, tex)
|
||||||
return tex
|
return tex.(gdk.Paintabler), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPictureFromPath(path string) *gtk.Picture {
|
func newPictureFromPath(path string) (*gtk.Picture, error) {
|
||||||
tex := getTexture(path)
|
tex, err := getTexture(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
img := gtk.NewPictureForPaintable(tex)
|
img := gtk.NewPictureForPaintable(tex)
|
||||||
return img
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newImageFromPath(path string) *gtk.Image {
|
func newImageFromPath(path string) (*gtk.Image, error) {
|
||||||
tex := getTexture(path)
|
tex, err := getTexture(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
img := gtk.NewImageFromPaintable(tex)
|
img := gtk.NewImageFromPaintable(tex)
|
||||||
return img
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPictureFromWeb(url string) *gtk.Picture {
|
func newPictureFromWeb(url string) (*gtk.Picture, error) {
|
||||||
pa, _ := ensureCache()
|
pa, _ := ensureCache()
|
||||||
// step 1: get a sha256 sum of the URL
|
// step 1: get a sha256 sum of the URL
|
||||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||||
|
|
||||||
p, ok := textureCache[sum]
|
p, ok := textureCache.Load(sum)
|
||||||
if ok {
|
if ok {
|
||||||
return gtk.NewPictureForPaintable(p)
|
return gtk.NewPictureForPaintable(p.(gdk.Paintabler)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 2: download it
|
// step 2: download it
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(resp.Body)
|
b, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fullpath := filepath.Join(pa, sum)
|
fullpath := filepath.Join(pa, sum)
|
||||||
@@ -81,31 +91,31 @@ func newPictureFromWeb(url string) *gtk.Picture {
|
|||||||
// step 3: save it
|
// step 3: save it
|
||||||
err = os.WriteFile(fullpath, b, 0644)
|
err = os.WriteFile(fullpath, b, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newPictureFromPath(fullpath)
|
return newPictureFromPath(fullpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newImageFromWeb(url string) *gtk.Image {
|
func newImageFromWeb(url string) (*gtk.Image, error) {
|
||||||
pa, _ := ensureCache()
|
pa, _ := ensureCache()
|
||||||
// step 1: get a sha256 sum of the URL
|
// step 1: get a sha256 sum of the URL
|
||||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||||
|
|
||||||
p, ok := textureCache[sum]
|
p, ok := textureCache.Load(sum)
|
||||||
if ok {
|
if ok {
|
||||||
return gtk.NewImageFromPaintable(p)
|
return gtk.NewImageFromPaintable(p.(gdk.Paintabler)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// step 2: download it
|
// step 2: download it
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(resp.Body)
|
b, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fullpath := filepath.Join(pa, sum)
|
fullpath := filepath.Join(pa, sum)
|
||||||
@@ -113,7 +123,7 @@ func newImageFromWeb(url string) *gtk.Image {
|
|||||||
// step 3: save it
|
// step 3: save it
|
||||||
err = os.WriteFile(fullpath, b, 0644)
|
err = os.WriteFile(fullpath, b, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newImageFromPath(fullpath)
|
return newImageFromPath(fullpath)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ go 1.25.5
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.6.0
|
github.com/BurntSushi/toml v1.6.0
|
||||||
|
github.com/boxes-ltd/imaging v1.7.5
|
||||||
|
github.com/crazy3lf/colorconv v1.2.0
|
||||||
github.com/diamondburned/gotk4/pkg v0.3.1
|
github.com/diamondburned/gotk4/pkg v0.3.1
|
||||||
github.com/gen2brain/beeep v0.11.2
|
github.com/gen2brain/beeep v0.11.2
|
||||||
github.com/go-analyze/charts v0.5.24
|
github.com/go-analyze/charts v0.5.24
|
||||||
@@ -12,6 +14,10 @@ require (
|
|||||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030
|
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030
|
||||||
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
||||||
github.com/kr/pretty v0.2.0
|
github.com/kr/pretty v0.2.0
|
||||||
|
github.com/mskrha/svg2png v0.0.0-20240706085601-64fa78f4eb07
|
||||||
|
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef
|
||||||
golang.org/x/net v0.29.0
|
golang.org/x/net v0.29.0
|
||||||
gosrc.io/xmpp v0.5.1
|
gosrc.io/xmpp v0.5.1
|
||||||
mellium.im/xmpp v0.22.0
|
mellium.im/xmpp v0.22.0
|
||||||
@@ -33,10 +39,10 @@ require (
|
|||||||
github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
|
github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
|
||||||
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
|
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||||
golang.org/x/image v0.24.0 // indirect
|
golang.org/x/image v0.36.0 // indirect
|
||||||
golang.org/x/sync v0.11.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 // indirect
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 // indirect
|
||||||
mellium.im/reader v0.1.0 // indirect
|
mellium.im/reader v0.1.0 // indirect
|
||||||
mellium.im/xmlstream v0.15.4 // indirect
|
mellium.im/xmlstream v0.15.4 // indirect
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2
|
|||||||
github.com/KarpelesLab/weak v0.1.1 h1:fNnlPo3aypS9tBzoEQluY13XyUfd/eWaSE/vMvo9s4g=
|
github.com/KarpelesLab/weak v0.1.1 h1:fNnlPo3aypS9tBzoEQluY13XyUfd/eWaSE/vMvo9s4g=
|
||||||
github.com/KarpelesLab/weak v0.1.1/go.mod h1:pzXsWs5f2bf+fpgHayTlBE1qJpO3MpJKo5sRaLu1XNw=
|
github.com/KarpelesLab/weak v0.1.1/go.mod h1:pzXsWs5f2bf+fpgHayTlBE1qJpO3MpJKo5sRaLu1XNw=
|
||||||
github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=
|
github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=
|
||||||
|
github.com/boxes-ltd/imaging v1.7.5 h1:k4kYxJEhysoGhEEN1IEeKoSbnG8/8snjj7M48Ok0fnk=
|
||||||
|
github.com/boxes-ltd/imaging v1.7.5/go.mod h1:+8H+oRvis3InOFtTpcoCCB1RDXqo6p9tQBtjZfWnrC8=
|
||||||
github.com/chromedp/cdproto v0.0.0-20190614062957-d6d2f92b486d/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
|
github.com/chromedp/cdproto v0.0.0-20190614062957-d6d2f92b486d/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
|
||||||
github.com/chromedp/cdproto v0.0.0-20190621002710-8cbd498dd7a0/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
|
github.com/chromedp/cdproto v0.0.0-20190621002710-8cbd498dd7a0/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
|
||||||
github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
|
github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
|
||||||
github.com/chromedp/cdproto v0.0.0-20190926234355-1b4886c6fad6/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
|
github.com/chromedp/cdproto v0.0.0-20190926234355-1b4886c6fad6/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
|
||||||
github.com/chromedp/chromedp v0.3.1-0.20190619195644-fd957a4d2901/go.mod h1:mJdvfrVn594N9tfiPecUidF6W5jPRKHymqHfzbobPsM=
|
github.com/chromedp/chromedp v0.3.1-0.20190619195644-fd957a4d2901/go.mod h1:mJdvfrVn594N9tfiPecUidF6W5jPRKHymqHfzbobPsM=
|
||||||
github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4=
|
github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4=
|
||||||
|
github.com/crazy3lf/colorconv v1.2.0 h1:UM7kSZWnwFMGiC+PpYrjxQSOd6sEyWb+dRKKTd3KslA=
|
||||||
|
github.com/crazy3lf/colorconv v1.2.0/go.mod h1:2jTJ7QCWCj2sSLOhF4Gzi0J5/hoX8/VY8VzNvXAlD1I=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -80,6 +84,8 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
|||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
|
github.com/mskrha/svg2png v0.0.0-20240706085601-64fa78f4eb07 h1:7fan6wzUXasMPMHho2ePSkB+QTEb0Rh/f6B+IkkP1Sc=
|
||||||
|
github.com/mskrha/svg2png v0.0.0-20240706085601-64fa78f4eb07/go.mod h1:KFdfdIgpr48ODxdkxKvpcYwuyLpQ6rfkAsFB2UQ6jD4=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -89,6 +95,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d h1:l3+2LWCbVxn5itfvXAfH9n4YL9jh8l1g5zcncbIc1cs=
|
||||||
|
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d/go.mod h1:TbpErkob6SY7cyozRVSGoB3OlO2qOAgVN8O3KAJ4fMI=
|
||||||
github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M=
|
github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M=
|
||||||
github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY=
|
github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY=
|
||||||
github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3K6MFJw4GfZvQ=
|
github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3K6MFJw4GfZvQ=
|
||||||
@@ -97,6 +105,10 @@ github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjM
|
|||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
@@ -123,11 +135,11 @@ golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc=
|
||||||
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4=
|
||||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
@@ -137,8 +149,8 @@ golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
|||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -152,14 +164,14 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/boxes-ltd/imaging"
|
||||||
|
"github.com/crazy3lf/colorconv"
|
||||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/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/diamondburned/gotk4/pkg/pango"
|
"github.com/diamondburned/gotk4/pkg/pango"
|
||||||
|
"github.com/rrivera/identicon"
|
||||||
"gosrc.io/xmpp/stanza"
|
"gosrc.io/xmpp/stanza"
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
xmpp_color "mellium.im/xmpp/color"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
||||||
@@ -18,8 +27,10 @@ func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTab(jid string, isMuc bool) bool {
|
func createTab(jid string, isMuc bool, name string) bool {
|
||||||
fmt.Println("Creating tab", jid, "isMuc:", isMuc)
|
if name == "" {
|
||||||
|
name = jid
|
||||||
|
}
|
||||||
_, ok := tabs.Load(jid)
|
_, ok := tabs.Load(jid)
|
||||||
_, uok := userdevices.Load(jid)
|
_, uok := userdevices.Load(jid)
|
||||||
_, mok := mucmembers.Load(jid)
|
_, mok := mucmembers.Load(jid)
|
||||||
@@ -29,8 +40,9 @@ func createTab(jid string, isMuc bool) bool {
|
|||||||
newTab.msgs = gtk.NewListBox()
|
newTab.msgs = gtk.NewListBox()
|
||||||
newTab.msgs.SetVExpand(true)
|
newTab.msgs.SetVExpand(true)
|
||||||
newTab.msgs.SetShowSeparators(true)
|
newTab.msgs.SetShowSeparators(true)
|
||||||
|
newTab.name = name
|
||||||
|
|
||||||
newTab.msgs.Append(gtk.NewButtonWithLabel("Get past messages..."))
|
newTab.msgs.Append(gtk.NewButtonWithLabel(loadedLocale["getPastMessages"]))
|
||||||
tabs.Store(jid, newTab)
|
tabs.Store(jid, newTab)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -46,10 +58,9 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typed_tab := tab.(*chatTab)
|
typed_tab := tab.(*chatTab)
|
||||||
|
|
||||||
scroller.SetChild(typed_tab.msgs)
|
scroller.SetChild(typed_tab.msgs)
|
||||||
|
typingStatus.SetText("")
|
||||||
if typed_tab.isMuc {
|
if typed_tab.isMuc {
|
||||||
|
|
||||||
m, ok := mucmembers.Load(jid)
|
m, ok := mucmembers.Load(jid)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@@ -59,18 +70,29 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
mm := ma.Members
|
mm := ma.Members
|
||||||
gen := gtk.NewBox(gtk.OrientationVertical, 0)
|
gen := gtk.NewBox(gtk.OrientationVertical, 10)
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
mm.Range(func(k, v any) bool {
|
rangeOrdered(&mm, (func(k, v any) bool {
|
||||||
i++
|
i++
|
||||||
userbox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
u, ok := v.(stanza.Presence)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
u.Get(&ocu)
|
u.Get(&ocu)
|
||||||
|
|
||||||
|
if mu.MucUserItem.Role == "moderator" {
|
||||||
|
gen.Prepend(userbox)
|
||||||
|
} else {
|
||||||
|
gen.Append(userbox)
|
||||||
|
}
|
||||||
|
|
||||||
//id := ocu.ID
|
//id := ocu.ID
|
||||||
//if id == "" {
|
//if id == "" {
|
||||||
id := JidMustParse(u.From).Resource
|
id := JidMustParse(u.From).Resource
|
||||||
@@ -83,19 +105,42 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
nick_label.SetOpacity(0.5)
|
nick_label.SetOpacity(0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\nClick for more information", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation))
|
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)
|
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 {
|
||||||
tag := gtk.NewImageFromPaintable(clientAssets["tag"])
|
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()))
|
||||||
tag.SetTooltipText(hat.Title)
|
tag.SetTooltipText(hat.Title)
|
||||||
userbox.Prepend(tag)
|
userbox.Prepend(tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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))
|
||||||
|
|
||||||
@@ -110,6 +155,22 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
medal.SetHExpand(true)
|
medal.SetHExpand(true)
|
||||||
userbox.Append(medal)
|
userbox.Append(medal)
|
||||||
|
|
||||||
|
default_av := createIdenticon(u.From)
|
||||||
|
userbox.Prepend(default_av)
|
||||||
|
var vcu VCardUpdate
|
||||||
|
ok = u.Get(&vcu)
|
||||||
|
if ok {
|
||||||
|
photo := vcu.Photo
|
||||||
|
go func() {
|
||||||
|
new_im := getAvatar(u.From, photo)
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
userbox.Remove(default_av)
|
||||||
|
userbox.Prepend(new_im)
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
gesture := gtk.NewGestureClick()
|
gesture := gtk.NewGestureClick()
|
||||||
gesture.SetButton(1)
|
gesture.SetButton(1)
|
||||||
|
|
||||||
@@ -121,10 +182,10 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
popover.SetParent(userbox)
|
popover.SetParent(userbox)
|
||||||
|
|
||||||
rc_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
rc_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
bb := gtk.NewButtonWithLabel("Ban")
|
bb := gtk.NewButtonWithLabel(loadedLocale["ban"])
|
||||||
kb := gtk.NewButtonWithLabel("Kick")
|
kb := gtk.NewButtonWithLabel(loadedLocale["kick"])
|
||||||
ab := gtk.NewButtonWithLabel("Set affil")
|
ab := gtk.NewButtonWithLabel(loadedLocale["setAffil"])
|
||||||
rb := gtk.NewButtonWithLabel("Set role")
|
rb := gtk.NewButtonWithLabel(loadedLocale["setRole"])
|
||||||
|
|
||||||
kb.ConnectClicked(func() {
|
kb.ConnectClicked(func() {
|
||||||
client.SendRaw(fmt.Sprintf(`
|
client.SendRaw(fmt.Sprintf(`
|
||||||
@@ -169,12 +230,12 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
win.SetResizable(false)
|
win.SetResizable(false)
|
||||||
|
|
||||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
box.Append(gtk.NewLabel("Set " + JidMustParse(u.From).Resource + "'s affiliation"))
|
box.Append(gtk.NewLabel(loadedLocale["setAffilDescPartOne"] + JidMustParse(u.From).Resource + loadedLocale["setAffilDescPartTwo"]))
|
||||||
|
|
||||||
the_entry := gtk.NewEntry()
|
the_entry := gtk.NewEntry()
|
||||||
the_entry.SetText(mu.MucUserItem.Affiliation)
|
the_entry.SetText(mu.MucUserItem.Affiliation)
|
||||||
|
|
||||||
submit := gtk.NewButtonWithLabel("Submit")
|
submit := gtk.NewButtonWithLabel(loadedLocale["submit"])
|
||||||
submit.ConnectClicked(func() {
|
submit.ConnectClicked(func() {
|
||||||
client.SendRaw(fmt.Sprintf(`
|
client.SendRaw(fmt.Sprintf(`
|
||||||
<iq from='%s'
|
<iq from='%s'
|
||||||
@@ -208,13 +269,13 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
win.SetResizable(false)
|
win.SetResizable(false)
|
||||||
|
|
||||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
box.Append(gtk.NewLabel("Set " + JidMustParse(u.From).Resource + "'s role"))
|
box.Append(gtk.NewLabel(loadedLocale["setRoleDescPartOne"] + JidMustParse(u.From).Resource + loadedLocale["setRoleDescPartTwo"]))
|
||||||
box.Append(gtk.NewLabel("Important: if you want this to be permanent, set their affiliation instead"))
|
box.Append(gtk.NewLabel(loadedLocale["setRoleWarning"]))
|
||||||
|
|
||||||
the_entry := gtk.NewEntry()
|
the_entry := gtk.NewEntry()
|
||||||
the_entry.SetText(mu.MucUserItem.Role)
|
the_entry.SetText(mu.MucUserItem.Role)
|
||||||
|
|
||||||
submit := gtk.NewButtonWithLabel("Submit")
|
submit := gtk.NewButtonWithLabel(loadedLocale["submit"])
|
||||||
submit.ConnectClicked(func() {
|
submit.ConnectClicked(func() {
|
||||||
|
|
||||||
client.SendRaw(fmt.Sprintf(`
|
client.SendRaw(fmt.Sprintf(`
|
||||||
@@ -257,15 +318,17 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
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(JidMustParse(u.From).Resource)
|
nick := gtk.NewLabel(JidMustParse(u.From).Resource)
|
||||||
ver_text := gtk.NewLabel("Getting version...")
|
ver_text := gtk.NewLabel(loadedLocale["gettingVersion"])
|
||||||
ver_text.AddCSSClass("visitor")
|
ver_text.AddCSSClass("visitor")
|
||||||
|
|
||||||
win.SetTitle(JidMustParse(u.From).Resource)
|
win.SetTitle(JidMustParse(u.From).Resource)
|
||||||
nick.AddCSSClass("author")
|
nick.AddCSSClass("author")
|
||||||
|
nick.SetSelectable(true)
|
||||||
profile_box.Append(nick)
|
profile_box.Append(nick)
|
||||||
profile_box.Append(ver_text)
|
profile_box.Append(ver_text)
|
||||||
fr := gtk.NewLabel(u.From)
|
fr := gtk.NewLabel(u.From)
|
||||||
fr.AddCSSClass("jid")
|
fr.AddCSSClass("jid")
|
||||||
|
fr.SetSelectable(true)
|
||||||
profile_box.Append(fr)
|
profile_box.Append(fr)
|
||||||
profile_box.Append(ver_text)
|
profile_box.Append(ver_text)
|
||||||
|
|
||||||
@@ -300,14 +363,14 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
if mu.MucUserItem.JID != "" {
|
if mu.MucUserItem.JID != "" {
|
||||||
ji := (gtk.NewLabel(mu.MucUserItem.JID))
|
ji := (gtk.NewLabel(mu.MucUserItem.JID))
|
||||||
ji.AddCSSClass("jid")
|
ji.AddCSSClass("jid")
|
||||||
|
ji.SetSelectable(true)
|
||||||
profile_box.Append(ji)
|
profile_box.Append(ji)
|
||||||
}
|
}
|
||||||
profile_box.Append(gtk.NewLabel("Connected with role " + mu.MucUserItem.Role))
|
profile_box.Append(gtk.NewLabel(loadedLocale["connectedWithRole"] + mu.MucUserItem.Role))
|
||||||
profile_box.Append(gtk.NewLabel("Affiliated as " + mu.MucUserItem.Affiliation))
|
profile_box.Append(gtk.NewLabel(loadedLocale["affiliatedAs"] + mu.MucUserItem.Affiliation))
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
fmt.Println("Attempting to get Disco info")
|
|
||||||
|
|
||||||
myIQ, err := stanza.NewIQ(stanza.Attrs{
|
myIQ, err := stanza.NewIQ(stanza.Attrs{
|
||||||
Type: "get",
|
Type: "get",
|
||||||
@@ -367,13 +430,13 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
|
|
||||||
vr := fmt.Sprintf("%s %s %s", name, version, os)
|
vr := fmt.Sprintf("%s %s %s", name, version, os)
|
||||||
if name == "" && version == "" && os == "" {
|
if name == "" && version == "" && os == "" {
|
||||||
ver_text.SetText("Client responded with empty version")
|
ver_text.SetText(loadedLocale["versionQueryEmpty"])
|
||||||
} else {
|
} else {
|
||||||
ver_text.SetText(vr)
|
ver_text.SetText(vr)
|
||||||
ver_text.RemoveCSSClass("visitor")
|
ver_text.RemoveCSSClass("visitor")
|
||||||
}
|
}
|
||||||
} else if result.Error != nil && result.Error.Type != "" {
|
} else if result.Error != nil && result.Error.Type != "" {
|
||||||
ver_text.SetText("Got error trying to get version")
|
ver_text.SetText(loadedLocale["versionQueryError"])
|
||||||
ver_text.SetTooltipText(result.Error.Reason + ": " + result.Error.Text)
|
ver_text.SetTooltipText(result.Error.Reason + ": " + result.Error.Text)
|
||||||
ver_text.RemoveCSSClass("visitor")
|
ver_text.RemoveCSSClass("visitor")
|
||||||
ver_text.AddCSSClass("error")
|
ver_text.AddCSSClass("error")
|
||||||
@@ -397,13 +460,13 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
profile_box.Prepend(im)
|
profile_box.Prepend(im)
|
||||||
} else {
|
} else {
|
||||||
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
im := createIdenticon(u.From)
|
||||||
im.SetPixelSize(80)
|
im.SetPixelSize(80)
|
||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
profile_box.Prepend(im)
|
profile_box.Prepend(im)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
im := createIdenticon(u.From)
|
||||||
im.SetPixelSize(80)
|
im.SetPixelSize(80)
|
||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
profile_box.Prepend(im)
|
profile_box.Prepend(im)
|
||||||
@@ -418,13 +481,8 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
userbox.AddController(gesture)
|
userbox.AddController(gesture)
|
||||||
userbox.AddController(mod_gesture)
|
userbox.AddController(mod_gesture)
|
||||||
|
|
||||||
if mu.MucUserItem.Role == "moderator" {
|
|
||||||
gen.Prepend(userbox)
|
|
||||||
} else {
|
|
||||||
gen.Append(userbox)
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
})
|
}))
|
||||||
|
|
||||||
headerBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
headerBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
if i >= 500 {
|
if i >= 500 {
|
||||||
@@ -434,12 +492,15 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
} else {
|
} else {
|
||||||
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["group"]))
|
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["group"]))
|
||||||
}
|
}
|
||||||
headerBox.Append(gtk.NewLabel(fmt.Sprintf("%d participant(s)", i)))
|
headerBox.Append(gtk.NewLabel(fmt.Sprintf("%d %s", i, loadedLocale["participants"])))
|
||||||
gen.Prepend(headerBox)
|
gen.Prepend(headerBox)
|
||||||
|
|
||||||
muci := getAvatar(jid, jid)
|
muci := getAvatar(jid, jid)
|
||||||
muci.SetPixelSize(80)
|
muci.SetPixelSize(80)
|
||||||
gen.Prepend(muci)
|
gen.Prepend(muci)
|
||||||
|
muc_name := gtk.NewLabel(typed_tab.name)
|
||||||
|
muc_name.AddCSSClass("author")
|
||||||
|
gen.Prepend(muc_name)
|
||||||
memberList.SetChild(gen)
|
memberList.SetChild(gen)
|
||||||
} else {
|
} else {
|
||||||
memberList.SetChild(gtk.NewLabel(jid))
|
memberList.SetChild(gtk.NewLabel(jid))
|
||||||
@@ -448,5 +509,114 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func showErrorDialog(err error) {
|
func showErrorDialog(err error) {
|
||||||
fmt.Println(err.Error())
|
err_win := gtk.NewWindow()
|
||||||
|
err_win.SetTitle(loadedLocale["error"])
|
||||||
|
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)
|
||||||
|
|
||||||
|
close_btn := gtk.NewButtonWithLabel(loadedLocale["close"])
|
||||||
|
close_btn.ConnectClicked(func() {
|
||||||
|
err_win.SetVisible(false)
|
||||||
|
})
|
||||||
|
box.Append(close_btn)
|
||||||
|
err_win.SetChild(box)
|
||||||
|
err_win.Present()
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIdenticon(word string) *gtk.Image { // This function generates an identicon
|
||||||
|
if !loadedConfig.Identicons {
|
||||||
|
i := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
|
i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
gen, _ := identicon.New("github", 5, 3)
|
||||||
|
ii, _ := gen.Draw(word)
|
||||||
|
im := ii.Image(250)
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err := png.Encode(buf, im)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
loader := gdkpixbuf.NewPixbufLoader()
|
||||||
|
loader.Write(buf.Bytes())
|
||||||
|
loader.Close()
|
||||||
|
i := gtk.NewImageFromPaintable(gdk.NewTextureForPixbuf(loader.Pixbuf()))
|
||||||
|
i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,22 +7,23 @@ 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/jasonlovesdoggo/gopen"
|
"github.com/jasonlovesdoggo/gopen"
|
||||||
"gosrc.io/xmpp/stanza"
|
"gosrc.io/xmpp/stanza"
|
||||||
"mellium.im/xmpp/jid"
|
"mellium.im/xmpp/jid"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
||||||
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
presence, ok := p.(stanza.Presence)
|
presence, ok := p.(stanza.Presence)
|
||||||
if !ok {
|
if !ok {
|
||||||
return gtk.NewLabel("Unsupported message.")
|
return gtk.NewLabel(loadedLocale["unsupportedMessage"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if presence.Type == stanza.PresenceTypeUnavailable {
|
if presence.Type == stanza.PresenceTypeUnavailable {
|
||||||
@@ -31,7 +32,7 @@ func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
if ok {
|
if ok {
|
||||||
if mu.MucUserItem.Affiliation == "outcast" {
|
if mu.MucUserItem.Affiliation == "outcast" {
|
||||||
b.Append(gtk.NewImageFromPaintable(clientAssets["outcast"]))
|
b.Append(gtk.NewImageFromPaintable(clientAssets["outcast"]))
|
||||||
b.Append(gtk.NewLabel(JidMustParse(presence.From).Resource + " has been banned by " + mu.MucUserItem.Actor.Nick + "!"))
|
b.Append(gtk.NewLabel(JidMustParse(presence.From).Resource + loadedLocale["bannedWidget"] + mu.MucUserItem.Actor.Nick + "!"))
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +59,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
ok = m.Get(&readmarker)
|
ok = m.Get(&readmarker)
|
||||||
if ok {
|
if ok {
|
||||||
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
b.Append(gtk.NewLabel(fmt.Sprintf("%s has read to this point", JidMustParse(m.From).Resource)))
|
b.Append(gtk.NewLabel(fmt.Sprintf("%s%s", JidMustParse(m.From).Resource, loadedLocale["readWidget"])))
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
ok = m.Get(&composing)
|
ok = m.Get(&composing)
|
||||||
if ok {
|
if ok {
|
||||||
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
b.Append(gtk.NewLabel(fmt.Sprintf("%s is typing...", JidMustParse(m.From).Resource)))
|
b.Append(gtk.NewLabel(fmt.Sprintf("%s%s", JidMustParse(m.From).Resource, loadedLocale["isTyping"])))
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +85,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
sid := StanzaID{}
|
sid := StanzaID{}
|
||||||
m.Get(&sid)
|
m.Get(&sid)
|
||||||
|
|
||||||
mainBox := gtk.NewBox(gtk.OrientationVertical, 0)
|
mainBox := gtk.NewBox(gtk.OrientationVertical, 10)
|
||||||
gesture := gtk.NewGestureClick()
|
gesture := gtk.NewGestureClick()
|
||||||
gesture.SetButton(3) // Right click
|
gesture.SetButton(3) // Right click
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
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)
|
||||||
@@ -117,7 +118,15 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
|
|
||||||
quote := gtk.NewButtonWithLabel("Quote")
|
quote := gtk.NewButtonWithLabel("Quote")
|
||||||
quote.ConnectClicked(func() {
|
quote.ConnectClicked(func() {
|
||||||
message_en.SetText("> " + m.Body + "\n")
|
lines := strings.Split(m.Body, "\n")
|
||||||
|
for i, line := range lines {
|
||||||
|
quoteline := "> " + line
|
||||||
|
lines[i] = quoteline
|
||||||
|
}
|
||||||
|
|
||||||
|
newstr := strings.Join(lines, "\n") + "\n\n"
|
||||||
|
|
||||||
|
message_en.SetText(newstr)
|
||||||
})
|
})
|
||||||
rc_box.Append(quote)
|
rc_box.Append(quote)
|
||||||
|
|
||||||
@@ -146,43 +155,47 @@ 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)
|
||||||
|
im := createIdenticon(m.From)
|
||||||
|
im.SetPixelSize(40)
|
||||||
|
im.AddCSSClass("author_img")
|
||||||
|
authorBox.Append(im)
|
||||||
if vu.Photo != "" {
|
if vu.Photo != "" {
|
||||||
im := getAvatar(m.From, vu.Photo)
|
go func() {
|
||||||
im.SetPixelSize(40)
|
new_im := getAvatar(m.From, vu.Photo)
|
||||||
im.AddCSSClass("author_img")
|
glib.IdleAdd(func() {
|
||||||
authorBox.Append(im)
|
new_im.SetPixelSize(40)
|
||||||
} else {
|
new_im.AddCSSClass("author_img")
|
||||||
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
authorBox.Remove(im)
|
||||||
im.SetPixelSize(40)
|
authorBox.Prepend(new_im)
|
||||||
im.AddCSSClass("author_img")
|
})
|
||||||
authorBox.Append(im)
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
im := createIdenticon(m.From)
|
||||||
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 {
|
} else if m.Type == stanza.MessageTypeChat {
|
||||||
al.SetText(al.Text() + " whispers")
|
al.SetText(al.Text() + loadedLocale["whispers"])
|
||||||
}
|
}
|
||||||
|
|
||||||
authorBox.Append(al)
|
authorBox.Append(al)
|
||||||
@@ -195,18 +208,20 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
|
|
||||||
mlabel := gtk.NewLabel(m.Body)
|
mlabel := gtk.NewLabel(m.Body)
|
||||||
if m.Body == "" {
|
if m.Body == "" {
|
||||||
mlabel.SetText("No body set")
|
mlabel.SetText(loadedLocale["noBodySet"])
|
||||||
mlabel.AddCSSClass("visitor")
|
mlabel.AddCSSClass("visitor")
|
||||||
}
|
}
|
||||||
mlabel.SetWrap(true)
|
mlabel.SetWrap(true)
|
||||||
mlabel.SetSelectable(true)
|
mlabel.SetSelectable(true)
|
||||||
mlabel.SetHAlign(gtk.AlignFill)
|
mlabel.SetHAlign(gtk.AlignFill)
|
||||||
|
|
||||||
mum := MucUser{}
|
/*
|
||||||
ok = m.Get(&mum)
|
mum := MucUser{}
|
||||||
if ok {
|
ok = m.Get(&mum)
|
||||||
mlabel.SetText(fmt.Sprintf("%s's affiliation has been changed to %s", mum.MucUserItem.JID, mum.MucUserItem.Affiliation))
|
if ok {
|
||||||
}
|
mlabel.SetText(fmt.Sprintf("%s%s%s", mum.MucUserItem.JID, loadedLocale["affilChange"], mum.MucUserItem.Affiliation))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
contentBox.Append(mlabel)
|
contentBox.Append(mlabel)
|
||||||
|
|
||||||
@@ -239,6 +254,30 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
mainBox.Append(subjectlabel)
|
mainBox.Append(subjectlabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
link_preview := LinkPreview{}
|
||||||
|
ok = m.Get(&link_preview)
|
||||||
|
if ok {
|
||||||
|
lp_box := gtk.NewBox(gtk.OrientationVertical, 10)
|
||||||
|
lp_box.AddCSSClass("link_preview")
|
||||||
|
lp_title := gtk.NewLabel(link_preview.Title)
|
||||||
|
lp_title.SetSelectable(true)
|
||||||
|
lp_title.SetWrap(true)
|
||||||
|
lp_title.SetHAlign(gtk.AlignFill)
|
||||||
|
lp_desc := gtk.NewLabel(link_preview.URL + "\n" + link_preview.Description)
|
||||||
|
lp_desc.SetSelectable(true)
|
||||||
|
lp_desc.SetWrap(true)
|
||||||
|
lp_desc.SetHAlign(gtk.AlignFill)
|
||||||
|
|
||||||
|
lp_box.Append(lp_title)
|
||||||
|
lp_box.Append(lp_desc)
|
||||||
|
|
||||||
|
warning := gtk.NewLabel("⚠️")
|
||||||
|
warning.SetTooltipText(loadedLocale["linkPreviewWarning"])
|
||||||
|
lp_box.Append(warning)
|
||||||
|
|
||||||
|
mainBox.Append(lp_box)
|
||||||
|
}
|
||||||
|
|
||||||
return mainBox
|
return mainBox
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,32 +290,36 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou
|
|||||||
oghash := hash
|
oghash := hash
|
||||||
p, err := ensureCache()
|
p, err := ensureCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gtk.NewImageFromPaintable(clientAssets["FailedAvatar"])
|
return createIdenticon(j)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hash == "" {
|
if hash == "" {
|
||||||
fmt.Println("Hash is nil!")
|
return createIdenticon(j)
|
||||||
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok := invalidImages[hash]
|
_, ok := invalidImages[hash]
|
||||||
if ok {
|
if ok {
|
||||||
fmt.Println("Image is invalid")
|
return createIdenticon(j)
|
||||||
return gtk.NewImageFromPaintable(clientAssets["FailedAvatar"])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash = filepath.Join(p, sanitizefilename.Sanitize(hash))
|
hash = filepath.Join(p, hash)
|
||||||
|
|
||||||
_, err = os.ReadFile(hash)
|
_, err = os.ReadFile(hash)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return newImageFromPath(hash)
|
i, err := newImageFromPath(hash)
|
||||||
|
if err != nil {
|
||||||
|
invalidImages[oghash] = true
|
||||||
|
return createIdenticon(j)
|
||||||
|
}
|
||||||
|
i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
iqResp, err := stanza.NewIQ(stanza.Attrs{
|
iqResp, err := stanza.NewIQ(stanza.Attrs{
|
||||||
Type: "get",
|
Type: "get",
|
||||||
From: clientroot.Session.BindJid,
|
From: clientroot.Session.BindJid,
|
||||||
To: j,
|
To: j,
|
||||||
Id: "vc2",
|
Id: uuid.New().String(),
|
||||||
Lang: "en",
|
Lang: "en",
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -294,14 +337,14 @@ 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 gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
return createIdenticon(j)
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
fmt.Println("Blocking image")
|
||||||
invalidImages[oghash] = true
|
invalidImages[oghash] = true
|
||||||
return gtk.NewImageFromPaintable(clientAssets["FailedAvatar"])
|
return createIdenticon(j)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := base64.StdEncoding.DecodeString(base64_data)
|
data, err := base64.StdEncoding.DecodeString(base64_data)
|
||||||
@@ -314,5 +357,11 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newImageFromPath(hash)
|
i, err := newImageFromPath(hash)
|
||||||
|
if err != nil {
|
||||||
|
invalidImages[oghash] = true
|
||||||
|
return createIdenticon(j)
|
||||||
|
}
|
||||||
|
i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ func dropToSignInPage(err error) {
|
|||||||
nickname_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
nickname_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
insecure_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
insecure_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
server_label := gtk.NewLabel("Server: ")
|
server_label := gtk.NewLabel(loadedLocale["SIServerLabel"])
|
||||||
username_label := gtk.NewLabel("JID: ")
|
username_label := gtk.NewLabel(loadedLocale["SIUsernameLabel"])
|
||||||
password_label := gtk.NewLabel("Password: ")
|
password_label := gtk.NewLabel(loadedLocale["SIPasswordLabel"])
|
||||||
nickname_label := gtk.NewLabel("Nickname: ")
|
nickname_label := gtk.NewLabel(loadedLocale["SINicknameLabel"])
|
||||||
insecure_label := gtk.NewLabel("Insecure: (?)")
|
insecure_label := gtk.NewLabel(loadedLocale["SIInsecureLabel"])
|
||||||
insecure_label.SetTooltipText("Tick this if you need to connect without TLS, usually for connecting to Tor XMPP servers")
|
insecure_label.SetTooltipText(loadedLocale["SIInsecureLabelTooltip"])
|
||||||
|
|
||||||
server_entry := gtk.NewEntry()
|
server_entry := gtk.NewEntry()
|
||||||
server_entry.SetHAlign(gtk.AlignEnd)
|
server_entry.SetHAlign(gtk.AlignEnd)
|
||||||
@@ -83,7 +83,7 @@ func dropToSignInPage(err error) {
|
|||||||
form_box.Append(nickname_box)
|
form_box.Append(nickname_box)
|
||||||
form_box.Append(insecure_box)
|
form_box.Append(insecure_box)
|
||||||
|
|
||||||
sumbit_btn := gtk.NewButtonWithLabel("Submit")
|
sumbit_btn := gtk.NewButtonWithLabel(loadedLocale["submit"])
|
||||||
sumbit_btn.ConnectClicked(func() {
|
sumbit_btn.ConnectClicked(func() {
|
||||||
conf := new(lambdaConfig)
|
conf := new(lambdaConfig)
|
||||||
conf.Server = server_entry.Text()
|
conf.Server = server_entry.Text()
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Generic helpers
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func rangeOrdered(m *sync.Map, fn func(k, v any) bool) {
|
||||||
|
var keys []string
|
||||||
|
|
||||||
|
m.Range(func(k, v any) bool {
|
||||||
|
keys = append(keys, k.(string))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
v, _ := m.Load(k)
|
||||||
|
if !fn(k, v) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Default language is en_GB
|
||||||
|
var loadedLocale = make(map[string]string)
|
||||||
|
|
||||||
|
var enGB = map[string]string{ // British English
|
||||||
|
// main.go
|
||||||
|
|
||||||
|
"appName": "Lambda",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"submit": "Submit",
|
||||||
|
"join": "Join",
|
||||||
|
"send": "Send",
|
||||||
|
"error": "Error",
|
||||||
|
"close": "Close",
|
||||||
|
"userRequested": "User requested",
|
||||||
|
"configResourceEmptyWarning": "Config resource is empty! Generating a random one",
|
||||||
|
"attention": "Attention",
|
||||||
|
"disconnected": "Disconnected: ",
|
||||||
|
"connecting": "Connecting...",
|
||||||
|
"milliseconds": "ms",
|
||||||
|
"KBPerSecond": "KB/s",
|
||||||
|
"connectedAs": "Connected as ",
|
||||||
|
"bindedJid": "Binded JID: ",
|
||||||
|
"usingTLS": "Using TLS: ",
|
||||||
|
"joinMUCMenu": "Join MUC",
|
||||||
|
"joinMUCJIDEntry": "MUC JID:",
|
||||||
|
"joinMUCNickEntry": "Nick:",
|
||||||
|
"joinMUCDiscoCheck": "Check MUC features before joining",
|
||||||
|
"joinMUCDiscoCheckTooltip": "If you are creating a MUC through this window then turn this off",
|
||||||
|
"joinPreviewTitle": "Joining ",
|
||||||
|
"joinPasswordRequired": "Password required",
|
||||||
|
"muc_passwordprotected_description": "This MUC is password-protected",
|
||||||
|
"muc_unsecured_description": "This MUC does not require a password",
|
||||||
|
"muc_membersonly_description": "Only members can join this MUC",
|
||||||
|
"muc_open_description": "Anyone can join this MUC",
|
||||||
|
"muc_moderated_description": "Only members can speak in this MUC",
|
||||||
|
"muc_unmoderated_description": "Anyone can speak in this MUC",
|
||||||
|
"muc_nonanonymous_description": "This MUC is non-anonymous, your JID will be visible to other users",
|
||||||
|
"muc_semianonymous_description": "This MUC is semi-anonymous, only moderators will see your full JID",
|
||||||
|
"muc_persistent_description": "This MUC is persistent, it will not be deleted when the last user leaves",
|
||||||
|
"muc_temporary_description": "This MUC is temporary, it will be deleted when the last user leaves",
|
||||||
|
"muc_public_description": "This MUC can be found in directories and search engines",
|
||||||
|
"muc_hidden_description": "This MUC is hidden and cannot be found in directories or search engines",
|
||||||
|
"urn:xmpp:mam_description": "This MUC supports archiving via MAM",
|
||||||
|
"urn:xmpp:message-moderate_description": "This MUC supports message moderation",
|
||||||
|
"discoFail": "Failed to get Disco info",
|
||||||
|
"startDMMenu": "Start DM",
|
||||||
|
"destroyMUCMenu": "Destroy MUC",
|
||||||
|
"aboutMenu": "About",
|
||||||
|
"destroyMUCWarningOne": "Are you sure? This MUC will be gone forever! (a very long time)",
|
||||||
|
"destroyMUCWarningTwo": "If you wish to continue, type 'I understand'",
|
||||||
|
"destroyMUCPassword": "I understand",
|
||||||
|
"destroyMUCActionButton": "Destroy",
|
||||||
|
"destroyMUCNotOwnerWarning": "You are not an owner of this MUC and thus will most likely not be able to delete it",
|
||||||
|
"pingBarTooltip": "Ping between you and your XMPP server\nRight-click to see graph",
|
||||||
|
"pingGraphTitle": "Server latency",
|
||||||
|
"pingGraphYAxis": "Ping (ms)",
|
||||||
|
"throughputTooltip": "Throughput of your XMPP connection in KB/s",
|
||||||
|
"messageEntryPlaceholder": "Say something, what else are you going to do here?",
|
||||||
|
|
||||||
|
// gtk-message.go
|
||||||
|
|
||||||
|
"unsupportedMessage": "Unsupported message.",
|
||||||
|
"bannedWidget": " has been banned by ",
|
||||||
|
"readWidget": " has read to this point",
|
||||||
|
"isTyping": " is typing...",
|
||||||
|
"whispers": " whispers",
|
||||||
|
"noBodySet": "No body set",
|
||||||
|
"affilChange": "'s affiliation has been changed to ",
|
||||||
|
"linkPreviewWarning": "This link preview was generated by the client sending it and may not be accurate of the actual website content",
|
||||||
|
|
||||||
|
// gtk-helpers.go
|
||||||
|
|
||||||
|
"getPastMessages": "Get past messages...",
|
||||||
|
"clickForMoreInfo": "Click for more information",
|
||||||
|
"ban": "Ban",
|
||||||
|
"kick": "Kick",
|
||||||
|
"setAffil": "Set affiliation",
|
||||||
|
"setAffilDescPartOne": "Set ",
|
||||||
|
"setAffilDescPartTwo": "'s affiliation",
|
||||||
|
"setRole": "Set role",
|
||||||
|
"setRoleDescPartOne": "Set ",
|
||||||
|
"setRoleDescPartTwo": "'s role",
|
||||||
|
"setRoleWarning": "Important: if you want this to be permanent, set their affiliation instead",
|
||||||
|
"gettingVersion": "Getting version...",
|
||||||
|
"connectedWithRole": "Connected with role ",
|
||||||
|
"affiliatedAs": "Affiliated as ",
|
||||||
|
"participants": "participant(s)",
|
||||||
|
"versionQueryEmpty": "Client responded with empty version",
|
||||||
|
"versionQueryError": "Got error trying to get version",
|
||||||
|
|
||||||
|
// gtk-signin.go
|
||||||
|
|
||||||
|
"SIServerLabel": "Server: ",
|
||||||
|
"SIUsernameLabel": "Username: ",
|
||||||
|
"SIPasswordLabel": "Password: ",
|
||||||
|
"SINicknameLabel": "Nickname: ",
|
||||||
|
"SIInsecureLabel": "Insecure: (?)",
|
||||||
|
"SIInsecureLabelTooltip": "Tick this if you need to connect without TLS, usually for connecting to Tor XMPP servers",
|
||||||
|
}
|
||||||
|
|
||||||
|
var kaGE = map[string]string{ // Georgian (Georgia)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var roRo = map[string]string{ // Romanian (Romania)
|
||||||
|
"appName": "Lambda",
|
||||||
|
"cancel": "Canselează",
|
||||||
|
"submit": "A preda",
|
||||||
|
"join": "Intră",
|
||||||
|
"send": "Trimite",
|
||||||
|
"error": "Eroare",
|
||||||
|
"close": "închide",
|
||||||
|
"userRequested": "Uzator cerut",
|
||||||
|
"configResourceEmptyWarning": "Resursa configurată este goala! Creiez unu aleatoriu",
|
||||||
|
"attention": "Atenție",
|
||||||
|
"disconnected": "Deconectat",
|
||||||
|
"connecting": "Conectat",
|
||||||
|
"bindedJid": "Lipit JID",
|
||||||
|
"joinMUCMenu": "Intră pe MUC",
|
||||||
|
"joinMUCJIDEntry": "MUC JID:",
|
||||||
|
"joinMUCNickEntry": "Poreclă:",
|
||||||
|
"joinMUCDiscoCheck": "Verifica detalile de MUC înainte sa intri",
|
||||||
|
"joinMUCDiscoCheckTooltip": "Dacă creiezi un MUC prin această oglindă închido",
|
||||||
|
}
|
||||||
|
|
||||||
|
var enUS = enGB // American English
|
||||||
|
|
||||||
|
var locales = map[string]map[string]string{
|
||||||
|
"en_GB": enGB,
|
||||||
|
"ka_GE": kaGE,
|
||||||
|
"en_US": enUS,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Load locale according to user configuration
|
||||||
|
func init() {
|
||||||
|
loadedLocale = locales["en_GB"]
|
||||||
|
}
|
||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"gosrc.io/xmpp"
|
"gosrc.io/xmpp"
|
||||||
"gosrc.io/xmpp/stanza"
|
"gosrc.io/xmpp/stanza"
|
||||||
"mellium.im/xmpp/jid"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
@@ -40,12 +39,17 @@ var connectionIcon *gtk.Image
|
|||||||
var mStatus *gtk.Label
|
var mStatus *gtk.Label
|
||||||
var mIcon *gtk.Image
|
var mIcon *gtk.Image
|
||||||
|
|
||||||
|
/*
|
||||||
|
var sStatus *gtk.Label
|
||||||
|
var sIcon *gtk.Image
|
||||||
|
*/
|
||||||
|
|
||||||
|
var typingStatus *gtk.Label
|
||||||
|
|
||||||
var pingStatus *gtk.Label
|
var pingStatus *gtk.Label
|
||||||
|
|
||||||
// var msgs *gtk.ListBox
|
|
||||||
var content *gtk.Widgetter
|
var content *gtk.Widgetter
|
||||||
|
|
||||||
// var tabs map[string]*chatTab = make(map[string]*chatTab)
|
|
||||||
var tabs sync.Map
|
var tabs sync.Map
|
||||||
var current string
|
var current string
|
||||||
|
|
||||||
@@ -73,8 +77,10 @@ var pingTimes = [][]float64{}
|
|||||||
|
|
||||||
var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
|
var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
|
||||||
|
|
||||||
|
var xmlLog *os.File
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
beeep.AppName = "Lambda"
|
beeep.AppName = loadedLocale["appName"]
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for fn := range uiQueue {
|
for fn := range uiQueue {
|
||||||
@@ -89,6 +95,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
pingTimes = append(pingTimes, []float64{})
|
pingTimes = append(pingTimes, []float64{})
|
||||||
p, err := ensureConfig()
|
p, err := ensureConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -99,7 +106,6 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
dropToSignInPage(err)
|
dropToSignInPage(err)
|
||||||
return
|
return
|
||||||
// panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = toml.Decode(string(b), &loadedConfig)
|
_, err = toml.Decode(string(b), &loadedConfig)
|
||||||
@@ -108,22 +114,35 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if loadedConfig.Resource == "" {
|
if loadedConfig.Resource == "" {
|
||||||
fmt.Println("Config resource is empty! Generating a random one")
|
fmt.Println(loadedLocale["configResourceEmptyWarning"])
|
||||||
loadedConfig.Resource = randomClientResource()
|
loadedConfig.Resource = randomClientResource()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !loadedConfig.Debug {
|
||||||
|
xmlLog, err = os.CreateTemp("", "xmpp-log")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.Remove(xmlLog.Name())
|
||||||
|
} else {
|
||||||
|
xmlLog = os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
config := xmpp.Config{
|
config := xmpp.Config{
|
||||||
TransportConfiguration: xmpp.TransportConfiguration{
|
TransportConfiguration: xmpp.TransportConfiguration{
|
||||||
Address: loadedConfig.Server,
|
Address: loadedConfig.Server,
|
||||||
CharsetReader: func(c string, input io.Reader) (io.Reader, error) {
|
CharsetReader: func(c string, input io.Reader) (io.Reader, error) {
|
||||||
return charset.NewReaderLabel(c, input)
|
return charset.NewReaderLabel(c, input)
|
||||||
},
|
},
|
||||||
|
ConnectTimeout: 300,
|
||||||
},
|
},
|
||||||
Jid: loadedConfig.Username + "/" + loadedConfig.Resource,
|
Jid: loadedConfig.Username + "/" + loadedConfig.Resource,
|
||||||
Credential: xmpp.Password(loadedConfig.Password),
|
Credential: xmpp.Password(loadedConfig.Password),
|
||||||
Insecure: loadedConfig.Insecure,
|
Insecure: loadedConfig.Insecure,
|
||||||
// StreamLogger: os.Stdout,
|
|
||||||
StreamManagementEnable: true,
|
StreamManagementEnable: true,
|
||||||
|
ConnectTimeout: 300,
|
||||||
|
StreamLogger: xmlLog,
|
||||||
}
|
}
|
||||||
router := xmpp.NewRouter()
|
router := xmpp.NewRouter()
|
||||||
|
|
||||||
@@ -176,7 +195,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v := &stanza.Version{}
|
v := &stanza.Version{}
|
||||||
v = v.SetInfo("Lambda", lambda_version, runtime.GOOS) // TODO: Allow spoofing on user request
|
v = v.SetInfo(loadedLocale["appName"], lambda_version, runtime.GOOS) // TODO: Allow spoofing on user request
|
||||||
|
|
||||||
iqResp.Payload = v
|
iqResp.Payload = v
|
||||||
s.Send(iqResp)
|
s.Send(iqResp)
|
||||||
@@ -201,12 +220,14 @@ func main() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
originator := JidMustParse(m.From).Bare()
|
originator := JidMustParse(m.From).Bare()
|
||||||
mStatus.SetText(originator)
|
glib.IdleAdd(func() {
|
||||||
|
mStatus.SetText(originator)
|
||||||
|
})
|
||||||
|
|
||||||
at := new(Attention)
|
at := new(Attention)
|
||||||
ok = m.Get(at)
|
ok = m.Get(at)
|
||||||
if ok {
|
if ok {
|
||||||
beeep.Notify("Attention", fmt.Sprintf("%s: %s", JidMustParse(m.From).Resource, m.Body), commentBytes) // TODO: Use localpart if DM
|
beeep.Notify(loadedLocale["attention"], fmt.Sprintf("%s: %s", JidMustParse(m.From).Resource, m.Body), commentBytes) // TODO: Use localpart if DM
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle mentions
|
// Handle mentions
|
||||||
@@ -225,7 +246,9 @@ func main() {
|
|||||||
if ok {
|
if ok {
|
||||||
if JidMustParse(fm.From).Bare() == JidMustParse(m.From).Bare() {
|
if JidMustParse(fm.From).Bare() == JidMustParse(m.From).Bare() {
|
||||||
p = sc.Forwarded.Stanza
|
p = sc.Forwarded.Stanza
|
||||||
|
orig := m.To
|
||||||
m = sc.Forwarded.Stanza.(stanza.Message)
|
m = sc.Forwarded.Stanza.(stanza.Message)
|
||||||
|
m.To = orig
|
||||||
} else {
|
} else {
|
||||||
panic(fmt.Sprintln("Impersonation: ", fm.From, m.From))
|
panic(fmt.Sprintln("Impersonation: ", fm.From, m.From))
|
||||||
}
|
}
|
||||||
@@ -245,9 +268,21 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
composing := stanza.StateComposing{}
|
||||||
|
ok = m.Get(&composing)
|
||||||
|
if ok && current == JidMustParse(m.From).Bare() {
|
||||||
|
typingStatus.SetText(fmt.Sprintf("%s%s", m.From, loadedLocale["isTyping"]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
inactive := stanza.StateInactive{}
|
||||||
|
ok = m.Get(&inactive)
|
||||||
|
if ok && current == JidMustParse(m.From).Bare() {
|
||||||
|
typingStatus.SetText("")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -259,7 +294,9 @@ func main() {
|
|||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
typed_tab.msgs.Append(b)
|
typed_tab.msgs.Append(b)
|
||||||
scrollToBottomAfterUpdate(scroller)
|
if current == JidMustParse(m.From).Bare() {
|
||||||
|
scrollToBottomAfterUpdate(scroller)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Got message when the tab does not exist!")
|
fmt.Println("Got message when the tab does not exist!")
|
||||||
}
|
}
|
||||||
@@ -268,7 +305,6 @@ func main() {
|
|||||||
if ok {
|
if ok {
|
||||||
b.Append(ba)
|
b.Append(ba)
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -323,7 +359,9 @@ func main() {
|
|||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
typed_tab.msgs.Append(b)
|
typed_tab.msgs.Append(b)
|
||||||
scrollToBottomAfterUpdate(scroller)
|
if current == muc {
|
||||||
|
scrollToBottomAfterUpdate(scroller)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Got message when the tab does not exist!")
|
fmt.Println("Got message when the tab does not exist!")
|
||||||
}
|
}
|
||||||
@@ -344,7 +382,9 @@ func main() {
|
|||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
typed_tab.msgs.Append(b)
|
typed_tab.msgs.Append(b)
|
||||||
scrollToBottomAfterUpdate(scroller)
|
if current == muc {
|
||||||
|
scrollToBottomAfterUpdate(scroller)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Got message when the tab does not exist!")
|
fmt.Println("Got message when the tab does not exist!")
|
||||||
}
|
}
|
||||||
@@ -355,48 +395,13 @@ func main() {
|
|||||||
|
|
||||||
} else { // This is a presence stanza from a regular user
|
} else { // This is a presence stanza from a regular user
|
||||||
// The code is basically the exact same as above, we just don't check for mucuser
|
// The code is basically the exact same as above, we just don't check for mucuser
|
||||||
user := jid.MustParse(presence.From).Bare().String()
|
|
||||||
_, ok := userdevices.Load(user)
|
|
||||||
_, mok := mucmembers.Load(user)
|
|
||||||
if !ok && !mok { // FIXME: The initial muc presence gets picked up from this check
|
|
||||||
ok := createTab(user, false)
|
|
||||||
if ok {
|
|
||||||
userdevices.Store(user, userUnit{})
|
|
||||||
|
|
||||||
b := gtk.NewLabel(user)
|
// TODO: Presence handling code goes here
|
||||||
gesture1 := gtk.NewGestureClick()
|
|
||||||
gesture1.SetButton(1)
|
|
||||||
gesture1.Connect("pressed", func() {
|
|
||||||
switchToTab(user, &window.Window)
|
|
||||||
})
|
|
||||||
|
|
||||||
b.AddController(gesture1)
|
|
||||||
menu.Append(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unit, ok := userdevices.Load(user)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resource := jid.MustParse(presence.From).Resourcepart()
|
|
||||||
|
|
||||||
typed_unit := unit.(userUnit)
|
|
||||||
|
|
||||||
if presence.Type != "unavailable" {
|
|
||||||
typed_unit.Devices.Store(resource, presence)
|
|
||||||
} else {
|
|
||||||
typed_unit.Devices.Delete(resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
userdevices.Store(user, typed_unit)
|
|
||||||
}
|
}
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
c, err := xmpp.NewClient(&config, router, func(err error) {
|
c, err := xmpp.NewClient(&config, router, func(err error) {
|
||||||
connectionStatus.SetText(fmt.Sprintf("Disconnected: %s", err.Error()))
|
connectionStatus.SetText(fmt.Sprintf("%s%s", loadedLocale["disconnected"], err.Error()))
|
||||||
connectionIcon.SetFromPaintable(clientAssets["disconnect"])
|
connectionIcon.SetFromPaintable(clientAssets["disconnect"])
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -409,33 +414,72 @@ func main() {
|
|||||||
|
|
||||||
cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) {
|
cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) {
|
||||||
fmt.Println("XMPP client connected")
|
fmt.Println("XMPP client connected")
|
||||||
|
// Ping
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
pingStatus.AddCSSClass("pending")
|
go func() {
|
||||||
before := time.Now()
|
pingStatus.AddCSSClass("pending")
|
||||||
iq := new(stanza.IQ)
|
before := time.Now()
|
||||||
iq.From = clientroot.Session.BindJid
|
iq := new(stanza.IQ)
|
||||||
iq.To = iq.From
|
iq.From = clientroot.Session.BindJid
|
||||||
iq.Type = "get"
|
iq.To = iq.From
|
||||||
|
iq.Type = "get"
|
||||||
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
mychan, err := client.SendIQ(ctx, iq)
|
mychan, err := client.SendIQ(ctx, iq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
_ = <-mychan
|
_ = <-mychan
|
||||||
|
delay := time.Since(before) / time.Millisecond
|
||||||
|
pingTimes[0] = append(pingTimes[0], float64(delay))
|
||||||
|
|
||||||
pingStatus.RemoveCSSClass("pending")
|
glib.IdleAdd(func() {
|
||||||
delay := time.Since(before) / time.Millisecond
|
pingStatus.RemoveCSSClass("pending")
|
||||||
pingStatus.SetText(fmt.Sprintf("%d ms", delay))
|
pingStatus.SetText(fmt.Sprintf("%d %s", delay, loadedLocale["milliseconds"]))
|
||||||
pingTimes[0] = append(pingTimes[0], float64(delay))
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
connectionStatus.SetText(fmt.Sprintf("Connected as %s", JidMustParse(clientroot.Session.BindJid).Bare()))
|
|
||||||
connectionStatus.SetTooltipText(fmt.Sprintf("Binded JID: %s\nUsing TLS: %t", clientroot.Session.BindJid, clientroot.Session.TlsEnabled))
|
// Throughput
|
||||||
connectionIcon.SetFromPaintable(clientAssets["connect"])
|
/*
|
||||||
|
var oldsize int64
|
||||||
|
var newsize int64
|
||||||
|
var diff float64
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
stat, err := xmlLog.Stat()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newsize = stat.Size()
|
||||||
|
diff = float64(newsize-oldsize) / 1000
|
||||||
|
|
||||||
|
ic := clientAssets["car"]
|
||||||
|
if diff >= 25 {
|
||||||
|
ic = clientAssets["car_high"]
|
||||||
|
}
|
||||||
|
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
sStatus.SetText(fmt.Sprintf("%.2f%s", diff, loadedLocale["KBPerSecond"]))
|
||||||
|
sIcon.SetFromPaintable(ic)
|
||||||
|
})
|
||||||
|
|
||||||
|
oldsize = newsize
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
*/
|
||||||
|
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
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))
|
||||||
|
connectionIcon.SetFromPaintable(clientAssets["connect"])
|
||||||
|
})
|
||||||
// Enable carbons
|
// Enable carbons
|
||||||
client.SendRaw(fmt.Sprintf(
|
client.SendRaw(fmt.Sprintf(
|
||||||
`<iq xmlns='jabber:client'
|
`<iq xmlns='jabber:client'
|
||||||
@@ -446,6 +490,55 @@ func main() {
|
|||||||
</iq>
|
</iq>
|
||||||
`, clientroot.Session.BindJid))
|
`, clientroot.Session.BindJid))
|
||||||
|
|
||||||
|
// Fetch roster
|
||||||
|
i, err := stanza.NewIQ(stanza.Attrs{
|
||||||
|
Type: "get",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
roster := i.RosterItems()
|
||||||
|
i.Payload = roster
|
||||||
|
mychan, err := c.SendIQ(context.TODO(), i)
|
||||||
|
result := <-mychan
|
||||||
|
if err == nil {
|
||||||
|
items, ok := result.Payload.(*stanza.RosterItems)
|
||||||
|
if ok {
|
||||||
|
for _, v := range items.Items {
|
||||||
|
name := v.Name
|
||||||
|
jid := v.Jid
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = jid
|
||||||
|
}
|
||||||
|
|
||||||
|
createTab(jid, false, name)
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
box := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
|
b := gtk.NewLabel(name)
|
||||||
|
gesture1 := gtk.NewGestureClick()
|
||||||
|
gesture1.SetButton(1)
|
||||||
|
gesture1.Connect("pressed", func() {
|
||||||
|
switchToTab(jid, &window.Window)
|
||||||
|
})
|
||||||
|
box.Append(b)
|
||||||
|
go func() {
|
||||||
|
new_im := getAvatar(jid, jid) // TODO: Use PEP avatar and do not use JID as hash
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
new_im.SetPixelSize(40)
|
||||||
|
box.Prepend(new_im)
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
box.AddController(gesture1)
|
||||||
|
menu.Append(box)
|
||||||
|
menu.Append(gtk.NewSeparator(gtk.OrientationHorizontal))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Join rooms in bookmarks
|
// Join rooms in bookmarks
|
||||||
if loadedConfig.JoinBookmarks {
|
if loadedConfig.JoinBookmarks {
|
||||||
books, err := stanza.NewItemsRequest("", "urn:xmpp:bookmarks:1", 0)
|
books, err := stanza.NewItemsRequest("", "urn:xmpp:bookmarks:1", 0)
|
||||||
@@ -456,66 +549,99 @@ func main() {
|
|||||||
res, ok := result.Payload.(*stanza.PubSubGeneric)
|
res, ok := result.Payload.(*stanza.PubSubGeneric)
|
||||||
if ok {
|
if ok {
|
||||||
for _, item := range res.Items.List {
|
for _, item := range res.Items.List {
|
||||||
go func() {
|
jid := item.Id
|
||||||
jid := item.Id
|
node := item.Any
|
||||||
node := item.Any
|
autojoin := false
|
||||||
autojoin := false
|
name := jid
|
||||||
nick := loadedConfig.Nick
|
password := ""
|
||||||
for _, attr := range node.Attrs {
|
nick := loadedConfig.Nick
|
||||||
if attr.Name.Local == "autojoin" {
|
for _, attr := range node.Attrs {
|
||||||
autojoin = attr.Value == "true"
|
if attr.Name.Local == "autojoin" {
|
||||||
}
|
autojoin = attr.Value == "true"
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, node := range node.Nodes {
|
for _, attr := range node.Attrs {
|
||||||
if node.XMLName.Local == "nick" {
|
if attr.Name.Local == "name" {
|
||||||
nick = node.Content
|
name = attr.Value
|
||||||
}
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_, ok := tabs.Load(jid)
|
for _, attr := range node.Attrs {
|
||||||
if !ok && autojoin {
|
if attr.Name.Local == "autojoin" {
|
||||||
err := joinMuc(client, clientroot.Session.BindJid, jid, nick)
|
autojoin = attr.Value == "true"
|
||||||
if err != nil {
|
break
|
||||||
panic(err)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createTab(jid, true)
|
for _, node := range node.Nodes {
|
||||||
b := gtk.NewLabel(jid)
|
if node.XMLName.Local == "nick" {
|
||||||
|
nick = node.Content
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range node.Nodes {
|
||||||
|
if node.XMLName.Local == "password" {
|
||||||
|
password = node.Content
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := tabs.Load(jid)
|
||||||
|
if !ok && autojoin {
|
||||||
|
joinMuc(client, clientroot.Session.BindJid, jid, nick, password)
|
||||||
|
createTab(jid, true, name)
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
box := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
|
b := gtk.NewLabel(name)
|
||||||
gesture1 := gtk.NewGestureClick()
|
gesture1 := gtk.NewGestureClick()
|
||||||
gesture1.SetButton(1)
|
gesture1.SetButton(1)
|
||||||
gesture1.Connect("pressed", func() {
|
gesture1.Connect("pressed", func() {
|
||||||
switchToTab(jid, &window.Window)
|
switchToTab(jid, &window.Window)
|
||||||
})
|
})
|
||||||
|
box.Append(b)
|
||||||
|
go func() {
|
||||||
|
new_im := getAvatar(jid, jid)
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
new_im.SetPixelSize(40)
|
||||||
|
box.Prepend(new_im)
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
box.AddController(gesture1)
|
||||||
|
menu.Append(box)
|
||||||
|
menu.Append(gtk.NewSeparator(gtk.OrientationHorizontal))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
b.AddController(gesture1)
|
|
||||||
menu.Append(b)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
conc := func() {
|
conc := func() {
|
||||||
time.Sleep(3 * time.Second)
|
// time.Sleep(3 * time.Second)
|
||||||
connectionStatus.SetText("Connecting...")
|
connectionStatus.SetText(loadedLocale["connecting"])
|
||||||
connectionIcon.SetFromPaintable(clientAssets["hourglass"])
|
connectionIcon.SetFromPaintable(clientAssets["hourglass"])
|
||||||
|
|
||||||
err = cm.Run()
|
err = cm.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
connectionStatus.SetText(fmt.Sprintf("Disconnected: %s", err.Error()))
|
connectionStatus.SetText(fmt.Sprintf("%s%s", loadedLocale["disconnected"], err.Error()))
|
||||||
connectionIcon.SetFromPaintable(clientAssets["disconnect"])
|
connectionIcon.SetFromPaintable(clientAssets["disconnect"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@@ -534,17 +660,35 @@ func activate(app *gtk.Application) {
|
|||||||
the_menu := gio.NewMenu()
|
the_menu := gio.NewMenu()
|
||||||
|
|
||||||
fileMenu := gio.NewMenu()
|
fileMenu := gio.NewMenu()
|
||||||
fileMenu.Append("Join MUC", "app.join")
|
fileMenu.Append(loadedLocale["joinMUCMenu"], "app.join")
|
||||||
fileMenu.Append("Start DM", "app.dm")
|
fileMenu.Append(loadedLocale["startDMMenu"], "app.dm")
|
||||||
fileMenu.Append("Destroy MUC", "app.destroymuc")
|
fileMenu.Append(loadedLocale["destroyMUCMenu"], "app.destroymuc")
|
||||||
|
|
||||||
helpMenu := gio.NewMenu()
|
helpMenu := gio.NewMenu()
|
||||||
helpMenu.Append("About", "app.about")
|
helpMenu.Append("About", "app.about")
|
||||||
|
|
||||||
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)
|
||||||
@@ -554,24 +698,24 @@ func activate(app *gtk.Application) {
|
|||||||
cur := cur.(*chatTab)
|
cur := cur.(*chatTab)
|
||||||
if cur.isMuc {
|
if cur.isMuc {
|
||||||
win := gtk.NewWindow()
|
win := gtk.NewWindow()
|
||||||
win.SetTitle("Destroy MUC")
|
win.SetTitle(loadedLocale["destroyMUCMenu"])
|
||||||
win.SetDefaultSize(400, 1)
|
win.SetDefaultSize(400, 1)
|
||||||
win.SetResizable(false)
|
win.SetResizable(false)
|
||||||
|
|
||||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
box := gtk.NewBox(gtk.OrientationVertical, 10)
|
||||||
box.Append(gtk.NewLabel("Are you sure? This MUC will be gone forever! (a very long time)"))
|
box.Append(gtk.NewLabel(loadedLocale["destroyMUCWarningOne"]))
|
||||||
box.Append(gtk.NewLabel("If you wish to continue, type 'I understand'"))
|
box.Append(gtk.NewLabel(loadedLocale["destroyMUCWarningTwo"]))
|
||||||
cancel := gtk.NewButtonWithLabel("Cancel")
|
cancel := gtk.NewButtonWithLabel(loadedLocale["cancel"])
|
||||||
cancel.ConnectClicked(func() {
|
cancel.ConnectClicked(func() {
|
||||||
win.SetVisible(false)
|
win.SetVisible(false)
|
||||||
})
|
})
|
||||||
en := gtk.NewEntry()
|
en := gtk.NewEntry()
|
||||||
en.SetPlaceholderText("...")
|
en.SetPlaceholderText("...")
|
||||||
|
|
||||||
submit := gtk.NewButtonWithLabel("Destroy")
|
submit := gtk.NewButtonWithLabel(loadedLocale["destroyMUCActionButton"])
|
||||||
submit.ConnectClicked(func() {
|
submit.ConnectClicked(func() {
|
||||||
fmt.Println(en.Text())
|
fmt.Println(en.Text())
|
||||||
if en.Text() == "I understand" {
|
if en.Text() == loadedLocale["destroyMUCPassword"] {
|
||||||
cur, ok := tabs.Load(current)
|
cur, ok := tabs.Load(current)
|
||||||
if ok {
|
if ok {
|
||||||
cur := cur.(*chatTab)
|
cur := cur.(*chatTab)
|
||||||
@@ -583,11 +727,11 @@ func activate(app *gtk.Application) {
|
|||||||
type='set'>
|
type='set'>
|
||||||
<query xmlns='http://jabber.org/protocol/muc#owner'>
|
<query xmlns='http://jabber.org/protocol/muc#owner'>
|
||||||
<destroy jid='%s'>
|
<destroy jid='%s'>
|
||||||
<reason>User requested</reason>
|
<reason>%s</reason>
|
||||||
</destroy>
|
</destroy>
|
||||||
</query>
|
</query>
|
||||||
</iq>
|
</iq>
|
||||||
`, clientroot.Session.BindJid, current, JidMustParse(clientroot.Session.BindJid).Bare()))
|
`, clientroot.Session.BindJid, current, JidMustParse(clientroot.Session.BindJid).Bare(), loadedLocale["userRequested"]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
win.SetVisible(false)
|
win.SetVisible(false)
|
||||||
@@ -610,7 +754,7 @@ func activate(app *gtk.Application) {
|
|||||||
if mu.MucUserItem.JID != "" {
|
if mu.MucUserItem.JID != "" {
|
||||||
if JidMustParse(mu.MucUserItem.JID).Bare() == JidMustParse(clientroot.Session.BindJid).Bare() {
|
if JidMustParse(mu.MucUserItem.JID).Bare() == JidMustParse(clientroot.Session.BindJid).Bare() {
|
||||||
if mu.MucUserItem.Affiliation != "owner" {
|
if mu.MucUserItem.Affiliation != "owner" {
|
||||||
box.Append(gtk.NewLabel("You are not an owner of this MUC and thus will most likely not be able to delete it"))
|
box.Append(gtk.NewLabel(loadedLocale["destroyMUCNotOwnerWarning"]))
|
||||||
}
|
}
|
||||||
// return false
|
// return false
|
||||||
}
|
}
|
||||||
@@ -636,12 +780,14 @@ func activate(app *gtk.Application) {
|
|||||||
|
|
||||||
joinAction := gio.NewSimpleAction("join", nil)
|
joinAction := gio.NewSimpleAction("join", nil)
|
||||||
joinAction.ConnectActivate(func(p *glib.Variant) {
|
joinAction.ConnectActivate(func(p *glib.Variant) {
|
||||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
box := gtk.NewBox(gtk.OrientationVertical, 10)
|
||||||
jid_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
jid_box := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
nick_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
nick_box := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
|
disco_box := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
|
|
||||||
jid_entry := gtk.NewEntry()
|
jid_entry := gtk.NewEntry()
|
||||||
nick_entry := gtk.NewEntry()
|
nick_entry := gtk.NewEntry()
|
||||||
|
disco_check := gtk.NewCheckButton()
|
||||||
|
|
||||||
jid_entry.SetHAlign(gtk.AlignEnd)
|
jid_entry.SetHAlign(gtk.AlignEnd)
|
||||||
jid_entry.SetHExpand(true)
|
jid_entry.SetHExpand(true)
|
||||||
@@ -651,22 +797,37 @@ func activate(app *gtk.Application) {
|
|||||||
|
|
||||||
nick_entry.SetText(loadedConfig.Nick)
|
nick_entry.SetText(loadedConfig.Nick)
|
||||||
|
|
||||||
jid_box.Append(gtk.NewLabel("MUC JID:"))
|
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(create_jid)
|
||||||
jid_box.Append(jid_entry)
|
jid_box.Append(jid_entry)
|
||||||
|
|
||||||
nick_box.Append(gtk.NewLabel("Nick:"))
|
nick_box.Append(gtk.NewLabel(loadedLocale["joinMUCNickEntry"]))
|
||||||
nick_box.Append(nick_entry)
|
nick_box.Append(nick_entry)
|
||||||
|
|
||||||
|
disco_check.SetActive(true)
|
||||||
|
disco_box.Append(gtk.NewLabel(loadedLocale["joinMUCDiscoCheck"]))
|
||||||
|
disco_box.Append(disco_check)
|
||||||
|
disco_box.SetTooltipText(loadedLocale["joinMUCDiscoCheckTooltip"])
|
||||||
|
|
||||||
box.Append(jid_box)
|
box.Append(jid_box)
|
||||||
box.Append(nick_box)
|
box.Append(nick_box)
|
||||||
|
box.Append(disco_box)
|
||||||
|
|
||||||
btn := gtk.NewButtonWithLabel("Submit")
|
btn := gtk.NewButtonWithLabel(loadedLocale["submit"])
|
||||||
btn.SetVAlign(gtk.AlignBaseline)
|
btn.SetVAlign(gtk.AlignBaseline)
|
||||||
|
|
||||||
box.Append(btn)
|
box.Append(btn)
|
||||||
|
|
||||||
win := gtk.NewWindow()
|
win := gtk.NewWindow()
|
||||||
win.SetTitle("Join MUC")
|
win.SetTitle(loadedLocale["joinMUCMenu"])
|
||||||
win.SetDefaultSize(400, 1)
|
win.SetDefaultSize(400, 1)
|
||||||
win.SetResizable(false)
|
win.SetResizable(false)
|
||||||
win.SetChild(box)
|
win.SetChild(box)
|
||||||
@@ -674,14 +835,24 @@ func activate(app *gtk.Application) {
|
|||||||
btn.ConnectClicked(func() {
|
btn.ConnectClicked(func() {
|
||||||
t := jid_entry.Text()
|
t := jid_entry.Text()
|
||||||
_, ok := tabs.Load(t)
|
_, ok := tabs.Load(t)
|
||||||
if !ok {
|
jm := func(n string, pw string) {
|
||||||
err := joinMuc(client, clientroot.Session.BindJid, t, nick_entry.Text())
|
err := joinMuc(client, clientroot.Session.BindJid, t, nick_entry.Text(), pw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
showErrorDialog(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
createTab(t, true)
|
createTab(t, true, n)
|
||||||
b := gtk.NewLabel(t)
|
box := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
|
go func() {
|
||||||
|
new_im := getAvatar(t, t)
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
new_im.SetPixelSize(40)
|
||||||
|
box.Prepend(new_im)
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
b := gtk.NewLabel(n)
|
||||||
|
box.Append(b)
|
||||||
gesture1 := gtk.NewGestureClick()
|
gesture1 := gtk.NewGestureClick()
|
||||||
gesture1.SetButton(1)
|
gesture1.SetButton(1)
|
||||||
gesture1.Connect("pressed", func() {
|
gesture1.Connect("pressed", func() {
|
||||||
@@ -689,7 +860,153 @@ func activate(app *gtk.Application) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
b.AddController(gesture1)
|
b.AddController(gesture1)
|
||||||
menu.Append(b)
|
menu.Append(box)
|
||||||
|
menu.Append(gtk.NewSeparator(gtk.OrientationHorizontal))
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
|
||||||
|
if !disco_check.Active() {
|
||||||
|
jm(t, "")
|
||||||
|
win.SetVisible(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var res *stanza.DiscoInfo
|
||||||
|
allowed := true
|
||||||
|
fmt.Println("Attempting to get Disco info")
|
||||||
|
|
||||||
|
myIQ, err := stanza.NewIQ(stanza.Attrs{
|
||||||
|
Type: "get",
|
||||||
|
From: clientroot.Session.BindJid,
|
||||||
|
To: t,
|
||||||
|
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 {
|
||||||
|
features := res.Features
|
||||||
|
allowed = false
|
||||||
|
password_protected := false
|
||||||
|
password := ""
|
||||||
|
warning_win := gtk.NewWindow()
|
||||||
|
warning_win.SetTitle(fmt.Sprintf("%s%s", loadedLocale["joinPreviewTitle"], res.Identity[0].Name))
|
||||||
|
warning_win.SetDefaultSize(400, 1)
|
||||||
|
warning_win.SetResizable(false)
|
||||||
|
|
||||||
|
buttons := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
|
|
||||||
|
join_button := gtk.NewButtonWithLabel("Join")
|
||||||
|
join_button.ConnectClicked(func() {
|
||||||
|
warning_win.SetVisible(false)
|
||||||
|
if password_protected {
|
||||||
|
allowed = false
|
||||||
|
|
||||||
|
password_win := gtk.NewWindow()
|
||||||
|
password_win.SetTitle(loadedLocale["joinPasswordRequired"])
|
||||||
|
password_win.SetDefaultSize(400, 1)
|
||||||
|
password_win.SetResizable(false)
|
||||||
|
box := gtk.NewBox(gtk.OrientationVertical, 10)
|
||||||
|
en := gtk.NewPasswordEntry()
|
||||||
|
submit := gtk.NewButtonWithLabel(loadedLocale["submit"])
|
||||||
|
submit.ConnectClicked(func() {
|
||||||
|
password = en.Text()
|
||||||
|
jm(res.Identity[0].Name, password)
|
||||||
|
password_win.SetVisible(false)
|
||||||
|
})
|
||||||
|
box.Append(en)
|
||||||
|
box.Append(submit)
|
||||||
|
password_win.SetChild(box)
|
||||||
|
password_win.SetVisible(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
jm(res.Identity[0].Name, password)
|
||||||
|
})
|
||||||
|
|
||||||
|
cancel_button := gtk.NewButtonWithLabel(loadedLocale["cancel"])
|
||||||
|
cancel_button.ConnectClicked(func() {
|
||||||
|
warning_win.SetVisible(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
join_button.SetHExpand(true)
|
||||||
|
cancel_button.SetHExpand(true)
|
||||||
|
|
||||||
|
buttons.Append(join_button)
|
||||||
|
buttons.Append(cancel_button)
|
||||||
|
warning_box := gtk.NewBox(gtk.OrientationVertical, 10)
|
||||||
|
header := gtk.NewLabel(res.Identity[0].Name)
|
||||||
|
warning_box.Append(header)
|
||||||
|
addFeature := func(icon string, description string) {
|
||||||
|
box := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
|
box.Append(gtk.NewImageFromPaintable(clientAssets[icon]))
|
||||||
|
box.Append(gtk.NewLabel(description))
|
||||||
|
warning_box.Append(box)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, feature := range features {
|
||||||
|
switch feature.Var {
|
||||||
|
case "muc_passwordprotected":
|
||||||
|
password_protected = true
|
||||||
|
addFeature("muc_passwordprotected", loadedLocale["muc_passwordprotected_description"])
|
||||||
|
case "muc_unsecured":
|
||||||
|
addFeature("muc_unsecured", loadedLocale["muc_unsecured_description"])
|
||||||
|
case "muc_membersonly":
|
||||||
|
addFeature("muc_membersonly", loadedLocale["muc_membersonly_description"])
|
||||||
|
case "muc_open":
|
||||||
|
addFeature("muc_open", loadedLocale["muc_open_description"])
|
||||||
|
case "muc_moderated":
|
||||||
|
addFeature("muc_moderated", loadedLocale["muc_moderated_description"])
|
||||||
|
case "muc_unmoderated":
|
||||||
|
addFeature("muc_unmoderated", loadedLocale["muc_unmoderated_description"])
|
||||||
|
case "muc_nonanonymous":
|
||||||
|
addFeature("muc_nonanonymous", loadedLocale["muc_nonanonymous_description"])
|
||||||
|
case "muc_semianonymous":
|
||||||
|
addFeature("muc_semianonymous", loadedLocale["muc_semianonymous_description"])
|
||||||
|
case "muc_persistent":
|
||||||
|
addFeature("muc_persistent", loadedLocale["muc_persistent_description"])
|
||||||
|
case "muc_temporary":
|
||||||
|
addFeature("muc_temporary", loadedLocale["muc_temporary_description"])
|
||||||
|
case "muc_public":
|
||||||
|
addFeature("muc_public", loadedLocale["muc_public_description"])
|
||||||
|
case "muc_hidden":
|
||||||
|
addFeature("muc_hidden", loadedLocale["muc_hidden_description"])
|
||||||
|
case "urn:xmpp:mam:0":
|
||||||
|
addFeature("ok", loadedLocale["urn:xmpp:mam_description"])
|
||||||
|
case "urn:xmpp:message-moderate:0":
|
||||||
|
addFeature("moderate", loadedLocale["urn:xmpp:message-moderate_description"])
|
||||||
|
/*
|
||||||
|
default:
|
||||||
|
addFeature("comment", feature.Var)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warning_box.Append(buttons)
|
||||||
|
warning_win.SetChild(warning_box)
|
||||||
|
warning_win.Present()
|
||||||
|
} else {
|
||||||
|
allowed = false
|
||||||
|
if result.Error != nil {
|
||||||
|
showErrorDialog(fmt.Errorf("%s: %s - %s", loadedLocale["discoFail"], result.Error.Reason, result.Error.Text))
|
||||||
|
} else {
|
||||||
|
showErrorDialog(fmt.Errorf(loadedLocale["discoFail"]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowed {
|
||||||
|
jm(res.Identity[0].Name, "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
win.SetVisible(false)
|
win.SetVisible(false)
|
||||||
})
|
})
|
||||||
@@ -702,7 +1019,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)
|
||||||
@@ -731,7 +1048,7 @@ func activate(app *gtk.Application) {
|
|||||||
cBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
cBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
connectionIcon = gtk.NewImageFromPaintable((clientAssets["disconnect"]))
|
connectionIcon = gtk.NewImageFromPaintable((clientAssets["disconnect"]))
|
||||||
connectionIcon.AddCSSClass("icon")
|
connectionIcon.AddCSSClass("icon")
|
||||||
connectionStatus = gtk.NewLabel("Disconnected")
|
connectionStatus = gtk.NewLabel(loadedLocale["disconnected"])
|
||||||
|
|
||||||
cBox.Append(connectionIcon)
|
cBox.Append(connectionIcon)
|
||||||
cBox.Append(connectionStatus)
|
cBox.Append(connectionStatus)
|
||||||
@@ -758,13 +1075,13 @@ func activate(app *gtk.Application) {
|
|||||||
statBar.Append(mBox)
|
statBar.Append(mBox)
|
||||||
|
|
||||||
pBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
pBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
pBox.SetTooltipText("Ping between you and your XMPP server\nRight-click to see graph")
|
pBox.SetTooltipText(loadedLocale["pingBarTooltip"])
|
||||||
gesture := gtk.NewGestureClick()
|
gesture := gtk.NewGestureClick()
|
||||||
gesture.SetButton(3)
|
gesture.SetButton(3)
|
||||||
gesture.Connect("pressed", func() {
|
gesture.Connect("pressed", func() {
|
||||||
opt := charts.NewLineChartOptionWithData(pingTimes)
|
opt := charts.NewLineChartOptionWithData(pingTimes)
|
||||||
opt.Title = charts.TitleOption{
|
opt.Title = charts.TitleOption{
|
||||||
Text: "Server latency",
|
Text: loadedLocale["pingGraphTitle"],
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
opt.XAxis.Labels = []string{
|
opt.XAxis.Labels = []string{
|
||||||
@@ -773,7 +1090,7 @@ func activate(app *gtk.Application) {
|
|||||||
}*/
|
}*/
|
||||||
opt.Legend = charts.LegendOption{
|
opt.Legend = charts.LegendOption{
|
||||||
SeriesNames: []string{
|
SeriesNames: []string{
|
||||||
"Ping (ms)",
|
loadedLocale["pingGraphYAxis"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -800,7 +1117,7 @@ func activate(app *gtk.Application) {
|
|||||||
i := gtk.NewPictureForPaintable(gdk.NewTextureForPixbuf(loader.Pixbuf()))
|
i := gtk.NewPictureForPaintable(gdk.NewTextureForPixbuf(loader.Pixbuf()))
|
||||||
win := gtk.NewWindow()
|
win := gtk.NewWindow()
|
||||||
win.SetDefaultSize(600, 400)
|
win.SetDefaultSize(600, 400)
|
||||||
win.SetTitle("Server latency")
|
win.SetTitle(loadedLocale["pingGraphTitle"])
|
||||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
box.Append(i)
|
box.Append(i)
|
||||||
win.SetChild(box)
|
win.SetChild(box)
|
||||||
@@ -815,6 +1132,17 @@ func activate(app *gtk.Application) {
|
|||||||
pBox.Append(pingStatus)
|
pBox.Append(pingStatus)
|
||||||
statBar.Append(pBox)
|
statBar.Append(pBox)
|
||||||
|
|
||||||
|
/*
|
||||||
|
sBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
sIcon = gtk.NewImageFromPaintable(clientAssets["car"])
|
||||||
|
sIcon.AddCSSClass("icon")
|
||||||
|
sStatus = gtk.NewLabel("-")
|
||||||
|
sBox.Append(sIcon)
|
||||||
|
sBox.Append(sStatus)
|
||||||
|
sStatus.SetTooltipText(loadedLocale["throughputTooltip"])
|
||||||
|
statBar.Append(sBox)
|
||||||
|
*/
|
||||||
|
|
||||||
scrollerStatBar := gtk.NewScrolledWindow()
|
scrollerStatBar := gtk.NewScrolledWindow()
|
||||||
scrollerStatBar.SetChild(statBar)
|
scrollerStatBar.SetChild(statBar)
|
||||||
box.Append(scrollerStatBar)
|
box.Append(scrollerStatBar)
|
||||||
@@ -845,11 +1173,11 @@ func activate(app *gtk.Application) {
|
|||||||
entry_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
entry_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
oob_en := gtk.NewEntry()
|
oob_en := gtk.NewEntry()
|
||||||
oob_en.SetPlaceholderText("Embed URL")
|
oob_en.SetPlaceholderText("URL")
|
||||||
|
|
||||||
message_en = gtk.NewEntry()
|
message_en = gtk.NewEntry()
|
||||||
message_en.SetPlaceholderText("Say something, what else are you gonna do here?")
|
message_en.SetPlaceholderText(loadedLocale["messageEntryPlaceholder"])
|
||||||
b := gtk.NewButtonWithLabel("Send")
|
b := gtk.NewButtonWithLabel(loadedLocale["send"])
|
||||||
|
|
||||||
sendtxt := func() {
|
sendtxt := func() {
|
||||||
t := message_en.Text()
|
t := message_en.Text()
|
||||||
@@ -884,7 +1212,7 @@ func activate(app *gtk.Application) {
|
|||||||
end := start + len("@everyone")
|
end := start + len("@everyone")
|
||||||
|
|
||||||
new_mention := new(Mention)
|
new_mention := new(Mention)
|
||||||
new_mention.Type = "urn:xmpp:mentions:0#channel"
|
new_mention.Mentions = "urn:xmpp:mentions:0#channel"
|
||||||
|
|
||||||
new_mention.Begin = start
|
new_mention.Begin = start
|
||||||
new_mention.End = end
|
new_mention.End = end
|
||||||
@@ -897,7 +1225,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 {
|
||||||
panic(err) // TODO: Show error message via GTK
|
showErrorDialog(err)
|
||||||
}
|
}
|
||||||
message_en.SetText("")
|
message_en.SetText("")
|
||||||
scrollToBottomAfterUpdate(scroller)
|
scrollToBottomAfterUpdate(scroller)
|
||||||
@@ -915,6 +1243,9 @@ func activate(app *gtk.Application) {
|
|||||||
|
|
||||||
box.Append(entry_box)
|
box.Append(entry_box)
|
||||||
|
|
||||||
|
typingStatus = gtk.NewLabel("")
|
||||||
|
box.Append(typingStatus)
|
||||||
|
|
||||||
window.SetChild(box)
|
window.SetChild(box)
|
||||||
|
|
||||||
window.SetVisible(true)
|
window.SetVisible(true)
|
||||||
|
|||||||
@@ -61,3 +61,22 @@
|
|||||||
color: white;
|
color: white;
|
||||||
background-color: red;
|
background-color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.RedGreen_CVD {
|
||||||
|
filter: hue-rotate(30deg) saturate(120%) contrast(110%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Blue_CVD {
|
||||||
|
filter: hue-rotate(30deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.None_CVD {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.link_preview {
|
||||||
|
color: white;
|
||||||
|
background-color: grey;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/srwiley/oksvg"
|
||||||
|
"github.com/srwiley/rasterx"
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SVGToPNG(svgData []byte) ([]byte, error) {
|
||||||
|
// Parse SVG
|
||||||
|
icon, err := oksvg.ReadIconStream(bytes.NewReader(svgData))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse SVG: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := int(icon.ViewBox.W)
|
||||||
|
h := int(icon.ViewBox.H)
|
||||||
|
if w == 0 || h == 0 {
|
||||||
|
w, h = 100, 100
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rasterize into an RGBA image
|
||||||
|
rgba := image.NewRGBA(image.Rect(0, 0, w, h))
|
||||||
|
scanner := rasterx.NewScannerGV(w, h, rgba, rgba.Bounds())
|
||||||
|
dasher := rasterx.NewDasher(w, h, scanner)
|
||||||
|
|
||||||
|
icon.SetTarget(0, 0, float64(w), float64(h))
|
||||||
|
icon.Draw(dasher, 1.0)
|
||||||
|
|
||||||
|
// Encode to PNG bytes
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := png.Encode(&buf, rgba); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to encode PNG: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
type chatTab struct {
|
type chatTab struct {
|
||||||
isMuc bool
|
isMuc bool
|
||||||
msgs *gtk.ListBox
|
msgs *gtk.ListBox
|
||||||
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type lambdaConfig struct {
|
type lambdaConfig struct {
|
||||||
@@ -20,6 +21,8 @@ type lambdaConfig struct {
|
|||||||
Nick string
|
Nick string
|
||||||
JoinBookmarks bool
|
JoinBookmarks bool
|
||||||
CVD color.CVD
|
CVD color.CVD
|
||||||
|
Identicons bool
|
||||||
|
Debug bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type mucUnit struct {
|
type mucUnit struct {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var lambda_version string = "26w11a"
|
var lambda_version string = "26w17a"
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
@@ -28,17 +27,76 @@ func sendMessage(c xmpp.Sender, sendTo string, msgType stanza.StanzaType, body s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Joins a MUC
|
// Joins a MUC
|
||||||
func joinMuc(c xmpp.Sender, jid string, muc string, nick string) error {
|
func joinMuc(c xmpp.Sender, jid string, muc string, nick string, password string) error {
|
||||||
|
var joinPresence stanza.Presence
|
||||||
addr := muc + "/" + nick
|
addr := muc + "/" + nick
|
||||||
fmt.Println(addr)
|
if password == "" {
|
||||||
joinPresence := stanza.Presence{
|
joinPresence = stanza.Presence{
|
||||||
Attrs: stanza.Attrs{
|
Attrs: stanza.Attrs{
|
||||||
From: jid,
|
From: jid,
|
||||||
To: addr,
|
To: addr,
|
||||||
},
|
},
|
||||||
Extensions: []stanza.PresExtension{
|
Extensions: []stanza.PresExtension{
|
||||||
&stanza.MucPresence{},
|
&stanza.MucPresence{},
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
joinPresence = stanza.Presence{
|
||||||
|
Attrs: stanza.Attrs{
|
||||||
|
From: jid,
|
||||||
|
To: addr,
|
||||||
|
},
|
||||||
|
Extensions: []stanza.PresExtension{
|
||||||
|
&stanza.MucPresence{
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.Send(joinPresence)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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)
|
err := client.Send(joinPresence)
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinkPreview struct {
|
||||||
|
stanza.MsgExtension
|
||||||
|
XMLName xml.Name `xml:"http://www.w3.org/1999/02/22-rdf-syntax-ns# Description"`
|
||||||
|
About string `xml:"https://ogp.me/ns#,attr"`
|
||||||
|
Title string `xml:"https://ogp.me/ns# title"`
|
||||||
|
Description string `xml:"https://ogp.me/ns# description"`
|
||||||
|
Image string `xml:"https://ogp.me/ns# image"`
|
||||||
|
URL string `xml:"https://ogp.me/ns# url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", Local: "Description"}, LinkPreview{})
|
||||||
|
}
|
||||||
@@ -5,17 +5,22 @@ import (
|
|||||||
"gosrc.io/xmpp/stanza"
|
"gosrc.io/xmpp/stanza"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Experimental implementation of XEP-XXXX: Explicit Mentions
|
// Implementation of XEP-0513: Explicit Mentions
|
||||||
// https://git.isekai.rocks/snit/protoxeps/tree/explicit-mentions.xml
|
// https://xmpp.org/extensions/xep-0513.html
|
||||||
|
|
||||||
|
type NoPing struct{}
|
||||||
|
type Active struct{}
|
||||||
|
|
||||||
type Mention struct {
|
type Mention struct {
|
||||||
stanza.MsgExtension
|
stanza.MsgExtension
|
||||||
XMLName xml.Name `xml:"urn:xmpp:mentions:0 mention"`
|
XMLName xml.Name `xml:"urn:xmpp:mentions:0 mention"`
|
||||||
URI string `xml:"uri,attr,omitempty"`
|
URI string `xml:"uri,attr,omitempty"`
|
||||||
Begin int `xml:"begin,attr,omitempty"`
|
Begin int `xml:"begin,attr,omitempty"`
|
||||||
End int `xml:"end,attr,omitempty"`
|
End int `xml:"end,attr,omitempty"`
|
||||||
Type string `xml:"type,attr"`
|
Mentions string `xml:"mentions,attr"`
|
||||||
Target string `xml:"target,attr,omitempty"`
|
OccupantID string `xml:"occupantid,attr,omitempty"`
|
||||||
|
NoPing NoPing `xml:"noping,omitempty"`
|
||||||
|
Active Active `xml:"active,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -9,9 +9,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type VCard struct {
|
type VCard struct {
|
||||||
XMLName xml.Name `xml:"vcard-temp vCard"`
|
XMLName xml.Name `xml:"vcard-temp vCard"`
|
||||||
Photo Photo `xml:"PHOTO"`
|
FirstName string `xml:"FN"`
|
||||||
ResultSet *stanza.ResultSet `xml:"set,omitempty"`
|
LastName string `xml:"N>FAMILY"`
|
||||||
|
GivenName string `xml:"N>GIVEN"`
|
||||||
|
MiddleName string `xml:"N>MIDDLE"`
|
||||||
|
Nickname string `xml:"NICKNAME"`
|
||||||
|
URI string `xml:"URL"`
|
||||||
|
Birthday string `xml:"BDAY"`
|
||||||
|
OrgName string `xml:"ORG>ORGNAME"`
|
||||||
|
OrgUnit string `xml:"ORG>ORGUNIT"`
|
||||||
|
Title string `xml:"TITLE"`
|
||||||
|
Role string `xml:"ROLE"`
|
||||||
|
Description string `xml:"DESC"`
|
||||||
|
Jid string `xml:"JABBERID"`
|
||||||
|
Photo Photo `xml:"PHOTO"`
|
||||||
|
Email string `xml:"EMAIL>USERID"`
|
||||||
|
ResultSet *stanza.ResultSet `xml:"set,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VCardUpdate struct {
|
type VCardUpdate struct {
|
||||||
|
|||||||