19 Commits
Author SHA1 Message Date
Sunglocto 23288e3dfa minor spelling mistake (world implodes) 2026-08-23 20:36:42 +01:00
Sunglocto 6d95c8419c add hiatus note 2026-08-23 20:35:28 +01:00
Sunglocto a31d9e766d Add april fools joke 2026-03-31 07:39:06 +01:00
Sunglocto 542ade77b2 Modify corrected message appearance and make notification text less insane 2026-01-25 16:45:42 +00:00
Sunglocto 183ec02918 set notification title to pi 2026-01-13 16:59:32 +00:00
Sunglocto 881b72cb15 Add experimental stanza sender and allow sending empty messages upon confirmation 2026-01-12 16:36:05 +00:00
Sunglocto 4d0e29550c Use better notification lib 2026-01-11 09:12:13 +00:00
Sunglocto b926173f43 Merge branch 'master' of https://forge.sunglocto.net/sunglocto/pi-im 2026-01-11 08:54:27 +00:00
Sunglocto 2667b0ccfd Colored nicks 2026-01-11 08:54:13 +00:00
Sunglocto 3aa8054dc0 Fix AUR link (oof that's embarassing, it's been like that for months) 2026-01-10 15:16:48 +00:00
Sunglocto ad9a0ea9a8 fix app icon 2026-01-10 14:56:28 +00:00
Sunglocto de40a5dec8 changes to follow 7bab52f033 2026-01-10 14:44:27 +00:00
Sunglocto 78c7f0175d changes to follow 7bab52f033 2026-01-10 14:43:57 +00:00
Sunglocto 7bab52f033 Remove loading gif garbage 2026-01-10 14:42:06 +00:00
Sunglocto 652fd3ea19 sending retractions 2026-01-10 14:38:58 +00:00
Sunglocto 536ce4cae6 indicator when user is on note to self 2026-01-10 14:02:18 +00:00
Sunglocto 202e3ba6b1 Format code 2026-01-06 07:08:15 +00:00
Sunglocto 7f01f38436 Bump version number 2026-01-06 05:48:42 +00:00
Sunglocto 4afe2de35d fix some timeline glitching 2026-01-05 13:57:12 +00:00
5 changed files with 235 additions and 95 deletions
+9 -1
View File
@@ -1,3 +1,11 @@
# Notice
Pi is on hiatus for the time being. The reason why being that the libraries that Pi uses, being the Oasis SDK and the Mellium libraries, have multiple fundamental issues and bugs that are unlikely to be fixed. In addition, I began writing this project with a much less consolidated knowledge of Go and programming in general, meaning there are core architectural issues with this project. Regardless, you are still free to use Pi, and I may even rewrite the project to add some new features. Who knows.
I mostly work on [Lambda](https://forge.sunglocto.net/sunglocto/lambda) these days, which has already surpassed Pi in terms of features and usability.
<hr/>
<center>
<img width="100" height="100" src="https://forge.sunglocto.net/sunglocto/pi-im/raw/branch/master/pi.png">
</center>
@@ -112,7 +120,7 @@ go build -tags wayland .
## εγκατάσταση
(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)
+9 -1
View File
@@ -4,7 +4,8 @@ go 1.25.1
require (
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/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
github.com/shreve/musicwand v0.0.1
@@ -14,8 +15,10 @@ require (
require (
fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 // 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/esiqveland/notify v0.13.3 // indirect
github.com/fredbi/uri v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fyne-io/gl-js v0.2.0 // indirect
@@ -24,20 +27,25 @@ require (
github.com/fyne-io/oksvg v0.2.0 // 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-ole/go-ole v1.3.0 // indirect
github.com/go-text/render v0.2.0 // indirect
github.com/go-text/typesetting v0.2.1 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/hack-pad/go-indexeddb v0.3.2 // 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/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // 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/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // 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
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/image v0.24.0 // indirect
+24 -16
View File
@@ -2,8 +2,10 @@ fyne.io/fyne/v2 v2.7.1 h1:ja7rNHWWEooha4XBIZNnPP8tVFwmTfwMJdpZmLxm2Zc=
fyne.io/fyne/v2 v2.7.1/go.mod h1:xClVlrhxl7D+LT+BWYmcrW4Nf+dJTvkhnPgji7spAwE=
fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 h1:eA5/u2XRd8OUkoMqEv3IBlFYSruNlXD8bRHDiqm0VNI=
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-20250910205345-ecc79984d005/go.mod h1:kQFmF5meMIXnyCioLoCrXol5opruSS/PHYGKMBIE3SU=
fyne.io/x/fyne v0.0.0-20251214153509-fa68a7d234d5 h1:kDuBckHtdKTNV+jq0AornXroGSd/GMq06dBNXOWfmyY=
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 v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@@ -11,6 +13,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
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/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/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
@@ -25,10 +29,14 @@ 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/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
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/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/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/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
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/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
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/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
@@ -67,6 +77,10 @@ github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d/go.mod h1:TbpErk
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
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/go.mod h1:y7oBkMLTfGmpIxoKQFwXlzWLtffcQIpX/1no+iNPxwQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -75,25 +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/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/sunglocto/oasis-sdk v0.0.0-20251128101950-083ea4658d9c h1:peWSGIl0xBvx3sJR3+kHV1xOe1X8dbQ9xtvEXyXogCo=
github.com/sunglocto/oasis-sdk v0.0.0-20251128101950-083ea4658d9c/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
github.com/sunglocto/oasis-sdk v0.0.0-20260103140045-0405de0dd080 h1:29bQpS8K/agBsO2fkXXyId3aDBweVIA+USUNmQpwdac=
github.com/sunglocto/oasis-sdk v0.0.0-20260103140045-0405de0dd080/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
github.com/sunglocto/oasis-sdk v0.0.0-20260103140638-616fcb543bcf h1:yC8TNIiaJtxzP2hIABRe7EdhcSAODUVkjzsici5Fohw=
github.com/sunglocto/oasis-sdk v0.0.0-20260103140638-616fcb543bcf/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
github.com/sunglocto/oasis-sdk v0.0.0-20260103140748-3a5af37a9707 h1:r7CKzHIBlp2Cq28WrCe2VaKdZCD4dCd1BmxXLno7Mcg=
github.com/sunglocto/oasis-sdk v0.0.0-20260103140748-3a5af37a9707/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
github.com/sunglocto/oasis-sdk v0.0.0-20260103140915-72af6e4c3604 h1:yD8c7E3FECQ45Amrn4FnxBw30XbBgfQ+exgbA5IisEU=
github.com/sunglocto/oasis-sdk v0.0.0-20260103140915-72af6e4c3604/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
github.com/sunglocto/oasis-sdk v0.0.0-20260103142016-f599b73f44e3 h1:S1miV9gK4GKqr+RdmqW71JnpYhoFZehe9NkABbM2XW4=
github.com/sunglocto/oasis-sdk v0.0.0-20260103142016-f599b73f44e3/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
github.com/sunglocto/oasis-sdk v0.0.0-20260103142127-cc8ce586b6dc h1:7KKRazjvG5I1l+hyvTvM8XLIWewGQ2PAaKr4sN1Oi7E=
github.com/sunglocto/oasis-sdk v0.0.0-20260103142127-cc8ce586b6dc/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-20260103142308-7f1848e2aac0/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/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
@@ -107,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/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
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/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+193 -65
View File
@@ -3,9 +3,13 @@ package main
import (
//core - required
"context"
_ "embed"
"encoding/xml"
"errors"
"fmt"
"image/color"
_ "image/jpeg"
_ "image/png"
"io"
"log"
"math/rand/v2"
@@ -13,9 +17,6 @@ import (
"os"
"strings"
"time"
_ "embed"
_ "image/png"
_ "image/jpeg"
// gui - required
"fyne.io/fyne/v2"
@@ -27,25 +28,27 @@ import (
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"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/rrivera/identicon"
"github.com/shreve/musicwand/pkg/mpris"
// xmpp - required
oasisSdk "github.com/sunglocto/oasis-sdk"
"mellium.im/xmpp/bookmarks"
xmppColor "mellium.im/xmpp/color"
"mellium.im/xmpp/jid"
"mellium.im/xmpp/muc"
"mellium.im/xmpp/stanza"
"mellium.im/xmpp/pubsub"
oasisSdk "github.com/sunglocto/oasis-sdk"
"mellium.im/xmpp/stanza"
// TODO: integrated theme switcher
)
//go:embed pi.png
var iconBytes []byte
var version string = "3.1i"
var version string = "3.14i"
var statBar widget.Label
var chatInfo fyne.Container
var chatSidebar fyne.Container
@@ -53,6 +56,7 @@ var replyNameIcon string = ">"
var replyBodyIcon string = ">"
var newlineIcon string = " |-> "
var agreesToSendingHotFuckIntoChannel bool = false
var sendEmpty bool = false
var OccupantIdsToBlock = make(map[string]string)
// by sunglocto
@@ -64,6 +68,7 @@ type Message struct {
ID string
ReplyID string
ImageURL string
Corrected bool
Raw oasisSdk.XMPPChatMessage
Important bool
Readers []jid.JID
@@ -167,14 +172,14 @@ func CreateUITab(chatJidStr string) ChatTabUI {
im := ii.Image(25)
ico := canvas.NewImageFromImage(im)
ico.FillMode = canvas.ImageFillOriginal
author := widget.NewLabel("author")
author := canvas.NewText("author", color.RGBA{255, 255, 255, 255})
author.TextStyle.Bold = true
author.Selectable = true
// author.Selectable = true
content := widget.NewLabel("content")
content.Wrapping = fyne.TextWrapWord
content.Selectable = true
content.Importance = widget.MediumImportance
//content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
icon := theme.FileVideoIcon()
replytext := widget.NewLabel(">fallback reply text")
replytext.Hide()
@@ -189,7 +194,10 @@ func CreateUITab(chatJidStr string) ChatTabUI {
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) {
@@ -205,9 +213,14 @@ func CreateUITab(chatJidStr string) ChatTabUI {
authorBox.Objects[0].(*canvas.Image).FillMode = canvas.ImageFillOriginal
authorBox.Objects[0].Refresh()
pencil := authorBox.Objects[3].(*widget.Button)
// 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)
unknown_tags := chatTabs[chatJidStr].Messages[i].Raw.Unknown
for _, v := range unknown_tags {
@@ -216,8 +229,9 @@ func CreateUITab(chatJidStr string) ChatTabUI {
if attr.Name.Local == "id" {
reason, ok := OccupantIdsToBlock[attr.Value]
if ok {
author.SetText("Ignored user")
author.Text = ("Ignored user")
content.SetText("This user is ignored due to: " + reason)
content.Importance = widget.LowImportance
return // message is from blocked user
}
}
@@ -236,7 +250,20 @@ func CreateUITab(chatJidStr string) ChatTabUI {
if chatTabs[chatJidStr].Messages[i].Important {
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
msgContent := chatTabs[chatJidStr].Messages[i].Content
if chatTabs[chatJidStr].Messages[i].Raw.OutOfBandMedia != nil {
@@ -296,27 +323,31 @@ func CreateUITab(chatJidStr string) ChatTabUI {
for i := len(chatTabs[chatJidStr].Messages) - 1; i >= 0; i-- {
if reply.ID == chatTabs[chatJidStr].Messages[i].Raw.StanzaID.ID {
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
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 {
author.SetText(chatTabs[chatJidStr].Messages[i].Author)
author.Text = (chatTabs[chatJidStr].Messages[i].Author)
replytext.Hide()
}
sl := strings.Split(msgContent, " ")
content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
if len(sl) == 1 && isemoji.IsEmoji(sl[0]) {
content.SizeName = fyne.ThemeSizeName(theme.SizeNameHeadingText)
content.Refresh()
} else {
content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
content.Refresh()
}
if sl[0] == "/me" {
author.SetText(author.Text + " " + strings.Join(sl[1:], " "))
author.Text = (author.Text + " " + strings.Join(sl[1:], " "))
content.SetText(" ")
}
@@ -434,7 +465,9 @@ func dropToSignInPage(reason string) {
dialog.ShowError(err, w)
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()
//w.Close()
}
@@ -449,6 +482,7 @@ func dropToSignInPage(reason string) {
}
func main() {
beeep.AppName = "pi"
muc.Since(time.Now())
config = piConfig{}
res := fyne.NewStaticResource("image", iconBytes)
@@ -493,7 +527,8 @@ func main() {
if ok {
str := *msg.CleanedBody
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 {
@@ -531,7 +566,9 @@ func main() {
if correction {
for i := len(tab.Messages) - 1; i > 0; i-- {
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() {
UITabs[userJidStr].Scroller.Refresh()
})
@@ -599,7 +636,8 @@ func main() {
}
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)) {
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://") {
@@ -640,7 +678,9 @@ func main() {
if correction {
for i := len(tab.Messages) - 1; i > 0; i-- {
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() {
UITabs[mucJidStr].Scroller.Refresh()
})
@@ -664,6 +704,10 @@ func main() {
}
important = false
fyne.Do(func() {
_, ok := UITabs[mucJidStr]
if !ok {
return
}
UITabs[mucJidStr].Scroller.Refresh()
if scrollDownOnNewMessage {
tab, ok := UITabs[mucJidStr]
@@ -797,6 +841,7 @@ func main() {
}, w)
})
w.Resize(fyne.NewSize(500, 500))
w.SetIcon(res)
entry := NewCustomMultiLineEntry()
entry.SetPlaceHolder("Say something, you know you want to.\nCtrl+Enter for newline")
@@ -805,7 +850,13 @@ func main() {
SendCallback := func() {
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
}
@@ -1058,6 +1109,34 @@ func main() {
}, 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() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
@@ -1170,19 +1249,19 @@ func main() {
setNick = widget.NewButton("Set nick", func() {
go func() {
fyne.Do(func() {setNick.Disable()})
newNick := nickEntry.Text
bookmark.Nick = newNick
err := client.PublishBookmark(bookmark, context.TODO())
if err != nil {
fyne.Do(func() {
setNick.Enable()
dialog.ShowError(err, w)
})
return
}
client.RefreshBookmarks(false)
fyne.Do(func() {setNick.Enable()})
fyne.Do(func() { setNick.Disable() })
newNick := nickEntry.Text
bookmark.Nick = newNick
err := client.PublishBookmark(bookmark, context.TODO())
if err != nil {
fyne.Do(func() {
setNick.Enable()
dialog.ShowError(err, w)
})
return
}
client.RefreshBookmarks(false)
fyne.Do(func() { setNick.Enable() })
}()
})
@@ -1206,14 +1285,7 @@ func main() {
joinARoom := fyne.NewMenuItem("connect to a room", func() {
dialog.ShowEntryDialog("connect to a room", "JID:", func(s string) {
i := resourcePiloadingGif
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
if err != nil {
panic(err)
}
gif.Start()
gif.Show()
d := dialog.NewCustom("Please wait", "Close", gif, w)
d := dialog.NewCustom("Please wait", "Close", widget.NewProgressBarInfinite(), w)
d.Show()
go func() {
myjid, err := jid.Parse(s)
@@ -1242,14 +1314,7 @@ func main() {
beginADM := fyne.NewMenuItem("start a DM", func() {
dialog.ShowEntryDialog("Start a DM", "JID:", func(s string) {
i := resourcePiloadingGif
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
if err != nil {
panic(err)
}
gif.Start()
gif.Show()
d := dialog.NewCustom("Please wait", "Close", gif, w)
d := dialog.NewCustom("Please wait", "Close", widget.NewLabel("Opening a DM with "+s), w)
d.Show()
go func() {
myjid, err := jid.Parse(s)
@@ -1267,7 +1332,7 @@ func main() {
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)
hafjag := fyne.NewMenuItem("Hafjag", func() {
entry.Text = "Hafjag"
@@ -1314,6 +1379,13 @@ func main() {
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() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
@@ -1390,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() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
@@ -1408,6 +1480,36 @@ func main() {
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() {
replying = !replying
})
@@ -1517,7 +1619,7 @@ func main() {
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)
w.SetMainMenu(ma)
@@ -1558,9 +1660,9 @@ func main() {
dialog.ShowError(err, w)
}
fyne.Do(func() {
AppTabs.Selected().Text = fmt.Sprintf("%s (disconnected)", AppTabs.Selected().Text)
AppTabs.Remove(ti)
delete(UITabs, activeChatJid)
AppTabs.Selected().Text = fmt.Sprintf("%s (disconnected)", AppTabs.Selected().Text)
AppTabs.Remove(ti)
delete(UITabs, activeChatJid)
})
}()
}
@@ -1573,6 +1675,16 @@ 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) {
chatSidebar.Resize(chatSidebar.Size())
if AppTabs.Selected() == AppTabs.Items[0] {
@@ -1584,6 +1696,8 @@ func main() {
return
}
selectedScroller.Hidden = false
var activeChatJid string
for jid, tabData := range UITabs {
if tabData.Scroller == selectedScroller {
@@ -1601,8 +1715,8 @@ func main() {
nameLabel.Truncation = fyne.TextTruncateEllipsis
memberLabel := widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members)))
memberLabel.Truncation = fyne.TextTruncateEllipsis
box := container.NewVBox(nameLabel, )
chatSidebar.Objects = []fyne.CanvasObject{}
box := container.NewVBox(nameLabel)
// chatSidebar.Objects = []fyne.CanvasObject{}
for name, p := range tab.Members {
log.Println(string(p.Type))
@@ -1644,24 +1758,28 @@ func main() {
}
s.Truncation = fyne.TextTruncateEllipsis
box.Add(s)
box.Refresh()
}
}
}
chatSidebar = *container.NewGridWithColumns(1, container.NewVScroll(box))
} else {
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid))
if activeChatJid == config.Login.User {
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# "+activeChatJid), widget.NewLabel("Hey, that's you!"))
} else {
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid))
}
}
chatSidebar.Hidden = false
chatSidebar.Refresh()
chatSidebar.Resize(chatSidebar.Size())
chatSidebar.Show()
MainSplit.Resize(MainSplit.Size())
}
chatSidebar.Hidden = false
statBar.SetText("")
chatInfo = *container.NewHBox(widget.NewLabel(""))
MainSplit := container.NewHSplit(AppTabs, &chatSidebar)
MainSplit = container.NewHSplit(AppTabs, &chatSidebar)
DownSplit := container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn))
BigSplit := container.NewVSplit(MainSplit, DownSplit)
@@ -1673,5 +1791,15 @@ func main() {
MasterSplit.SetOffset(1)
w.SetContent(MasterSplit)
if time.Now().Month() == time.April && time.Now().Day() == 1 {
dialog.ShowConfirm("We care about your privacy", "We and our 5832 partner(s) would like to use your data for the following purposes:\n- Selling your organs\n- Stealing your identity\n- Putting you in jail\nYou're in control of your in data.\nDo you want us to do this? We'll do it anyway if you say no.", func(b bool) {
if b {
u, err := url.Parse("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
if err == nil {
a.OpenURL(u)
}
}
}, w)
}
w.ShowAndRun()
}
-12
View File
File diff suppressed because one or more lines are too long