forked from sunglocto/pi-im
Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
4390763985 | |||
0e713ab417 | |||
![]() |
a8b1160711 | ||
187d8e750d | |||
0578e6fafc | |||
3571e5fad8 | |||
7ed560e45d | |||
![]() |
1f353ae258 | ||
![]() |
f22cfb56c0 | ||
![]() |
f85caccbac | ||
![]() |
09370be81a | ||
![]() |
c7664e6b96 | ||
![]() |
3aa619dd92 | ||
![]() |
dfc0b1088d | ||
147f10fe66 | |||
4f0fe5cac7 | |||
![]() |
4ae8efba21 | ||
![]() |
35c4e76e0b | ||
![]() |
3eee204bbf | ||
![]() |
59d93da428 | ||
829438a3a5 | |||
6c3195b029 | |||
5b5d4656aa | |||
![]() |
3afa1e7e38 | ||
ece04e1c36 | |||
52e38e7e66 | |||
59d83cb185 | |||
4015107de0 | |||
150f42bc58 | |||
3d6f835d4f | |||
922bc1d7cf | |||
a61d3090e1 | |||
215839d833 | |||
d7264e91f7 | |||
![]() |
93d3bb20d2 | ||
181b91edb4 | |||
ca6bda7950 |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -30,5 +30,5 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: pi-binary
|
name: pi-binary
|
||||||
path: pi
|
path: pi-im
|
||||||
|
|
||||||
|
39
README.md
39
README.md
@@ -1,10 +1,10 @@
|
|||||||
<center>
|
<center>
|
||||||
<img src="https://github.com/sunglocto/pi/blob/255bc3749c089e3945871ddf19dd17d14a83f9ff/pi.png">
|
<img width="100" height="100" src="https://github.com/sunglocto/pi/blob/255bc3749c089e3945871ddf19dd17d14a83f9ff/pi.png">
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
# π
|
|
||||||
[](https://github.com/sunglocto/pi/actions/workflows/go.yml)
|
[](https://github.com/sunglocto/pi/actions/workflows/go.yml)
|
||||||
<img width="1050" height="632" alt="image" src="https://github.com/user-attachments/assets/a5a3a7ab-8a85-49f0-81e9-ae5b977e7455" />
|
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/9e2d9209-6ad5-4f22-94d0-4cc18c835372" />
|
||||||
|
|
||||||
|
|
||||||
## the XMPP client from hell
|
## the XMPP client from hell
|
||||||
> it's 10% code. 20% ai
|
> it's 10% code. 20% ai
|
||||||
@@ -13,7 +13,7 @@ Experimental and extremely weird XMPP client written in Go. No solicitors.
|
|||||||
|
|
||||||
pi is currently pre-pre-pre-pre alpha software which you should not use as your primary XMPP client.
|
pi is currently pre-pre-pre-pre alpha software which you should not use as your primary XMPP client.
|
||||||
|
|
||||||
pi uses [Fyne](https://fyne.io) for the frontend and uses the [Oasis SDK](https://github.com/jjj333-p/oasis-sdk) for XMPP functionality.
|
pi uses [Fyne](https://fyne.io) for the frontend and uses the [Oasis SDK](https://github.com/jjj333-p/oasis-sdk) by [Joseph Winkie](https://pain.agency) for XMPP functionality.
|
||||||
|
|
||||||
pi is an extremely opinionated client. It aims to have as little extra windows as possible, instead using alt-menus to perform many of the actions you'd see in a typical client.
|
pi is an extremely opinionated client. It aims to have as little extra windows as possible, instead using alt-menus to perform many of the actions you'd see in a typical client.
|
||||||
|
|
||||||
@@ -38,12 +38,19 @@ If you want to add MUCs or DMs, you must configure the program by editing the pi
|
|||||||
<MucsToJoin>room2@muc.example.com</MucsToJoin>
|
<MucsToJoin>room2@muc.example.com</MucsToJoin>
|
||||||
</Login>
|
</Login>
|
||||||
<Notifications>true</Notifications>
|
<Notifications>true</Notifications>
|
||||||
|
<DMs>person1@example.com</DMs>
|
||||||
</piConfig>
|
</piConfig>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The file is usually located at, on GNU/Linux systems:
|
||||||
|
```
|
||||||
|
~/.config/fyne/pi-im/Documents/pi.xml
|
||||||
|
```
|
||||||
|
This will be changed eventually, likely before a 3b release.
|
||||||
|
|
||||||
Currently joining and saving DM tabs is not supported, nor is getting avatars, reactions or encryption.
|
Currently joining and saving DM tabs is not supported, nor is getting avatars, reactions or encryption.
|
||||||
|
|
||||||
As of writing, pi supports basic message sending and receiving, replies and ~~file upload~~.
|
As of writing, pi supports basic message sending and receiving, replies, file upload and corrections.
|
||||||
|
|
||||||
|
|
||||||
## να χτίσω
|
## να χτίσω
|
||||||
@@ -51,23 +58,27 @@ As of writing, pi supports basic message sending and receiving, replies and ~~fi
|
|||||||
|
|
||||||
To build pi, you will need the latest version of Go, at least 1.21. You can grab it [here](https://go.dev).
|
To build pi, you will need the latest version of Go, at least 1.21. You can grab it [here](https://go.dev).
|
||||||
|
|
||||||
The build instructions are very simple. Simply clone the repo, fetch the repositories and build the program:
|
The build instructions are very simple. Simply clone the repo, fetch the repositories and build the program.
|
||||||
|
|
||||||
Here is a summary of the commands you would need to use to build and run the program:
|
Here is a summary of the commands you would need to use:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/sunglocto/pi
|
git clone https://github.com/sunglocto/pi-im
|
||||||
cd pi
|
cd pi-im
|
||||||
go mod tidy
|
go mod tidy
|
||||||
go build .
|
go build .
|
||||||
./pi
|
./pi-im
|
||||||
```
|
```
|
||||||
> Uh, Windows???
|
> Uh, Windows???
|
||||||
|
|
||||||
Eventually. Don't count on it.
|
Eventually. Don't count on it.
|
||||||
Fyne has first-class support for Windows and none of my dependencies are platform dependent. I've built this app for Android before. If you compile it, it will most likely work with no issues.
|
Fyne has first-class support for Windows and all of my dependencies are platform imdependent. I've built this app for Android before. If you compile it, it will most likely work with no issues.
|
||||||
|
|
||||||
Static executable snapshots are also provided for GNU/Linux systems, and CI runs on every commit, producing a binary on every successful build. You're welcome.
|
Static executable snapshots are also provided for GNU/Linux systems on every new version, and CI runs on every commit, producing a binary on every successful build. You're welcome.
|
||||||
|
|
||||||
|
## εγκατάσταση
|
||||||
|
(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).
|
||||||
|
|
||||||
## υποστήριξη
|
## υποστήριξη
|
||||||
(support)
|
(support)
|
||||||
@@ -90,6 +101,10 @@ From fellow insane and schizophrenic XMPP users:
|
|||||||
|
|
||||||
> pi devstream when
|
> pi devstream when
|
||||||
|
|
||||||
|
<img width="361" height="66" alt="image" src="https://github.com/user-attachments/assets/5a926f6b-1005-4795-a6ef-4e0538bb4d5a" />
|
||||||
|
<img width="316" height="73" alt="image" src="https://github.com/user-attachments/assets/52309c60-8110-43eb-9c45-56c9cfd82cc4" />
|
||||||
|
|
||||||
|
|
||||||
## επιπλέον
|
## επιπλέον
|
||||||
(extra)
|
(extra)
|
||||||
|
|
||||||
|
21
go.mod
21
go.mod
@@ -1,19 +1,20 @@
|
|||||||
module pi
|
module pi-im
|
||||||
|
|
||||||
go 1.24.5
|
go 1.24.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/fyne/v2 v2.6.2
|
fyne.io/fyne/v2 v2.6.2
|
||||||
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb
|
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb
|
||||||
|
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
|
||||||
mellium.im/xmpp v0.22.0
|
mellium.im/xmpp v0.22.0
|
||||||
pain.agency/oasis-sdk v0.0.0-20250805052243-df6be3f9f629
|
pain.agency/oasis-sdk v0.0.0-20250809192709-a3e5dff1aa61
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/systray v1.11.0 // indirect
|
fyne.io/systray v1.11.0 // indirect
|
||||||
github.com/BurntSushi/toml v1.5.0 // 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/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
|
||||||
@@ -36,14 +37,14 @@ require (
|
|||||||
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.10.0 // indirect
|
||||||
github.com/yuin/goldmark v1.7.13 // indirect
|
github.com/yuin/goldmark v1.7.13 // indirect
|
||||||
golang.org/x/crypto v0.40.0 // indirect
|
golang.org/x/crypto v0.41.0 // indirect
|
||||||
golang.org/x/image v0.29.0 // indirect
|
golang.org/x/image v0.30.0 // indirect
|
||||||
golang.org/x/mod v0.26.0 // indirect
|
golang.org/x/mod v0.27.0 // indirect
|
||||||
golang.org/x/net v0.42.0 // indirect
|
golang.org/x/net v0.43.0 // indirect
|
||||||
golang.org/x/sync v0.16.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/sys v0.34.0 // indirect
|
golang.org/x/sys v0.35.0 // indirect
|
||||||
golang.org/x/text v0.27.0 // indirect
|
golang.org/x/text v0.28.0 // indirect
|
||||||
golang.org/x/tools v0.35.0 // indirect
|
golang.org/x/tools v0.36.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
mellium.im/reader v0.1.0 // indirect
|
mellium.im/reader v0.1.0 // indirect
|
||||||
mellium.im/sasl v0.3.2 // indirect
|
mellium.im/sasl v0.3.2 // indirect
|
||||||
|
38
go.sum
38
go.sum
@@ -10,8 +10,8 @@ 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/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=
|
||||||
@@ -58,6 +58,8 @@ github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
|||||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||||
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/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
|
github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
|
||||||
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
||||||
@@ -68,22 +70,22 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
||||||
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||||
golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas=
|
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
|
||||||
golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA=
|
golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c=
|
||||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -97,5 +99,5 @@ mellium.im/xmlstream v0.15.4 h1:gLKxcWl4rLMUpKgtzrTBvr4OexPeO/edYus+uK3F6ZI=
|
|||||||
mellium.im/xmlstream v0.15.4/go.mod h1:yXaCW2++fmVO4L9piKVkyLDqnCmictVYF7FDQW8prb4=
|
mellium.im/xmlstream v0.15.4/go.mod h1:yXaCW2++fmVO4L9piKVkyLDqnCmictVYF7FDQW8prb4=
|
||||||
mellium.im/xmpp v0.22.0 h1:UthQVSwEAr7SNrmyc90c2ykGpVHxjn/3yw8Ey4+Im8s=
|
mellium.im/xmpp v0.22.0 h1:UthQVSwEAr7SNrmyc90c2ykGpVHxjn/3yw8Ey4+Im8s=
|
||||||
mellium.im/xmpp v0.22.0/go.mod h1:WSjq12nhREFD88Vy/0WD6Q8inE8t6a8w7QjzwivWitw=
|
mellium.im/xmpp v0.22.0/go.mod h1:WSjq12nhREFD88Vy/0WD6Q8inE8t6a8w7QjzwivWitw=
|
||||||
pain.agency/oasis-sdk v0.0.0-20250805052243-df6be3f9f629 h1:NE+Z2HQzc77nw7l7DsSDSi0x9l+YfLfXBYerK+GsPrQ=
|
pain.agency/oasis-sdk v0.0.0-20250809192709-a3e5dff1aa61 h1:7zb69SAfLAJhXoqXZaS0pq/p1Y9W19Pm4FjcwWjTVoE=
|
||||||
pain.agency/oasis-sdk v0.0.0-20250805052243-df6be3f9f629/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=
|
pain.agency/oasis-sdk v0.0.0-20250809192709-a3e5dff1aa61/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=
|
||||||
|
634
main.go
634
main.go
@@ -4,10 +4,12 @@ import (
|
|||||||
//core - required
|
//core - required
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
_ "image/color"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand/v2"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -20,43 +22,83 @@ 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"
|
||||||
|
"github.com/rrivera/identicon"
|
||||||
|
extraWidgets "fyne.io/x/fyne/widget"
|
||||||
|
|
||||||
// xmpp - required
|
// xmpp - required
|
||||||
"mellium.im/xmpp/disco"
|
"mellium.im/xmpp/disco"
|
||||||
"mellium.im/xmpp/jid"
|
"mellium.im/xmpp/jid"
|
||||||
"mellium.im/xmpp/muc"
|
"mellium.im/xmpp/muc"
|
||||||
oasisSdk "pain.agency/oasis-sdk"
|
oasisSdk "pain.agency/oasis-sdk"
|
||||||
|
|
||||||
// gui - optional
|
// gui - optional
|
||||||
// catppuccin "github.com/mbaklor/fyne-catppuccin"
|
// catppuccin "github.com/mbaklor/fyne-catppuccin"
|
||||||
adwaita "fyne.io/x/fyne/theme"
|
|
||||||
// TODO: integrated theme switcher
|
// TODO: integrated theme switcher
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "3.1a"
|
var version string = "3.142a"
|
||||||
var statBar widget.Label
|
var statBar widget.Label
|
||||||
var chatInfo fyne.Container
|
var chatInfo fyne.Container
|
||||||
var chatSidebar fyne.Container
|
var chatSidebar fyne.Container
|
||||||
|
|
||||||
|
var agreesToSendingHotFuckIntoChannel bool = false
|
||||||
|
|
||||||
// by sunglocto
|
// by sunglocto
|
||||||
// license AGPL
|
// license AGPL
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Author string
|
Author string
|
||||||
Content string
|
Content string
|
||||||
ID string
|
ID string
|
||||||
ReplyID string
|
ReplyID string
|
||||||
ImageURL string
|
ImageURL string
|
||||||
Raw oasisSdk.XMPPChatMessage
|
Raw oasisSdk.XMPPChatMessage
|
||||||
|
Important bool
|
||||||
|
Readers []jid.JID
|
||||||
}
|
}
|
||||||
|
|
||||||
type MucTab struct {
|
type ChatTab struct {
|
||||||
Jid jid.JID
|
Jid jid.JID
|
||||||
Nick string
|
Nick string
|
||||||
Messages []Message
|
Messages []Message
|
||||||
Scroller *widget.List
|
isMuc bool
|
||||||
isMuc bool
|
Muc *muc.Channel
|
||||||
Muc *muc.Channel
|
UpdateSidebar bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatTabUI struct {
|
||||||
|
Internal *ChatTab
|
||||||
|
Scroller *widget.List `xml:"-"`
|
||||||
|
Sidebar *fyne.Container `xml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomMultiLineEntry struct {
|
||||||
|
widget.Entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomMultiLineEntry() *CustomMultiLineEntry {
|
||||||
|
entry := &CustomMultiLineEntry{}
|
||||||
|
entry.ExtendBaseWidget(entry)
|
||||||
|
entry.MultiLine = true
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CustomMultiLineEntry) TypedShortcut(sc fyne.Shortcut) {
|
||||||
|
if sc.ShortcutName() == "CustomDesktop:Control+Return" {
|
||||||
|
e.Entry.TypedRune('\n')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.Entry.TypedShortcut(sc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CustomMultiLineEntry) TypedKey(ev *fyne.KeyEvent) {
|
||||||
|
if ev.Name == fyne.KeyReturn || ev.Name == fyne.KeyEnter {
|
||||||
|
// Normal Enter (no modifier) = submit
|
||||||
|
if e.OnSubmitted != nil {
|
||||||
|
e.OnSubmitted(e.Text)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e.Entry.TypedKey(ev)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type piConfig struct {
|
type piConfig struct {
|
||||||
@@ -69,97 +111,113 @@ var config piConfig
|
|||||||
var login oasisSdk.LoginInfo
|
var login oasisSdk.LoginInfo
|
||||||
var DMs []string
|
var DMs []string
|
||||||
|
|
||||||
var chatTabs = make(map[string]*MucTab)
|
var chatTabs = make(map[string]*ChatTab)
|
||||||
var tabs *container.AppTabs
|
var UITabs = make(map[string]*ChatTabUI)
|
||||||
|
|
||||||
|
var AppTabs *container.AppTabs
|
||||||
var selectedId widget.ListItemID
|
var selectedId widget.ListItemID
|
||||||
var replying bool = false
|
var replying bool = false
|
||||||
var notifications bool
|
var notifications bool
|
||||||
var connection bool = true
|
var connection bool = true
|
||||||
|
|
||||||
type myTheme struct{}
|
/*
|
||||||
|
func (m myTheme) Font(style fyne.TextStyle) fyne.Resource {
|
||||||
|
return resourceAppleColorEmojiTtf
|
||||||
|
}
|
||||||
|
|
||||||
func (m myTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
|
func (m myTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
|
||||||
return adwaita.AdwaitaTheme().Color(name, variant)
|
return adwaita.AdwaitaTheme().Color(name, fyne.CurrentApp().Settings().ThemeVariant())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m myTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
|
func (m myTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
|
||||||
return theme.DefaultTheme().Icon(name)
|
return theme.DefaultTheme().Icon(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func (m myTheme) Font(style fyne.TextStyle) fyne.Resource {
|
func (m myTheme) Font(style fyne.TextStyle) fyne.Resource {
|
||||||
return theme.DefaultTheme().Font(style)
|
return theme.DefaultTheme().Font(style)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
func (m myTheme) Size(name fyne.ThemeSizeName) float32 {
|
|
||||||
if name == theme.SizeNameHeadingText {
|
|
||||||
return 18
|
|
||||||
}
|
|
||||||
return theme.DefaultTheme().Size(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
var scrollDownOnNewMessage bool = true
|
var scrollDownOnNewMessage bool = true
|
||||||
var w fyne.Window
|
var w fyne.Window
|
||||||
var a fyne.App
|
var a fyne.App
|
||||||
|
|
||||||
func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
func CreateUITab(chatJidStr string) ChatTabUI {
|
||||||
mucJidStr := chatJid.String()
|
|
||||||
if _, ok := chatTabs[mucJidStr]; ok {
|
|
||||||
// Tab already exists
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tabData := &MucTab{
|
|
||||||
Jid: chatJid,
|
|
||||||
Nick: nick,
|
|
||||||
Messages: []Message{},
|
|
||||||
isMuc: isMuc,
|
|
||||||
}
|
|
||||||
|
|
||||||
var scroller *widget.List
|
var scroller *widget.List
|
||||||
scroller = widget.NewList(
|
scroller = widget.NewList(
|
||||||
func() int {
|
func() int {
|
||||||
return len(tabData.Messages)
|
return len(chatTabs[chatJidStr].Messages)
|
||||||
},
|
},
|
||||||
func() fyne.CanvasObject {
|
func() fyne.CanvasObject {
|
||||||
|
gen, _ := identicon.New("github", 5, 3)
|
||||||
|
ii, _ := gen.Draw("default")
|
||||||
|
im := ii.Image(25)
|
||||||
|
ico := canvas.NewImageFromImage(im)
|
||||||
|
ico.FillMode = canvas.ImageFillOriginal
|
||||||
author := widget.NewLabel("author")
|
author := widget.NewLabel("author")
|
||||||
author.TextStyle.Bold = true
|
author.TextStyle.Bold = true
|
||||||
content := widget.NewRichTextWithText("content")
|
content := widget.NewLabel("content")
|
||||||
content.Wrapping = fyne.TextWrapWord
|
content.Wrapping = fyne.TextWrapWord
|
||||||
|
content.Selectable = true
|
||||||
icon := theme.FileVideoIcon()
|
icon := theme.FileVideoIcon()
|
||||||
btn := widget.NewButtonWithIcon("View media", icon, func() {
|
btn := widget.NewButtonWithIcon("View media", icon, func() {
|
||||||
|
|
||||||
})
|
})
|
||||||
return container.NewVBox(author, content, btn)
|
return container.NewVBox(container.NewHBox(ico, author), content, btn)
|
||||||
},
|
},
|
||||||
func(i widget.ListItemID, co fyne.CanvasObject) {
|
func(i widget.ListItemID, co fyne.CanvasObject) {
|
||||||
vbox := co.(*fyne.Container)
|
vbox := co.(*fyne.Container)
|
||||||
author := vbox.Objects[0].(*widget.Label)
|
authorBox := vbox.Objects[0].(*fyne.Container)
|
||||||
content := vbox.Objects[1].(*widget.RichText)
|
// generate a Icon
|
||||||
|
|
||||||
|
gen, _ := identicon.New("github", 5, 3)
|
||||||
|
ii, _ := gen.Draw(chatTabs[chatJidStr].Messages[i].Author)
|
||||||
|
im := ii.Image(25)
|
||||||
|
authorBox.Objects[0] = canvas.NewImageFromImage(im)
|
||||||
|
authorBox.Objects[0].(*canvas.Image).FillMode = canvas.ImageFillOriginal
|
||||||
|
authorBox.Objects[0].Refresh()
|
||||||
|
|
||||||
|
// Icon generate end
|
||||||
|
|
||||||
|
author := authorBox.Objects[1].(*widget.Label)
|
||||||
|
content := vbox.Objects[1].(*widget.Label)
|
||||||
btn := vbox.Objects[2].(*widget.Button)
|
btn := vbox.Objects[2].(*widget.Button)
|
||||||
|
if chatTabs[chatJidStr].Messages[i].Important {
|
||||||
|
//content.Importance = widget.DangerImportance TODO: Fix highlighting messages with mentions, it's currently broken
|
||||||
|
}
|
||||||
btn.Hidden = true // Hide by default
|
btn.Hidden = true // Hide by default
|
||||||
msgContent := tabData.Messages[i].Content
|
msgContent := chatTabs[chatJidStr].Messages[i].Content
|
||||||
if tabData.Messages[i].ImageURL != "" {
|
if chatTabs[chatJidStr].Messages[i].ImageURL != "" {
|
||||||
btn.Hidden = false
|
btn.Hidden = false
|
||||||
btn.OnTapped = func() {
|
btn.OnTapped = func() {
|
||||||
fyne.Do(func() {
|
go func() {
|
||||||
u, err := storage.ParseURI(tabData.Messages[i].ImageURL)
|
u, err := storage.ParseURI(chatTabs[chatJidStr].Messages[i].ImageURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dialog.ShowError(err, w)
|
fyne.Do(func() {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strings.HasSuffix(tabData.Messages[i].ImageURL, "mp4") {
|
if strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "mp4") || strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "mp3") {
|
||||||
url, err := url.Parse(tabData.Messages[i].ImageURL)
|
url, err := url.Parse(chatTabs[chatJidStr].Messages[i].ImageURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dialog.ShowError(err, w)
|
fyne.Do(func() {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a.OpenURL(url)
|
fyne.Do(func() {
|
||||||
|
a.OpenURL(url)
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
image := canvas.NewImageFromURI(u)
|
image := canvas.NewImageFromURI(u)
|
||||||
image.FillMode = canvas.ImageFillOriginal
|
image.FillMode = canvas.ImageFillOriginal
|
||||||
dialog.ShowCustom("Image", "Close", image, w)
|
fyne.Do(func() {
|
||||||
})
|
dialog.ShowCustom("Image", "Close", image, w)
|
||||||
|
})
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if the message is a quote
|
// Check if the message is a quote
|
||||||
@@ -171,27 +229,71 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
|||||||
}
|
}
|
||||||
msgContent = strings.Join(lines, "\n")
|
msgContent = strings.Join(lines, "\n")
|
||||||
|
|
||||||
content.ParseMarkdown(msgContent)
|
//content.ParseMarkdown(msgContent)
|
||||||
if tabData.Messages[i].ReplyID != "PICLIENT:UNAVAILABLE" {
|
content.SetText(msgContent)
|
||||||
author.SetText(fmt.Sprintf("%s > %s", tabData.Messages[i].Author, jid.MustParse(tabData.Messages[i].Raw.Reply.To).Resourcepart()))
|
if chatTabs[chatJidStr].Messages[i].ReplyID != "PICLIENT:UNAVAILABLE" {
|
||||||
|
author.SetText(fmt.Sprintf("%s > %s", chatTabs[chatJidStr].Messages[i].Author, jid.MustParse(chatTabs[chatJidStr].Messages[i].Raw.Reply.To).Resourcepart()))
|
||||||
} else {
|
} else {
|
||||||
author.SetText(tabData.Messages[i].Author)
|
author.SetText(chatTabs[chatJidStr].Messages[i].Author)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sl := strings.Split(msgContent, " ")
|
||||||
|
if sl[0] == "/me" {
|
||||||
|
author.SetText(author.Text + " " + strings.Join(sl[1:], " "))
|
||||||
|
content.SetText(" ")
|
||||||
|
}
|
||||||
|
|
||||||
scroller.SetItemHeight(i, vbox.MinSize().Height)
|
scroller.SetItemHeight(i, vbox.MinSize().Height)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
scroller.OnSelected = func(id widget.ListItemID) {
|
scroller.OnSelected = func(id widget.ListItemID) {
|
||||||
selectedId = id
|
selectedId = id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myUITab := ChatTabUI{}
|
||||||
|
|
||||||
scroller.CreateItem()
|
scroller.CreateItem()
|
||||||
|
myUITab.Scroller = scroller
|
||||||
|
gen, _ := identicon.New("github", 50, 20)
|
||||||
|
ii, _ := gen.Draw(chatJidStr)
|
||||||
|
im := ii.Image(250)
|
||||||
|
imw := canvas.NewImageFromImage(im)
|
||||||
|
imw.FillMode = canvas.ImageFillOriginal
|
||||||
|
myUITab.Sidebar = container.NewVBox(imw)
|
||||||
|
|
||||||
tabData.Scroller = scroller
|
return myUITab
|
||||||
|
}
|
||||||
|
func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
||||||
|
|
||||||
chatTabs[mucJidStr] = tabData
|
chatJidStr := chatJid.String()
|
||||||
|
if _, ok := chatTabs[chatJidStr]; ok {
|
||||||
|
// Tab already exists
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tabItem := container.NewTabItem(chatJid.Localpart(), scroller)
|
myChatTab := ChatTab{
|
||||||
tabs.Append(tabItem)
|
Jid: chatJid,
|
||||||
|
Nick: nick,
|
||||||
|
Messages: []Message{},
|
||||||
|
isMuc: isMuc,
|
||||||
|
}
|
||||||
|
|
||||||
|
myUITab := CreateUITab(chatJid.String())
|
||||||
|
myUITab.Internal = &myChatTab
|
||||||
|
|
||||||
|
chatTabs[chatJidStr] = &myChatTab
|
||||||
|
UITabs[chatJidStr] = &myUITab
|
||||||
|
var icon fyne.Resource
|
||||||
|
if isMuc {
|
||||||
|
icon = theme.HomeIcon()
|
||||||
|
} else{
|
||||||
|
icon = theme.AccountIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
fyne.Do(func() {
|
||||||
|
AppTabs.Append(container.NewTabItemWithIcon(chatJid.String(), icon, myUITab.Scroller))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropToSignInPage(reason string) {
|
func dropToSignInPage(reason string) {
|
||||||
@@ -227,8 +329,7 @@ func dropToSignInPage(reason string) {
|
|||||||
config.Login.User = userEntry.Text
|
config.Login.User = userEntry.Text
|
||||||
config.Login.Password = passwordEntry.Text
|
config.Login.Password = passwordEntry.Text
|
||||||
config.Login.DisplayName = nicknameEntry.Text
|
config.Login.DisplayName = nicknameEntry.Text
|
||||||
config.Notifications = true
|
config.Notifications = false
|
||||||
config.Login.MucsToJoin = append(config.Login.MucsToJoin, "ringen@muc.isekai.rocks") // DEBUG
|
|
||||||
|
|
||||||
bytes, err := xml.MarshalIndent(config, "", "\t")
|
bytes, err := xml.MarshalIndent(config, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -263,8 +364,9 @@ func dropToSignInPage(reason string) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
muc.Since(time.Now())
|
muc.Since(time.Now())
|
||||||
|
|
||||||
config = piConfig{}
|
config = piConfig{}
|
||||||
a = app.NewWithID("pi-ism")
|
a = app.NewWithID("pi-im")
|
||||||
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())
|
||||||
@@ -305,10 +407,9 @@ func main() {
|
|||||||
lines := strings.Split(str, "\n")
|
lines := strings.Split(str, "\n")
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
s := strings.Split(line, " ")
|
s := strings.Split(line, " ")
|
||||||
for j, v := range s {
|
for _, v := range s {
|
||||||
_, err := url.Parse(v)
|
_, err := url.Parse(v)
|
||||||
if err == nil && strings.HasPrefix(v, "https://") {
|
if err == nil && strings.HasPrefix(v, "https://") {
|
||||||
s[j] = fmt.Sprintf("[%s](%s)", v, v)
|
|
||||||
if strings.HasSuffix(v, ".png") || strings.HasSuffix(v, ".jpg") || strings.HasSuffix(v, ".jpeg") || strings.HasSuffix(v, ".webp") || strings.HasSuffix(v, ".mp4") {
|
if strings.HasSuffix(v, ".png") || strings.HasSuffix(v, ".jpg") || strings.HasSuffix(v, ".jpeg") || strings.HasSuffix(v, ".webp") || strings.HasSuffix(v, ".mp4") {
|
||||||
img = v
|
img = v
|
||||||
}
|
}
|
||||||
@@ -335,9 +436,9 @@ func main() {
|
|||||||
|
|
||||||
tab.Messages = append(tab.Messages, myMessage)
|
tab.Messages = append(tab.Messages, myMessage)
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
tab.Scroller.Refresh()
|
UITabs[userJidStr].Scroller.Refresh()
|
||||||
if scrollDownOnNewMessage {
|
if scrollDownOnNewMessage {
|
||||||
tab.Scroller.ScrollToBottom()
|
UITabs[userJidStr].Scroller.ScrollToBottom()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -346,16 +447,19 @@ func main() {
|
|||||||
// HACK: IGNORING ALL MESSAGES FROM CLASSIC MUC HISTORY IN PREPARATION OF MAM SUPPORT
|
// HACK: IGNORING ALL MESSAGES FROM CLASSIC MUC HISTORY IN PREPARATION OF MAM SUPPORT
|
||||||
ignore := false
|
ignore := false
|
||||||
correction := false
|
correction := false
|
||||||
|
important := false
|
||||||
for _, v := range msg.Unknown {
|
for _, v := range msg.Unknown {
|
||||||
if v.XMLName.Local == "delay" { // CLasic history message
|
if v.XMLName.Local == "delay" { // Classic history message
|
||||||
//ignore = true
|
//ignore = true
|
||||||
//fmt.Println("ignoring!")
|
//fmt.Println("ignoring!")
|
||||||
|
//return //what is blud doing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range msg.Unknown {
|
for _, v := range msg.Unknown {
|
||||||
if v.XMLName.Local == "replace" {
|
if v.XMLName.Local == "replace" {
|
||||||
correction = true
|
correction = true
|
||||||
|
break // dont need to look at more fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,8 +468,12 @@ func main() {
|
|||||||
if tab, ok := chatTabs[mucJidStr]; ok {
|
if tab, ok := chatTabs[mucJidStr]; ok {
|
||||||
chatTabs[mucJidStr].Muc = muc
|
chatTabs[mucJidStr].Muc = muc
|
||||||
str := *msg.CleanedBody
|
str := *msg.CleanedBody
|
||||||
|
if strings.Contains(str, login.DisplayName) {
|
||||||
|
fmt.Println(str)
|
||||||
|
important = true
|
||||||
|
}
|
||||||
if !ignore && notifications {
|
if !ignore && notifications {
|
||||||
if !correction && 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,11 +481,10 @@ func main() {
|
|||||||
lines := strings.Split(str, "\n")
|
lines := strings.Split(str, "\n")
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
s := strings.Split(line, " ")
|
s := strings.Split(line, " ")
|
||||||
for j, v := range s {
|
for _, v := range s {
|
||||||
_, err := url.Parse(v)
|
_, err := url.Parse(v)
|
||||||
if err == nil && strings.HasPrefix(v, "https://") {
|
if err == nil && strings.HasPrefix(v, "https://") {
|
||||||
s[j] = fmt.Sprintf("[%s](%s)", v, v)
|
if strings.HasSuffix(v, ".png") || strings.HasSuffix(v, ".jpg") || strings.HasSuffix(v, ".jpeg") || strings.HasSuffix(v, ".webp") || strings.HasSuffix(v, ".mp4") || strings.HasSuffix(v, ".mp3") {
|
||||||
if strings.HasSuffix(v, ".png") || strings.HasSuffix(v, ".jpg") || strings.HasSuffix(v, ".jpeg") || strings.HasSuffix(v, ".webp") || strings.HasSuffix(v, ".mp4") {
|
|
||||||
ImageID = v
|
ImageID = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,11 +503,11 @@ 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 + " (edited)"
|
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
tab.Scroller.Refresh()
|
UITabs[mucJidStr].Scroller.Refresh()
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -408,20 +515,21 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
myMessage := Message{
|
myMessage := Message{
|
||||||
Author: msg.From.Resourcepart(),
|
Author: msg.From.Resourcepart(),
|
||||||
Content: str,
|
Content: str,
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
ReplyID: replyID,
|
ReplyID: replyID,
|
||||||
Raw: *msg,
|
Raw: *msg,
|
||||||
ImageURL: ImageID,
|
ImageURL: ImageID,
|
||||||
|
Important: important,
|
||||||
}
|
}
|
||||||
if !ignore {
|
if !ignore {
|
||||||
tab.Messages = append(tab.Messages, myMessage)
|
tab.Messages = append(tab.Messages, myMessage)
|
||||||
}
|
}
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
tab.Scroller.Refresh()
|
UITabs[mucJidStr].Scroller.Refresh()
|
||||||
if scrollDownOnNewMessage {
|
if scrollDownOnNewMessage {
|
||||||
tab.Scroller.ScrollToBottom()
|
UITabs[mucJidStr].Scroller.ScrollToBottom()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -435,7 +543,7 @@ func main() {
|
|||||||
case oasisSdk.ChatStatePaused:
|
case oasisSdk.ChatStatePaused:
|
||||||
|
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
statBar.SetText(fmt.Sprintf("%s has stoped typing.", from.Resourcepart()))
|
statBar.SetText(fmt.Sprintf("%s has stopped typing.", from.Resourcepart()))
|
||||||
})
|
})
|
||||||
case oasisSdk.ChatStateInactive:
|
case oasisSdk.ChatStateInactive:
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
@@ -447,7 +555,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
statBar.SetText(fmt.Sprint("Unknown state: ", state))
|
statBar.SetText("")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -455,36 +563,25 @@ func main() {
|
|||||||
fmt.Printf("Delivered %s to %s", id, from.String())
|
fmt.Printf("Delivered %s to %s", id, from.String())
|
||||||
},
|
},
|
||||||
func(_ *oasisSdk.XmppClient, from jid.JID, id string) {
|
func(_ *oasisSdk.XmppClient, from jid.JID, id string) {
|
||||||
fmt.Printf("%s has seen %s", from.String(), id)
|
for _, tab := range chatTabs {
|
||||||
|
for i := len(tab.Messages) - 1; i >= 0; i-- {
|
||||||
|
fmt.Println(tab.Messages[i])
|
||||||
|
if tab.Messages[i].Raw.StanzaID == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tab.Messages[i].Raw.StanzaID.ID == id {
|
||||||
|
tab.Messages[i].Readers = append(tab.Messages[i].Readers, from)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("%s has seen %s\n", from.String(), id)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Could not create client - " + err.Error())
|
log.Fatalln("Could not create client - " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
client.Session.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
|
|
||||||
d := xml.NewTokenDecoder(t)
|
|
||||||
|
|
||||||
// Ignore anything that's not a message.
|
|
||||||
if start.Name.Local != "message" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := struct {
|
|
||||||
stanza.Message
|
|
||||||
Body string `xml:"body"`
|
|
||||||
}{}
|
|
||||||
err := d.DecodeElement(&msg, start)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if msg.Body != "" {
|
|
||||||
log.Println("Got message: %q", msg.Body)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
*/
|
|
||||||
go func() {
|
go func() {
|
||||||
for connection {
|
for connection {
|
||||||
err = client.Connect()
|
err = client.Connect()
|
||||||
@@ -503,32 +600,32 @@ func main() {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
a = app.New()
|
a = app.New()
|
||||||
a.Settings().SetTheme(myTheme{})
|
//a.Settings().SetTheme(myTheme{})
|
||||||
w = a.NewWindow("pi")
|
w = a.NewWindow("pi")
|
||||||
w.Resize(fyne.NewSize(500, 500))
|
w.Resize(fyne.NewSize(500, 500))
|
||||||
|
|
||||||
entry := widget.NewMultiLineEntry()
|
entry := NewCustomMultiLineEntry()
|
||||||
entry.SetPlaceHolder("Say something, you know you want to.")
|
entry.SetPlaceHolder("Say something, you know you want to.\nCtrl+Enter for newline")
|
||||||
entry.OnChanged = func(s string) {
|
entry.Wrapping = fyne.TextWrapBreak
|
||||||
}
|
//entry.TypedShortcut()
|
||||||
|
|
||||||
SendCallback := func() {
|
SendCallback := func() {
|
||||||
text := entry.Text
|
text := entry.Text
|
||||||
if tabs.Selected() == nil || tabs.Selected().Content == nil {
|
if AppTabs.Selected() == nil || AppTabs.Selected().Content == nil || text == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeMucJid string
|
var activeMucJid string
|
||||||
var isMuc bool
|
var isMuc bool
|
||||||
for jid, tabData := range chatTabs {
|
for jid, tabData := range UITabs {
|
||||||
if tabData.Scroller == selectedScroller {
|
if tabData.Scroller == selectedScroller {
|
||||||
activeMucJid = jid
|
activeMucJid = jid
|
||||||
isMuc = tabData.isMuc
|
isMuc = chatTabs[activeMucJid].isMuc
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -540,11 +637,25 @@ func main() {
|
|||||||
go func() {
|
go func() {
|
||||||
if replying {
|
if replying {
|
||||||
m := chatTabs[activeMucJid].Messages[selectedId].Raw
|
m := chatTabs[activeMucJid].Messages[selectedId].Raw
|
||||||
client.ReplyToEvent(&m, text)
|
fmt.Println(selectedId)
|
||||||
|
err = client.ReplyToEvent(&m, text)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url, uerr := url.Parse(strings.Split(text, " ")[0])
|
||||||
|
if uerr == nil && strings.HasPrefix(strings.Split(text, " ")[0], "https://") {
|
||||||
|
//err = client.SendImage(jid.MustParse(activeMucJid).Bare(), text, url.String(), &text)
|
||||||
|
err = client.SendSingleFileMessage(jid.MustParse(activeMucJid).Bare(), url.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
err = client.SendText(jid.MustParse(activeMucJid).Bare(), text)
|
err = client.SendText(jid.MustParse(activeMucJid).Bare(), text)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
}
|
}
|
||||||
@@ -558,7 +669,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
if scrollDownOnNewMessage {
|
if scrollDownOnNewMessage {
|
||||||
chatTabs[activeMucJid].Scroller.ScrollToBottom()
|
UITabs[activeMucJid].Scroller.ScrollToBottom()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -566,6 +677,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendbtn := widget.NewButton("Send", SendCallback)
|
sendbtn := widget.NewButton("Send", SendCallback)
|
||||||
|
replybtn := widget.NewButton("Reply", func() {
|
||||||
|
replying = true
|
||||||
|
SendCallback()
|
||||||
|
replying = false
|
||||||
|
})
|
||||||
entry.OnSubmitted = func(s string) {
|
entry.OnSubmitted = func(s string) {
|
||||||
SendCallback()
|
SendCallback()
|
||||||
// i fucking hate fyne
|
// i fucking hate fyne
|
||||||
@@ -614,7 +730,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
jtb := fyne.NewMenuItem("jump to bottom", func() {
|
jtb := fyne.NewMenuItem("jump to bottom", func() {
|
||||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -622,7 +738,7 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
jtt := fyne.NewMenuItem("jump to top", func() {
|
jtt := fyne.NewMenuItem("jump to top", func() {
|
||||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -663,9 +779,6 @@ func main() {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
deb := fyne.NewMenuItem("DEBUG: Attempt to get MAM history from a user", func() {
|
|
||||||
//res, err := history.Fetch(client.Ctx, history.Query{}, jid.MustParse("ringen@muc.isekai.rocks"), client.Session)
|
|
||||||
})
|
|
||||||
mic := fyne.NewMenuItem("upload a file", func() {
|
mic := fyne.NewMenuItem("upload a file", func() {
|
||||||
var link string
|
var link string
|
||||||
var toperr error
|
var toperr error
|
||||||
@@ -691,7 +804,9 @@ func main() {
|
|||||||
progress := make(chan oasisSdk.UploadProgress)
|
progress := make(chan oasisSdk.UploadProgress)
|
||||||
myprogressbar := widget.NewProgressBar()
|
myprogressbar := widget.NewProgressBar()
|
||||||
diag := dialog.NewCustom("Uploading file", "Hide", myprogressbar, w)
|
diag := dialog.NewCustom("Uploading file", "Hide", myprogressbar, w)
|
||||||
diag.Show()
|
fyne.Do(func() {
|
||||||
|
diag.Show()
|
||||||
|
})
|
||||||
go func() {
|
go func() {
|
||||||
client.UploadFile(client.Ctx, reader.URI().Path(), progress)
|
client.UploadFile(client.Ctx, reader.URI().Path(), progress)
|
||||||
}()
|
}()
|
||||||
@@ -721,32 +836,172 @@ func main() {
|
|||||||
}, w)
|
}, w)
|
||||||
})
|
})
|
||||||
|
|
||||||
servDisc := fyne.NewMenuItem("Service discovery", func() {
|
leaveRoom := fyne.NewMenuItem("Leave current room (experimental)", func() {
|
||||||
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
myBox := container.NewVBox()
|
|
||||||
info, err := disco.GetInfo(client.Ctx, "", jid.MustParse("ringen@muc.isekai.rocks"), client.Session)
|
|
||||||
if err != nil {
|
|
||||||
dialog.ShowError(err, w)
|
|
||||||
}
|
|
||||||
m := info.Features
|
|
||||||
for _, v := range m {
|
|
||||||
myBox.Objects = append(myBox.Objects, widget.NewLabel(v.Var))
|
|
||||||
myBox.Refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.ShowCustom("things", "cancel", myBox, w)
|
|
||||||
})
|
|
||||||
|
|
||||||
menu_help := fyne.NewMenu("π", mit, reconnect, deb)
|
|
||||||
menu_changeroom := fyne.NewMenu("β", mic, servDisc)
|
|
||||||
menu_configureview := fyne.NewMenu("γ", mia, mis, jtt, jtb)
|
|
||||||
bit := fyne.NewMenuItem("mark selected message as read", func() {
|
|
||||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var activeMucJid string
|
var activeMucJid string
|
||||||
for jid, tabData := range chatTabs {
|
for jid, tabData := range UITabs {
|
||||||
|
if tabData.Scroller == selectedScroller {
|
||||||
|
activeMucJid = jid
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppTabs.Selected().Text = fmt.Sprintf("%s (disconnected)", AppTabs.Selected().Text)
|
||||||
|
AppTabs.SelectIndex(0)
|
||||||
|
delete(client.MucChannels, activeMucJid)
|
||||||
|
//delete(chatTabs, activeMucJid)
|
||||||
|
})
|
||||||
|
|
||||||
|
joinARoom := fyne.NewMenuItem("Join a room", func() {
|
||||||
|
dialog.ShowEntryDialog("Join 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.Show()
|
||||||
|
go func() {
|
||||||
|
myjid, err := jid.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
d.Hide()
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
joinjid, err := myjid.WithResource(login.DisplayName)
|
||||||
|
if err != nil {
|
||||||
|
d.Hide()
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch, err := client.MucClient.Join(client.Ctx, joinjid, client.Session)
|
||||||
|
if err != nil {
|
||||||
|
d.Hide()
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.MucChannels[s] = ch
|
||||||
|
addChatTab(true, myjid, login.DisplayName)
|
||||||
|
d.Hide()
|
||||||
|
}()
|
||||||
|
}, w)
|
||||||
|
})
|
||||||
|
|
||||||
|
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.Show()
|
||||||
|
go func() {
|
||||||
|
myjid, err := jid.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
d.Hide()
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addChatTab(false, myjid, login.DisplayName)
|
||||||
|
d.Hide()
|
||||||
|
}()
|
||||||
|
}, w)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
servDisc := fyne.NewMenuItem("Disco features", func() {
|
||||||
|
var search jid.JID
|
||||||
|
dialog.ShowEntryDialog("Disco features", "JID: ", func(s string) { // TODO: replace with undeprecated widget
|
||||||
|
search, err = jid.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
myBox := container.NewGridWithColumns(1, widget.NewLabel("Items"))
|
||||||
|
info, err := disco.GetInfo(client.Ctx, "", search, client.Session)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m := info.Features
|
||||||
|
for _, v := range m {
|
||||||
|
myBox.Add(widget.NewLabel(v.Var))
|
||||||
|
myBox.Refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.ShowCustom("Features", "cancel", myBox, w)
|
||||||
|
|
||||||
|
}, w)
|
||||||
|
})
|
||||||
|
|
||||||
|
savedata := fyne.NewMenuItem("DEBUG: Save tab data to disk", func() {
|
||||||
|
d := []ChatTab{}
|
||||||
|
for _, v := range chatTabs {
|
||||||
|
d = append(d, *v)
|
||||||
|
}
|
||||||
|
b, err := xml.Marshal(d)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os.Create("test.xml")
|
||||||
|
os.WriteFile("text.xml", b, os.ModeAppend)
|
||||||
|
})
|
||||||
|
menu_help := fyne.NewMenu("π", mit, reconnect, savedata)
|
||||||
|
|
||||||
|
menu_changeroom := fyne.NewMenu("Α", mic, servDisc, beginADM, joinARoom, leaveRoom)
|
||||||
|
menu_configureview := fyne.NewMenu("Β", mia, mis, jtt, jtb)
|
||||||
|
hafjag := fyne.NewMenuItem("Hafjag", func() {
|
||||||
|
entry.Text = "Hafjag"
|
||||||
|
SendCallback()
|
||||||
|
entry.Text = "Hafjag"
|
||||||
|
})
|
||||||
|
|
||||||
|
hotfuck := fyne.NewMenuItem("Hot Fuck", func() {
|
||||||
|
d := dialog.NewConfirm("WARNING", "This button will send the message \"Hot Fuck\" into your currently focused chat. Do you want to continue?", func(b bool) {
|
||||||
|
if b {
|
||||||
|
agreesToSendingHotFuckIntoChannel = true
|
||||||
|
entry.Text = "Hot Fuck"
|
||||||
|
SendCallback()
|
||||||
|
entry.Text = "Oh Yeah."
|
||||||
|
}
|
||||||
|
}, w)
|
||||||
|
|
||||||
|
if agreesToSendingHotFuckIntoChannel {
|
||||||
|
d.Confirm()
|
||||||
|
} else {
|
||||||
|
d.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
agree := fyne.NewMenuItem("Agree", func() {
|
||||||
|
old := entry.Text
|
||||||
|
entry.Text = strings.Repeat("^", rand.IntN(30))
|
||||||
|
SendCallback()
|
||||||
|
entry.Text = old
|
||||||
|
})
|
||||||
|
|
||||||
|
mycurrenttime := fyne.NewMenuItem("Current time", func() {
|
||||||
|
entry.Text = fmt.Sprintf("It is currently %s", time.Now().Format(time.RFC850))
|
||||||
|
SendCallback()
|
||||||
|
})
|
||||||
|
menu_jokes := fyne.NewMenu("Δ", mycurrenttime, hafjag, hotfuck, agree)
|
||||||
|
bit := fyne.NewMenuItem("mark selected message as read", func() {
|
||||||
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var activeMucJid string
|
||||||
|
for jid, tabData := range UITabs {
|
||||||
if tabData.Scroller == selectedScroller {
|
if tabData.Scroller == selectedScroller {
|
||||||
activeMucJid = jid
|
activeMucJid = jid
|
||||||
break
|
break
|
||||||
@@ -762,15 +1017,15 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
bic := fyne.NewMenuItem("show message XML", func() {
|
bic := fyne.NewMenuItem("show message XML", func() {
|
||||||
pre := widget.NewLabel("")
|
pre := widget.NewMultiLineEntry()
|
||||||
|
|
||||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeChatJid string
|
var activeChatJid string
|
||||||
for jid, tabData := range chatTabs {
|
for jid, tabData := range UITabs {
|
||||||
if tabData.Scroller == selectedScroller {
|
if tabData.Scroller == selectedScroller {
|
||||||
activeChatJid = jid
|
activeChatJid = jid
|
||||||
break
|
break
|
||||||
@@ -784,15 +1039,51 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
pre.SetText(string(bytes))
|
pre.SetText(string(bytes))
|
||||||
pre.Selectable = true
|
|
||||||
pre.Refresh()
|
pre.Refresh()
|
||||||
dialog.ShowCustom("Message", "Close", pre, w)
|
dialog.ShowCustom("Message", "Close", pre, w)
|
||||||
})
|
})
|
||||||
menu_messageoptions := fyne.NewMenu("Σ", bit, bia, bic)
|
|
||||||
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions)
|
red := fyne.NewMenuItem("show people who have read this message", func() {
|
||||||
|
pre := container.NewVBox()
|
||||||
|
|
||||||
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeChatJid string
|
||||||
|
for jid, tabData := range UITabs {
|
||||||
|
if tabData.Scroller == selectedScroller {
|
||||||
|
activeChatJid = jid
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gen, _ := identicon.New("github", 5, 3)
|
||||||
|
m := chatTabs[activeChatJid].Messages[selectedId].Readers
|
||||||
|
for _, v := range m {
|
||||||
|
if chatTabs[activeChatJid].isMuc {
|
||||||
|
ii, _ := gen.Draw(v.Resourcepart())
|
||||||
|
im := ii.Image(25)
|
||||||
|
iw := canvas.NewImageFromImage(im)
|
||||||
|
iw.FillMode = canvas.ImageFillOriginal
|
||||||
|
pre.Add(container.NewHBox(iw, widget.NewLabel(v.Resourcepart())))
|
||||||
|
} else {
|
||||||
|
ii, _ := gen.Draw(v.Localpart())
|
||||||
|
im := ii.Image(25)
|
||||||
|
iw := canvas.NewImageFromImage(im)
|
||||||
|
iw.FillMode = canvas.ImageFillOriginal
|
||||||
|
pre.Add(container.NewHBox(iw, widget.NewLabel(v.Localpart())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pre.Refresh()
|
||||||
|
dialog.ShowCustom("Message", "Close", pre, w)
|
||||||
|
})
|
||||||
|
|
||||||
|
menu_messageoptions := fyne.NewMenu("Γ", bit, bia, bic, red)
|
||||||
|
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions, menu_jokes)
|
||||||
w.SetMainMenu(ma)
|
w.SetMainMenu(ma)
|
||||||
|
|
||||||
tabs = container.NewAppTabs(
|
AppTabs = container.NewAppTabs(
|
||||||
container.NewTabItem("τίποτα", widget.NewLabel(`
|
container.NewTabItem("τίποτα", widget.NewLabel(`
|
||||||
pi
|
pi
|
||||||
`)),
|
`)),
|
||||||
@@ -813,14 +1104,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tabs.OnSelected = func(ti *container.TabItem) {
|
AppTabs.OnSelected = func(ti *container.TabItem) {
|
||||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeChatJid string
|
var activeChatJid string
|
||||||
for jid, tabData := range chatTabs {
|
for jid, tabData := range UITabs {
|
||||||
if tabData.Scroller == selectedScroller {
|
if tabData.Scroller == selectedScroller {
|
||||||
activeChatJid = jid
|
activeChatJid = jid
|
||||||
break
|
break
|
||||||
@@ -828,19 +1119,22 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tab := chatTabs[activeChatJid]
|
tab := chatTabs[activeChatJid]
|
||||||
|
UITab := UITabs[activeChatJid]
|
||||||
if tab.isMuc {
|
if tab.isMuc {
|
||||||
chatInfo = *container.NewHBox(widget.NewLabel(tab.Muc.Addr().String()))
|
//chatInfo = *container.NewHBox(widget.NewLabel(tab.Muc.Addr().String()))
|
||||||
} else {
|
} else {
|
||||||
chatInfo = *container.NewHBox(widget.NewLabel(tab.Jid.String()))
|
chatInfo = *container.NewHBox(widget.NewLabel(tab.Jid.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if tab.isMuc {
|
chatSidebar = *UITab.Sidebar
|
||||||
chatSidebar = *container.NewStack(container.NewVScroll(container.NewVBox(widget.NewRichTextFromMarkdown(fmt.Sprintf("# %s", tab.Jid.String())), widget.NewRichTextFromMarkdown(tab.Muc.Addr().String()))))
|
old := chatSidebar.Position()
|
||||||
//chatSidebar.Refresh()
|
chatSidebar.Refresh()
|
||||||
}
|
chatSidebar.Move(old)
|
||||||
}
|
}
|
||||||
|
|
||||||
statBar.SetText("nothing seems to be happening right now...")
|
// HACK - disable chatsidebar because it's currently very buggy
|
||||||
w.SetContent(container.NewVSplit(container.NewVSplit(container.NewHSplit(tabs, &chatSidebar), container.NewHSplit(entry, sendbtn)), container.NewHSplit(&statBar, &chatInfo)))
|
chatSidebar.Hidden = true
|
||||||
|
statBar.SetText("")
|
||||||
|
w.SetContent(container.NewVSplit(container.NewVSplit(AppTabs, container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn))), container.NewHSplit(&statBar, &chatInfo)))
|
||||||
w.ShowAndRun()
|
w.ShowAndRun()
|
||||||
}
|
}
|
||||||
|
12
piloadinggif.go
Normal file
12
piloadinggif.go
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user