19 Commits

Author SHA1 Message Date
542ade77b2 Modify corrected message appearance and make notification text less insane 2026-01-25 16:45:42 +00:00
183ec02918 set notification title to pi 2026-01-13 16:59:32 +00:00
881b72cb15 Add experimental stanza sender and allow sending empty messages upon confirmation 2026-01-12 16:36:05 +00:00
4d0e29550c Use better notification lib 2026-01-11 09:12:13 +00:00
b926173f43 Merge branch 'master' of https://forge.sunglocto.net/sunglocto/pi-im 2026-01-11 08:54:27 +00:00
2667b0ccfd Colored nicks 2026-01-11 08:54:13 +00:00
3aa8054dc0 Fix AUR link (oof that's embarassing, it's been like that for months) 2026-01-10 15:16:48 +00:00
ad9a0ea9a8 fix app icon 2026-01-10 14:56:28 +00:00
de40a5dec8 changes to follow 7bab52f033 2026-01-10 14:44:27 +00:00
78c7f0175d changes to follow 7bab52f033 2026-01-10 14:43:57 +00:00
7bab52f033 Remove loading gif garbage 2026-01-10 14:42:06 +00:00
652fd3ea19 sending retractions 2026-01-10 14:38:58 +00:00
536ce4cae6 indicator when user is on note to self 2026-01-10 14:02:18 +00:00
202e3ba6b1 Format code 2026-01-06 07:08:15 +00:00
7f01f38436 Bump version number 2026-01-06 05:48:42 +00:00
4afe2de35d fix some timeline glitching 2026-01-05 13:57:12 +00:00
04083a5ec4 attempt to fix presence 2026-01-05 06:47:23 +00:00
02dcd4b453 Make sure MUCs are joined with legacy history enabled 2026-01-02 13:48:14 +00:00
85b82b2d3b Make bookmarks manager show an indicator when a bookamrk does not have a name, fix capitalisation on system tray, use legacy history when joining new room and bump Fyne version to 2.7.1 2026-01-02 12:41:05 +00:00
5 changed files with 264 additions and 114 deletions

View File

@@ -112,7 +112,7 @@ go build -tags wayland .
## εγκατάσταση ## εγκατάσταση
(installation) (installation)
pi currently has no consolidated way of installing it. There is an [Arch User Repository package available](https://aur.archlinux.org/pi-im), which is maintained by [snit](https://isekai.rocks/~snit). pi currently has no consolidated way of installing it. There is an [Arch User Repository package available](https://aur.archlinux.org/packages/pi-im), which is maintained by [snit](https://isekai.rocks/~snit).
## υποστήριξη ## υποστήριξη
(support) (support)

26
go.mod
View File

@@ -3,41 +3,49 @@ module pi-im
go 1.25.1 go 1.25.1
require ( require (
fyne.io/fyne/v2 v2.6.3 fyne.io/fyne/v2 v2.7.1
fyne.io/x/fyne v0.0.0-20250910205345-ecc79984d005 fyne.io/x/fyne v0.0.0-20251214153509-fa68a7d234d5
github.com/gen2brain/beeep v0.11.2
github.com/makeworld-the-better-one/go-isemoji v1.3.0 github.com/makeworld-the-better-one/go-isemoji v1.3.0
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
github.com/shreve/musicwand v0.0.1 github.com/shreve/musicwand v0.0.1
github.com/sunglocto/oasis-sdk v0.0.0-20251128101950-083ea4658d9c github.com/sunglocto/oasis-sdk v0.0.0-20260103142308-7f1848e2aac0
mellium.im/xmpp v0.22.0 mellium.im/xmpp v0.22.0
) )
require ( require (
fyne.io/systray v1.11.0 // indirect fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fredbi/uri v1.1.0 // indirect github.com/esiqveland/notify v0.13.3 // indirect
github.com/fredbi/uri v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fyne-io/gl-js v0.2.0 // indirect github.com/fyne-io/gl-js v0.2.0 // indirect
github.com/fyne-io/glfw-js v0.3.0 // indirect github.com/fyne-io/glfw-js v0.3.0 // indirect
github.com/fyne-io/image v0.1.1 // indirect github.com/fyne-io/image v0.1.1 // indirect
github.com/fyne-io/oksvg v0.1.0 // indirect github.com/fyne-io/oksvg v0.2.0 // indirect
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-text/render v0.2.0 // indirect github.com/go-text/render v0.2.0 // indirect
github.com/go-text/typesetting v0.2.1 // indirect github.com/go-text/typesetting v0.2.1 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/hack-pad/go-indexeddb v0.3.2 // indirect github.com/hack-pad/go-indexeddb v0.3.2 // indirect
github.com/hack-pad/safejs v0.1.0 // indirect github.com/hack-pad/safejs v0.1.0 // indirect
github.com/jackmordaunt/icns/v3 v3.0.1 // indirect
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.4.1 // indirect github.com/rymdport/portal v0.4.2 // indirect
github.com/sergeymakinen/go-bmp v1.0.0 // indirect
github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
github.com/stretchr/testify v1.10.0 // indirect github.com/stretchr/testify v1.11.1 // indirect
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
github.com/yuin/goldmark v1.7.8 // indirect github.com/yuin/goldmark v1.7.8 // indirect
golang.org/x/crypto v0.36.0 // indirect golang.org/x/crypto v0.36.0 // indirect
golang.org/x/image v0.24.0 // indirect golang.org/x/image v0.24.0 // indirect

62
go.sum
View File

@@ -1,20 +1,24 @@
fyne.io/fyne/v2 v2.6.3 h1:cvtM2KHeRuH+WhtHiA63z5wJVBkQ9+Ay0UMl9PxFHyA= fyne.io/fyne/v2 v2.7.1 h1:ja7rNHWWEooha4XBIZNnPP8tVFwmTfwMJdpZmLxm2Zc=
fyne.io/fyne/v2 v2.6.3/go.mod h1:NGSurpRElVoI1G3h+ab2df3O5KLGh1CGbsMMcX0bPIs= fyne.io/fyne/v2 v2.7.1/go.mod h1:xClVlrhxl7D+LT+BWYmcrW4Nf+dJTvkhnPgji7spAwE=
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 h1:eA5/u2XRd8OUkoMqEv3IBlFYSruNlXD8bRHDiqm0VNI=
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
fyne.io/x/fyne v0.0.0-20250910205345-ecc79984d005 h1:CmdApAnt07juL0dhcFReFGpADUdRjjm0eDVJDS01uKE= fyne.io/x/fyne v0.0.0-20251214153509-fa68a7d234d5 h1:kDuBckHtdKTNV+jq0AornXroGSd/GMq06dBNXOWfmyY=
fyne.io/x/fyne v0.0.0-20250910205345-ecc79984d005/go.mod h1:kQFmF5meMIXnyCioLoCrXol5opruSS/PHYGKMBIE3SU= fyne.io/x/fyne v0.0.0-20251214153509-fa68a7d234d5/go.mod h1:kQFmF5meMIXnyCioLoCrXol5opruSS/PHYGKMBIE3SU=
git.sr.ht/~jackmordaunt/go-toast v1.1.2 h1:/yrfI55LRt1M7H1vkaw+NaH1+L1CDxrqDltwm5euVuE=
git.sr.ht/~jackmordaunt/go-toast v1.1.2/go.mod h1:jA4OqHKTQ4AFBdwrSnwnskUIIS3HYzlJSgdzCKqfavo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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=
github.com/esiqveland/notify v0.13.3 h1:QCMw6o1n+6rl+oLUfg8P1IIDSFsDEb2WlXvVvIJbI/o=
github.com/esiqveland/notify v0.13.3/go.mod h1:hesw/IRYTO0x99u1JPweAl4+5mwXJibQVUcP0Iu5ORE=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8= github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4= github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs= github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs=
@@ -23,12 +27,16 @@ github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk
github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk= github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA= github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM= github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
github.com/fyne-io/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw= github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
github.com/fyne-io/oksvg v0.1.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI= github.com/fyne-io/oksvg v0.2.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
github.com/gen2brain/beeep v0.11.2 h1:+KfiKQBbQCuhfJFPANZuJ+oxsSKAYNe88hIpJuyKWDA=
github.com/gen2brain/beeep v0.11.2/go.mod h1:jQVvuwnLuwOcdctHn/uyh8horSBNJ8uGb9Cn2W4tvoc=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA= github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc= github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU= github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8= github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
@@ -44,6 +52,8 @@ github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQb
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0= github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8= github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio= github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/jackmordaunt/icns/v3 v3.0.1 h1:xxot6aNuGrU+lNgxz5I5H0qSeCjNKp8uTXB1j8D4S3o=
github.com/jackmordaunt/icns/v3 v3.0.1/go.mod h1:5sHL59nqTd2ynTnowxB/MDQFhKNqkK8X687uKNygaSQ=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE= github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M= github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
@@ -65,8 +75,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d h1:l3+2LWCbVxn5itfvXAfH9n4YL9jh8l1g5zcncbIc1cs= 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/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d/go.mod h1:TbpErkob6SY7cyozRVSGoB3OlO2qOAgVN8O3KAJ4fMI=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA= github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
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-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3K6MFJw4GfZvQ=
github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk=
github.com/shreve/musicwand v0.0.1 h1:uF1GDmk6b9xZPAGJU3iWBSqKruyVeyVs/cqM6YzaJ3k= github.com/shreve/musicwand v0.0.1 h1:uF1GDmk6b9xZPAGJU3iWBSqKruyVeyVs/cqM6YzaJ3k=
github.com/shreve/musicwand v0.0.1/go.mod h1:y7oBkMLTfGmpIxoKQFwXlzWLtffcQIpX/1no+iNPxwQ= github.com/shreve/musicwand v0.0.1/go.mod h1:y7oBkMLTfGmpIxoKQFwXlzWLtffcQIpX/1no+iNPxwQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -75,15 +89,18 @@ github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= 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/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/sunglocto/oasis-sdk v0.0.0-20251128095406-3c003b830815 h1:HsI7kxYauygjRK15Iwf3FlZnU8XJwRPSmbtoNfeyBYk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/sunglocto/oasis-sdk v0.0.0-20251128095406-3c003b830815/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/sunglocto/oasis-sdk v0.0.0-20251128100941-f53b1dabbcac h1:pVarwnSbjNSDpeQer/SERztTEpkl6Cnb5xuyxXmb50k= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/sunglocto/oasis-sdk v0.0.0-20251128100941-f53b1dabbcac/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk= github.com/sunglocto/oasis-sdk v0.0.0-20260103142308-7f1848e2aac0 h1:7zNLaQVg8jYYrNZ6KqBv23k/RYu58p3wL9YS72B+eNM=
github.com/sunglocto/oasis-sdk v0.0.0-20251128101950-083ea4658d9c h1:peWSGIl0xBvx3sJR3+kHV1xOe1X8dbQ9xtvEXyXogCo= github.com/sunglocto/oasis-sdk v0.0.0-20260103142308-7f1848e2aac0/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
github.com/sunglocto/oasis-sdk v0.0.0-20251128101950-083ea4658d9c/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
@@ -97,6 +114,7 @@ golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=

242
main.go
View File

@@ -3,9 +3,13 @@ package main
import ( import (
//core - required //core - required
"context" "context"
_ "embed"
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt" "fmt"
"image/color"
_ "image/jpeg"
_ "image/png"
"io" "io"
"log" "log"
"math/rand/v2" "math/rand/v2"
@@ -24,21 +28,27 @@ import (
"fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
extraWidgets "fyne.io/x/fyne/widget" "fyne.io/fyne/v2/layout"
"github.com/gen2brain/beeep"
"github.com/makeworld-the-better-one/go-isemoji" "github.com/makeworld-the-better-one/go-isemoji"
"github.com/rrivera/identicon" "github.com/rrivera/identicon"
"github.com/shreve/musicwand/pkg/mpris" "github.com/shreve/musicwand/pkg/mpris"
// xmpp - required // xmpp - required
oasisSdk "github.com/sunglocto/oasis-sdk"
"mellium.im/xmpp/bookmarks" "mellium.im/xmpp/bookmarks"
xmppColor "mellium.im/xmpp/color"
"mellium.im/xmpp/jid" "mellium.im/xmpp/jid"
"mellium.im/xmpp/muc" "mellium.im/xmpp/muc"
"mellium.im/xmpp/pubsub" "mellium.im/xmpp/pubsub"
oasisSdk "github.com/sunglocto/oasis-sdk" "mellium.im/xmpp/stanza"
// TODO: integrated theme switcher // TODO: integrated theme switcher
) )
var version string = "3.1i" //go:embed pi.png
var iconBytes []byte
var version string = "3.14i"
var statBar widget.Label var statBar widget.Label
var chatInfo fyne.Container var chatInfo fyne.Container
var chatSidebar fyne.Container var chatSidebar fyne.Container
@@ -46,6 +56,7 @@ var replyNameIcon string = ">"
var replyBodyIcon string = ">" var replyBodyIcon string = ">"
var newlineIcon string = " |-> " var newlineIcon string = " |-> "
var agreesToSendingHotFuckIntoChannel bool = false var agreesToSendingHotFuckIntoChannel bool = false
var sendEmpty bool = false
var OccupantIdsToBlock = make(map[string]string) var OccupantIdsToBlock = make(map[string]string)
// by sunglocto // by sunglocto
@@ -57,6 +68,7 @@ type Message struct {
ID string ID string
ReplyID string ReplyID string
ImageURL string ImageURL string
Corrected bool
Raw oasisSdk.XMPPChatMessage Raw oasisSdk.XMPPChatMessage
Important bool Important bool
Readers []jid.JID Readers []jid.JID
@@ -160,14 +172,14 @@ func CreateUITab(chatJidStr string) ChatTabUI {
im := ii.Image(25) im := ii.Image(25)
ico := canvas.NewImageFromImage(im) ico := canvas.NewImageFromImage(im)
ico.FillMode = canvas.ImageFillOriginal ico.FillMode = canvas.ImageFillOriginal
author := widget.NewLabel("author") author := canvas.NewText("author", color.RGBA{255, 255, 255, 255})
author.TextStyle.Bold = true author.TextStyle.Bold = true
author.Selectable = true // author.Selectable = true
content := widget.NewLabel("content") content := widget.NewLabel("content")
content.Wrapping = fyne.TextWrapWord content.Wrapping = fyne.TextWrapWord
content.Selectable = true content.Selectable = true
content.Importance = widget.MediumImportance content.Importance = widget.MediumImportance
//content.SizeName = fyne.ThemeSizeName(theme.SizeNameText) content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
icon := theme.FileVideoIcon() icon := theme.FileVideoIcon()
replytext := widget.NewLabel(">fallback reply text") replytext := widget.NewLabel(">fallback reply text")
replytext.Hide() replytext.Hide()
@@ -178,10 +190,14 @@ func CreateUITab(chatJidStr string) ChatTabUI {
btn := widget.NewButtonWithIcon("View media", icon, func() { btn := widget.NewButtonWithIcon("View media", icon, func() {
}) })
btn.Hide()
reactions := container.NewHBox() reactions := container.NewHBox()
return container.NewVBox(replytext, container.NewHBox(ico, author), content, btn, reactions) pencil := widget.NewButtonWithIcon("", theme.DocumentCreateIcon(), func(){})
pencil.Hidden = true
return container.NewVBox(replytext, container.NewHBox(ico, author, layout.NewSpacer(), pencil), content, btn, reactions)
}, },
func(i widget.ListItemID, co fyne.CanvasObject) { func(i widget.ListItemID, co fyne.CanvasObject) {
@@ -197,9 +213,14 @@ func CreateUITab(chatJidStr string) ChatTabUI {
authorBox.Objects[0].(*canvas.Image).FillMode = canvas.ImageFillOriginal authorBox.Objects[0].(*canvas.Image).FillMode = canvas.ImageFillOriginal
authorBox.Objects[0].Refresh() authorBox.Objects[0].Refresh()
pencil := authorBox.Objects[3].(*widget.Button)
// Icon generate end // Icon generate end
author := authorBox.Objects[1].(*widget.Label) author := authorBox.Objects[1].(*canvas.Text)
col := xmppColor.String(chatTabs[chatJidStr].Messages[i].Author, 0, xmppColor.None)
author.Color = col
content := vbox.Objects[2].(*widget.Label) content := vbox.Objects[2].(*widget.Label)
unknown_tags := chatTabs[chatJidStr].Messages[i].Raw.Unknown unknown_tags := chatTabs[chatJidStr].Messages[i].Raw.Unknown
for _, v := range unknown_tags { for _, v := range unknown_tags {
@@ -208,8 +229,9 @@ func CreateUITab(chatJidStr string) ChatTabUI {
if attr.Name.Local == "id" { if attr.Name.Local == "id" {
reason, ok := OccupantIdsToBlock[attr.Value] reason, ok := OccupantIdsToBlock[attr.Value]
if ok { if ok {
author.SetText("Ignored user") author.Text = ("Ignored user")
content.SetText("This user is ignored due to: " + reason) content.SetText("This user is ignored due to: " + reason)
content.Importance = widget.LowImportance
return // message is from blocked user return // message is from blocked user
} }
} }
@@ -228,7 +250,20 @@ func CreateUITab(chatJidStr string) ChatTabUI {
if chatTabs[chatJidStr].Messages[i].Important { if chatTabs[chatJidStr].Messages[i].Important {
content.Importance = widget.DangerImportance content.Importance = widget.DangerImportance
} else {
content.Importance = widget.MediumImportance
} }
if chatTabs[chatJidStr].Messages[i].Corrected {
pencil.Hidden = false
pencil.OnTapped = func() {
dialog.ShowInformation("Original", *chatTabs[chatJidStr].Messages[i].Raw.CleanedBody, w)
}
} else {
pencil.Hidden = true
}
btn.Hidden = true // Hide by default btn.Hidden = true // Hide by default
msgContent := chatTabs[chatJidStr].Messages[i].Content msgContent := chatTabs[chatJidStr].Messages[i].Content
if chatTabs[chatJidStr].Messages[i].Raw.OutOfBandMedia != nil { if chatTabs[chatJidStr].Messages[i].Raw.OutOfBandMedia != nil {
@@ -288,27 +323,31 @@ func CreateUITab(chatJidStr string) ChatTabUI {
for i := len(chatTabs[chatJidStr].Messages) - 1; i >= 0; i-- { for i := len(chatTabs[chatJidStr].Messages) - 1; i >= 0; i-- {
if reply.ID == chatTabs[chatJidStr].Messages[i].Raw.StanzaID.ID { if reply.ID == chatTabs[chatJidStr].Messages[i].Raw.StanzaID.ID {
replytext.Show() replytext.Show()
replytext.SetText(fmt.Sprintf("%s %s", replyBodyIcon, strings.ReplaceAll(chatTabs[chatJidStr].Messages[i].Content, "\n", newlineIcon))) replytext.Text = fmt.Sprintf("%s %s", replyBodyIcon, strings.ReplaceAll(chatTabs[chatJidStr].Messages[i].Content, "\n", newlineIcon))
guy = chatTabs[chatJidStr].Messages[i].Author guy = chatTabs[chatJidStr].Messages[i].Author
break break
} }
} }
author.SetText(fmt.Sprintf("%s %s %s", chatTabs[chatJidStr].Messages[i].Author, replyNameIcon, guy)) author.Text = (fmt.Sprintf("%s %s %s", chatTabs[chatJidStr].Messages[i].Author, replyNameIcon, guy))
} else { } else {
author.SetText(chatTabs[chatJidStr].Messages[i].Author) author.Text = (chatTabs[chatJidStr].Messages[i].Author)
replytext.Hide() replytext.Hide()
} }
sl := strings.Split(msgContent, " ") sl := strings.Split(msgContent, " ")
content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
if len(sl) == 1 && isemoji.IsEmoji(sl[0]) { if len(sl) == 1 && isemoji.IsEmoji(sl[0]) {
content.SizeName = fyne.ThemeSizeName(theme.SizeNameHeadingText) content.SizeName = fyne.ThemeSizeName(theme.SizeNameHeadingText)
content.Refresh() content.Refresh()
} else {
content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
content.Refresh()
} }
if sl[0] == "/me" { if sl[0] == "/me" {
author.SetText(author.Text + " " + strings.Join(sl[1:], " ")) author.Text = (author.Text + " " + strings.Join(sl[1:], " "))
content.SetText(" ") content.SetText(" ")
} }
@@ -426,7 +465,9 @@ func dropToSignInPage(reason string) {
dialog.ShowError(err, w) dialog.ShowError(err, w)
return return
} }
a.SendNotification(fyne.NewNotification("Done", "Relaunch the application")) // a.SendNotification(fyne.NewNotification("Done", "Relaunch the application"))
beeep.Notify("Done", "Relaunch the application", iconBytes)
a.Quit() a.Quit()
//w.Close() //w.Close()
} }
@@ -441,9 +482,12 @@ func dropToSignInPage(reason string) {
} }
func main() { func main() {
beeep.AppName = "pi"
muc.Since(time.Now()) muc.Since(time.Now())
config = piConfig{} config = piConfig{}
res := fyne.NewStaticResource("image", iconBytes)
a = app.NewWithID("pi-im") a = app.NewWithID("pi-im")
a.SetIcon(res)
reader, err := a.Storage().Open("pi.xml") reader, err := a.Storage().Open("pi.xml")
if err != nil { if err != nil {
dropToSignInPage(err.Error()) dropToSignInPage(err.Error())
@@ -483,7 +527,8 @@ func main() {
if ok { if ok {
str := *msg.CleanedBody str := *msg.CleanedBody
if notifications { if notifications {
a.SendNotification(fyne.NewNotification(fmt.Sprintf("%s says", userJidStr), str)) // a.SendNotification(fyne.NewNotification(fmt.Sprintf("%s says", userJidStr), str))
beeep.Notify("Direct message", fmt.Sprintf("%s says:\n%s", userJidStr, str), iconBytes)
} }
for _, v := range msg.Unknown { for _, v := range msg.Unknown {
@@ -521,7 +566,9 @@ func main() {
if correction { if correction {
for i := len(tab.Messages) - 1; i > 0; i-- { for i := len(tab.Messages) - 1; i > 0; i-- {
if tab.Messages[i].Raw.From.String() == msg.From.String() { if tab.Messages[i].Raw.From.String() == msg.From.String() {
tab.Messages[i].Content = *msg.CleanedBody + " (corrected)" // tab.Messages[i].Content = *msg.CleanedBody + " (corrected)"
tab.Messages[i].Content = *msg.CleanedBody
tab.Messages[i].Corrected = true
fyne.Do(func() { fyne.Do(func() {
UITabs[userJidStr].Scroller.Refresh() UITabs[userJidStr].Scroller.Refresh()
}) })
@@ -589,7 +636,8 @@ func main() {
} }
if !donotnotify && !ignore && notifications { if !donotnotify && !ignore && notifications {
if !correction && msg.From.String() != client.JID.String() && strings.Contains(str, login.DisplayName) || (msg.Reply != nil && strings.Contains(msg.Reply.To, login.DisplayName)) { if !correction && msg.From.String() != client.JID.String() && strings.Contains(str, login.DisplayName) || (msg.Reply != nil && strings.Contains(msg.Reply.To, login.DisplayName)) {
a.SendNotification(fyne.NewNotification(fmt.Sprintf("Mentioned in %s", mucJidStr), str)) // a.SendNotification(fyne.NewNotification(fmt.Sprintf("Mentioned in %s", mucJidStr), str))
beeep.Notify(fmt.Sprintf("Mentioned in %s by %s", jid.MustParse(mucJidStr).Localpart(), msg.From.Resourcepart()), str, iconBytes)
} }
} }
if strings.Contains(str, "https://") { if strings.Contains(str, "https://") {
@@ -630,7 +678,9 @@ func main() {
if correction { if correction {
for i := len(tab.Messages) - 1; i > 0; i-- { for i := len(tab.Messages) - 1; i > 0; i-- {
if tab.Messages[i].Raw.From.String() == msg.From.String() { if tab.Messages[i].Raw.From.String() == msg.From.String() {
tab.Messages[i].Content = *msg.CleanedBody + " (corrected)" // tab.Messages[i].Content = *msg.CleanedBody + " (corrected)"
tab.Messages[i].Content = *msg.CleanedBody
tab.Messages[i].Corrected = true
fyne.Do(func() { fyne.Do(func() {
UITabs[mucJidStr].Scroller.Refresh() UITabs[mucJidStr].Scroller.Refresh()
}) })
@@ -654,6 +704,10 @@ func main() {
} }
important = false important = false
fyne.Do(func() { fyne.Do(func() {
_, ok := UITabs[mucJidStr]
if !ok {
return
}
UITabs[mucJidStr].Scroller.Refresh() UITabs[mucJidStr].Scroller.Refresh()
if scrollDownOnNewMessage { if scrollDownOnNewMessage {
tab, ok := UITabs[mucJidStr] tab, ok := UITabs[mucJidStr]
@@ -692,7 +746,7 @@ func main() {
}) })
client.SetPresenceHandler(func(client *oasisSdk.XmppClient, from jid.JID, p oasisSdk.UserPresence) { client.SetPresenceHandler(func(client *oasisSdk.XmppClient, from jid.JID, p oasisSdk.UserPresence) {
log.Println(p.Type) fmt.Println(p.Header.Type == stanza.UnavailablePresence)
bareAcc := from.Bare() bareAcc := from.Bare()
tab, ok := chatTabs[bareAcc.String()] tab, ok := chatTabs[bareAcc.String()]
if !ok { if !ok {
@@ -702,6 +756,7 @@ func main() {
} }
if tab.isMuc { if tab.isMuc {
fmt.Println(p.Type)
tab.Members[from.String()] = p tab.Members[from.String()] = p
} }
}) })
@@ -775,7 +830,7 @@ func main() {
w = a.NewWindow("pi") w = a.NewWindow("pi")
w.SetCloseIntercept(func() { w.SetCloseIntercept(func() {
w.RequestFocus() w.RequestFocus()
dialog.ShowConfirm("Close pi", "You hit the close button. Do you want Pi to close completely (confirm) or minimize to the tray? (cancel)", func(b bool) { dialog.ShowConfirm("Close pi", "You hit the close button. Do you want Pi to close completely?", func(b bool) {
if b { if b {
w.Close() w.Close()
a.Quit() a.Quit()
@@ -786,6 +841,7 @@ func main() {
}, w) }, w)
}) })
w.Resize(fyne.NewSize(500, 500)) w.Resize(fyne.NewSize(500, 500))
w.SetIcon(res)
entry := NewCustomMultiLineEntry() entry := NewCustomMultiLineEntry()
entry.SetPlaceHolder("Say something, you know you want to.\nCtrl+Enter for newline") entry.SetPlaceHolder("Say something, you know you want to.\nCtrl+Enter for newline")
@@ -794,7 +850,13 @@ func main() {
SendCallback := func() { SendCallback := func() {
text := entry.Text text := entry.Text
if AppTabs.Selected() == nil || AppTabs.Selected().Content == nil || text == "" { if text == "" {
dialog.ShowConfirm("Empty message", "Send an empty message?", func(b bool) {
sendEmpty = b
}, w)
}
if AppTabs.Selected() == nil || AppTabs.Selected().Content == nil || (text == "" && !sendEmpty) {
return return
} }
@@ -1047,6 +1109,34 @@ func main() {
}, w) }, w)
}) })
getMAM := fyne.NewMenuItem("send stanza", func() {
sendiq := false
b := container.NewVBox()
note := widget.NewRichTextFromMarkdown(fmt.Sprintf("# How to send a stanza\nThe stanza must be formed properly or the client will disconnect! Some actions such as joining rooms should be done via the client.\n Here is an example of a typical stanza:\n\n```\n<message from=abc@example.com to=xyz@example.com type=chat>\n\t<body>We have had a most delightful evening, a most excellent ball.</body>\n</message>\n```\n\nThe `from` attribute should be your JID `(%s)` and the `to` attribute should be the intended recipient. The `type` attribute can be `chat`, `groupchat`, `headline` and more.\n\nFor more information, read [RFC 6120](https://www.rfc-editor.org/rfc/rfc6120.html).", client.JID.String()))
note.Hidden = true
// note.Wrapping = fyne.TextWrapWord
b.Add(note)
isiq := widget.NewCheck("IQ stanza (get a reponse back)", func(b bool) {sendiq=b})
b.Add(isiq)
hlpbtn := widget.NewButton("Help!", func() {note.Hidden = !note.Hidden})
b.Add(hlpbtn)
entry := widget.NewMultiLineEntry()
entry.SetPlaceHolder(fmt.Sprintf("<message type='chat' from='%s' id='ilovepi' to='friend@home.org'><body>Hello from Pi!</body></message>", client.JID.String()))
b.Add(entry)
btn := widget.NewButton("Send", func() {
fmt.Println(sendiq)
msg := entry.Text
err := client.Session.Send(context.TODO(), xml.NewDecoder(strings.NewReader(msg)))
if err != nil {
dialog.ShowError(err, w)
return
}
})
b.Add(btn)
dialog.ShowCustom("Send a custom XML stanza", "Close", b, w)
})
leaveRoom := fyne.NewMenuItem("disconnect from current room", func() { leaveRoom := fyne.NewMenuItem("disconnect from current room", func() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List) selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok { if !ok {
@@ -1082,7 +1172,13 @@ func main() {
for address, bookmark := range bookmarks { for address, bookmark := range bookmarks {
bookmarkWidget := container.NewGridWithColumns(7) bookmarkWidget := container.NewGridWithColumns(7)
nameLabel := widget.NewLabel(bookmark.Name) var nameLabel *widget.RichText
if bookmark.Name != "" {
nameLabel = widget.NewRichTextFromMarkdown(bookmark.Name)
} else {
nameLabel = widget.NewRichTextFromMarkdown("_no name_")
}
nameLabel.Wrapping = fyne.TextWrapBreak nameLabel.Wrapping = fyne.TextWrapBreak
bookmarkWidget.Add(nameLabel) bookmarkWidget.Add(nameLabel)
@@ -1102,9 +1198,8 @@ func main() {
if bookmark.Nick == "" { if bookmark.Nick == "" {
bookmark.Nick = client.Login.DisplayName bookmark.Nick = client.Login.DisplayName
} }
var zero uint64 = uint64(0)
addChatTab(true, bookmark.JID, bookmark.Nick) addChatTab(true, bookmark.JID, bookmark.Nick)
_, err := client.ConnectMuc(bookmark, oasisSdk.MucLegacyHistoryConfig{MaxCount: &zero}, context.TODO()) _, err := client.ConnectMuc(bookmark, oasisSdk.MucLegacyHistoryConfig{}, context.TODO())
if err != nil { if err != nil {
fyne.Do(func() { fyne.Do(func() {
joinRoomButton.Enable() joinRoomButton.Enable()
@@ -1190,14 +1285,7 @@ func main() {
joinARoom := fyne.NewMenuItem("connect to a room", func() { joinARoom := fyne.NewMenuItem("connect to a room", func() {
dialog.ShowEntryDialog("connect to a room", "JID:", func(s string) { dialog.ShowEntryDialog("connect to a room", "JID:", func(s string) {
i := resourcePiloadingGif d := dialog.NewCustom("Please wait", "Close", widget.NewProgressBarInfinite(), w)
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
if err != nil {
panic(err)
}
gif.Start()
gif.Show()
d := dialog.NewCustom("Please wait", "Close", gif, w)
d.Show() d.Show()
go func() { go func() {
myjid, err := jid.Parse(s) myjid, err := jid.Parse(s)
@@ -1212,8 +1300,7 @@ func main() {
mychannel.Nick = login.DisplayName mychannel.Nick = login.DisplayName
//ch, err := client.MucClient.Join(client.Ctx, joinjid, client.Session) //ch, err := client.MucClient.Join(client.Ctx, joinjid, client.Session)
addChatTab(true, myjid, login.DisplayName) addChatTab(true, myjid, login.DisplayName)
num := uint64(0) _, err = client.ConnectMuc(*mychannel, oasisSdk.MucLegacyHistoryConfig{}, context.TODO())
_, err = client.ConnectMuc(*mychannel, oasisSdk.MucLegacyHistoryConfig{MaxCount: &num}, context.TODO())
if err != nil { if err != nil {
d.Hide() d.Hide()
dialog.ShowError(err, w) dialog.ShowError(err, w)
@@ -1225,16 +1312,9 @@ func main() {
}, w) }, w)
}) })
beginADM := fyne.NewMenuItem("Start a DM", func() { beginADM := fyne.NewMenuItem("start a DM", func() {
dialog.ShowEntryDialog("Start a DM", "JID:", func(s string) { dialog.ShowEntryDialog("Start a DM", "JID:", func(s string) {
i := resourcePiloadingGif d := dialog.NewCustom("Please wait", "Close", widget.NewLabel("Opening a DM with "+s), w)
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
if err != nil {
panic(err)
}
gif.Start()
gif.Show()
d := dialog.NewCustom("Please wait", "Close", gif, w)
d.Show() d.Show()
go func() { go func() {
myjid, err := jid.Parse(s) myjid, err := jid.Parse(s)
@@ -1252,7 +1332,7 @@ func main() {
menu_help := fyne.NewMenu("π", mit, reconnect, licensesbtn) menu_help := fyne.NewMenu("π", mit, reconnect, licensesbtn)
menu_changeroom := fyne.NewMenu("Α", mic, beginADM, joinARoom, leaveRoom, manageBookmarks) menu_changeroom := fyne.NewMenu("Α", mic, beginADM, joinARoom, leaveRoom, manageBookmarks, getMAM)
menu_configureview := fyne.NewMenu("Β", mia, jtt, jtb) menu_configureview := fyne.NewMenu("Β", mia, jtt, jtb)
hafjag := fyne.NewMenuItem("Hafjag", func() { hafjag := fyne.NewMenuItem("Hafjag", func() {
entry.Text = "Hafjag" entry.Text = "Hafjag"
@@ -1299,6 +1379,13 @@ func main() {
entry.Text = old entry.Text = old
}) })
wspeed := fyne.NewMenuItem("W Speed ❤️‍🩹❤️‍🩹", func() {
old := entry.Text
entry.Text = "W Speed ❤️‍🩹❤️‍🩹"
SendCallback()
entry.Text = old
})
exposed_suffix := fyne.NewMenuItem(".exposed", func() { exposed_suffix := fyne.NewMenuItem(".exposed", func() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List) selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok { if !ok {
@@ -1375,7 +1462,7 @@ func main() {
} }
}) })
menu_jokes := fyne.NewMenu("Δ", mycurrenttime, mycurrentplayingsong, hafjag, hotfuck, agree, kai, mipipiemi, exposed_suffix) menu_jokes := fyne.NewMenu("Δ", mycurrenttime, mycurrentplayingsong, hafjag, hotfuck, agree, kai, mipipiemi, exposed_suffix, wspeed)
bit := fyne.NewMenuItem("mark selected message as read", func() { bit := fyne.NewMenuItem("mark selected message as read", func() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List) selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok { if !ok {
@@ -1393,6 +1480,36 @@ func main() {
client.MarkAsRead(&m) client.MarkAsRead(&m)
}) })
rec := fyne.NewMenuItem("retract selected message", func() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
return
}
var activeMucJid string
for jid, tabData := range UITabs {
if tabData.Scroller == selectedScroller {
activeMucJid = jid
break
}
}
if !chatTabs[activeMucJid].isMuc {
return // TODO: For 1:1 DMs we use OccupantID for the ID and send message type 'chat'
}
id := chatTabs[activeMucJid].Messages[selectedId].Raw.StanzaID
if id == nil {
return
}
msg := fmt.Sprintf("<message type='groupchat' to='%s' id='retract-message-1'><retract id='%s' xmlns='urn:xmpp:message-retract:1'/><fallback xmlns='urn:xmpp:fallback:0' for='urn:xmpp:message-retract:1' /><body>This user retracted a previous message, but it's unsupported by your client.</body><store xmlns='urn:xmpp:hints'/></message>", activeMucJid, id.ID)
fmt.Println(msg)
err := client.Session.Send(context.TODO(), xml.NewDecoder(strings.NewReader(msg)))
if err != nil {
dialog.ShowError(err, w)
}
})
bia := fyne.NewMenuItem("toggle replying to message", func() { bia := fyne.NewMenuItem("toggle replying to message", func() {
replying = !replying replying = !replying
}) })
@@ -1502,13 +1619,13 @@ func main() {
dialog.ShowCustom("Message", "Close", pre, w) dialog.ShowCustom("Message", "Close", pre, w)
}) })
menu_messageoptions := fyne.NewMenu("Γ", bit, bia, bic, red, blck) menu_messageoptions := fyne.NewMenu("Γ", bit, bia, bic, rec, red, blck)
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions, menu_jokes) ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions, menu_jokes)
w.SetMainMenu(ma) w.SetMainMenu(ma)
desk, ok := a.(desktop.App) desk, ok := a.(desktop.App)
if ok { if ok {
desk.SetSystemTrayMenu(fyne.NewMenu("", fyne.NewMenuItem("show", w.Show))) desk.SetSystemTrayMenu(fyne.NewMenu("", fyne.NewMenuItem("Show window", w.Show)))
} }
AppTabs = container.NewDocTabs( AppTabs = container.NewDocTabs(
@@ -1542,10 +1659,11 @@ func main() {
if err != nil { if err != nil {
dialog.ShowError(err, w) dialog.ShowError(err, w)
} }
fyne.Do(func() {
AppTabs.Selected().Text = fmt.Sprintf("%s (disconnected)", AppTabs.Selected().Text) AppTabs.Selected().Text = fmt.Sprintf("%s (disconnected)", AppTabs.Selected().Text)
AppTabs.Remove(ti) AppTabs.Remove(ti)
delete(UITabs, activeChatJid) delete(UITabs, activeChatJid)
})
}() }()
} }
@@ -1557,7 +1675,18 @@ func main() {
} }
} }
AppTabs.OnUnselected = func(ti *container.TabItem) {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
return
}
selectedScroller.Hidden = true
}
var MainSplit *container.Split
AppTabs.OnSelected = func(ti *container.TabItem) { AppTabs.OnSelected = func(ti *container.TabItem) {
chatSidebar.Resize(chatSidebar.Size())
if AppTabs.Selected() == AppTabs.Items[0] { if AppTabs.Selected() == AppTabs.Items[0] {
chatSidebar.Hidden = true chatSidebar.Hidden = true
return return
@@ -1567,6 +1696,8 @@ func main() {
return return
} }
selectedScroller.Hidden = false
var activeChatJid string var activeChatJid string
for jid, tabData := range UITabs { for jid, tabData := range UITabs {
if tabData.Scroller == selectedScroller { if tabData.Scroller == selectedScroller {
@@ -1584,8 +1715,8 @@ func main() {
nameLabel.Truncation = fyne.TextTruncateEllipsis nameLabel.Truncation = fyne.TextTruncateEllipsis
memberLabel := widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members))) memberLabel := widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members)))
memberLabel.Truncation = fyne.TextTruncateEllipsis memberLabel.Truncation = fyne.TextTruncateEllipsis
box := container.NewVBox(nameLabel, ) box := container.NewVBox(nameLabel)
chatSidebar.Objects = []fyne.CanvasObject{} // chatSidebar.Objects = []fyne.CanvasObject{}
for name, p := range tab.Members { for name, p := range tab.Members {
log.Println(string(p.Type)) log.Println(string(p.Type))
@@ -1627,23 +1758,28 @@ func main() {
} }
s.Truncation = fyne.TextTruncateEllipsis s.Truncation = fyne.TextTruncateEllipsis
box.Add(s) box.Add(s)
box.Refresh()
} }
} }
} }
chatSidebar = *container.NewGridWithColumns(1, container.NewVScroll(box)) chatSidebar = *container.NewGridWithColumns(1, container.NewVScroll(box))
} else {
if activeChatJid == config.Login.User {
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# "+activeChatJid), widget.NewLabel("Hey, that's you!"))
} else { } else {
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid)) chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid))
} }
}
chatSidebar.Hidden = false chatSidebar.Hidden = false
chatSidebar.Refresh() chatSidebar.Show()
MainSplit.Resize(MainSplit.Size())
} }
chatSidebar.Hidden = false chatSidebar.Hidden = false
statBar.SetText("") statBar.SetText("")
chatInfo = *container.NewHBox(widget.NewLabel("")) chatInfo = *container.NewHBox(widget.NewLabel(""))
MainSplit := container.NewHSplit(AppTabs, &chatSidebar) MainSplit = container.NewHSplit(AppTabs, &chatSidebar)
DownSplit := container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn)) DownSplit := container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn))
BigSplit := container.NewVSplit(MainSplit, DownSplit) BigSplit := container.NewVSplit(MainSplit, DownSplit)

File diff suppressed because one or more lines are too long