37 Commits

Author SHA1 Message Date
4390763985 Add the ability to join rooms, as well as join 1:1 DMs inside the app 2025-08-10 09:50:24 +01:00
0e713ab417 Attempt to add loading wheel 2025-08-10 07:15:36 +01:00
sunglocto
a8b1160711 Delete emoji.go 2025-08-09 21:48:07 +00:00
187d8e750d Add the ability to join rooms 2025-08-09 22:45:39 +01:00
0578e6fafc idk 2025-08-09 18:10:32 +01:00
3571e5fad8 add ability to view readers of a message 2025-08-09 14:31:26 +01:00
7ed560e45d Update dependencies and add warning to Hot Fuck button 2025-08-09 10:07:05 +01:00
sunglocto
1f353ae258 Merge pull request #14 from 88572/master
Fix spelling and casing in one comment
2025-08-09 08:22:37 +01:00
sunglocto
f22cfb56c0 Merge pull request #12 from jjj333-p/patch-1
make /me parsing more efficient
2025-08-09 08:22:09 +01:00
sunglocto
f85caccbac Merge pull request #13 from jjj333-p/patch-2
make pointless loops no longer pointless
2025-08-09 08:21:22 +01:00
Nyx
09370be81a Fix spelling too 2025-08-08 20:46:36 -05:00
Nyx
c7664e6b96 fix: fix casing in comment 2025-08-08 20:45:48 -05:00
Joseph (Joe) Winkie
3aa619dd92 make pointless loops no longer pointless 2025-08-08 15:44:40 -10:00
Joseph (Joe) Winkie
dfc0b1088d make /me parsing more efficient
- only split once
- slicing a slice in go is efficient https://g.co/gemini/share/15cb7d25e6f3
2025-08-08 15:26:12 -10:00
147f10fe66 Merge branch 'master' of https://github.com/sunglocto/pi-im 2025-08-08 19:13:05 +01:00
4f0fe5cac7 Update all dependencies 2025-08-08 19:12:59 +01:00
sunglocto
4ae8efba21 Update README.md 2025-08-08 16:12:43 +00:00
sunglocto
35c4e76e0b Update README.md 2025-08-08 16:10:26 +00:00
sunglocto
3eee204bbf Update README.md 2025-08-08 16:08:19 +00:00
sunglocto
59d93da428 Update README.md 2025-08-08 16:07:17 +00:00
829438a3a5 lel 2025-08-08 11:24:36 +01:00
6c3195b029 Merge branch 'master' of https://github.com/sunglocto/pi-im 2025-08-08 10:26:21 +01:00
5b5d4656aa Put all greek letters in alphabetical order and capitalize them, except for Pi for obvious reasons 2025-08-08 10:26:14 +01:00
sunglocto
3afa1e7e38 Update README.md 2025-08-08 07:59:19 +00:00
ece04e1c36 Fix artifacts which broke due to renamed application name 2025-08-07 23:27:11 +01:00
52e38e7e66 Format code 2025-08-07 23:12:40 +01:00
59d83cb185 Add identicons 2025-08-07 23:11:56 +01:00
4015107de0 Continue last commit 2025-08-07 22:29:40 +01:00
150f42bc58 Begin seperation of XMPP and UI 2025-08-07 21:53:28 +01:00
3d6f835d4f Add Delta menu 2025-08-07 21:13:56 +01:00
922bc1d7cf Some changes 2025-08-07 11:50:50 +01:00
a61d3090e1 Disable all mardown support in preparation for a better solution 2025-08-07 06:50:23 +01:00
215839d833 Change name to pi-im everywhere 2025-08-07 06:43:09 +01:00
d7264e91f7 Change name to pi-im everywhere 2025-08-07 06:42:59 +01:00
sunglocto
93d3bb20d2 Add more testimonials 2025-08-06 21:05:15 +00:00
181b91edb4 dont send a message if entry is empty - Fixes #7 2025-08-06 21:59:49 +01:00
ca6bda7950 Do not show unknown state - Fixes #5 2025-08-06 21:39:27 +01:00
6 changed files with 535 additions and 211 deletions

View File

@@ -30,5 +30,5 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: pi-binary
path: pi
path: pi-im

View File

@@ -1,10 +1,10 @@
<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>
# π
[![build this now](https://github.com/sunglocto/pi/actions/workflows/go.yml/badge.svg)](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
> 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 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.
@@ -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>
</Login>
<Notifications>true</Notifications>
<DMs>person1@example.com</DMs>
</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.
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).
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
git clone https://github.com/sunglocto/pi
cd pi
git clone https://github.com/sunglocto/pi-im
cd pi-im
go mod tidy
go build .
./pi
./pi-im
```
> Uh, Windows???
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)
@@ -90,6 +101,10 @@ From fellow insane and schizophrenic XMPP users:
> 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)

21
go.mod
View File

@@ -1,19 +1,20 @@
module pi
module pi-im
go 1.24.5
require (
fyne.io/fyne/v2 v2.6.2
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
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 (
fyne.io/systray v1.11.0 // indirect
github.com/BurntSushi/toml v1.5.0 // 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/fyne-io/gl-js v0.2.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/stretchr/testify v1.10.0 // indirect
github.com/yuin/goldmark v1.7.13 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/image v0.29.0 // indirect
golang.org/x/mod v0.26.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/image v0.30.0 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/tools v0.35.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/tools v0.36.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
mellium.im/reader v0.1.0 // indirect
mellium.im/sasl v0.3.2 // indirect

38
go.sum
View File

@@ -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/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.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
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/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
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/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
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.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas=
golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
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/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
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 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
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/xmpp v0.22.0 h1:UthQVSwEAr7SNrmyc90c2ykGpVHxjn/3yw8Ey4+Im8s=
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-20250805052243-df6be3f9f629/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=
pain.agency/oasis-sdk v0.0.0-20250809192709-a3e5dff1aa61 h1:7zb69SAfLAJhXoqXZaS0pq/p1Y9W19Pm4FjcwWjTVoE=
pain.agency/oasis-sdk v0.0.0-20250809192709-a3e5dff1aa61/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=

634
main.go
View File

@@ -4,10 +4,12 @@ import (
//core - required
"encoding/xml"
"fmt"
"image/color"
_ "image/color"
"io"
"log"
"math/rand/v2"
"net/url"
"os"
"strings"
"time"
@@ -20,43 +22,83 @@ import (
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/rrivera/identicon"
extraWidgets "fyne.io/x/fyne/widget"
// xmpp - required
"mellium.im/xmpp/disco"
"mellium.im/xmpp/jid"
"mellium.im/xmpp/muc"
oasisSdk "pain.agency/oasis-sdk"
// gui - optional
// catppuccin "github.com/mbaklor/fyne-catppuccin"
adwaita "fyne.io/x/fyne/theme"
// TODO: integrated theme switcher
)
var version string = "3.1a"
var version string = "3.142a"
var statBar widget.Label
var chatInfo fyne.Container
var chatSidebar fyne.Container
var agreesToSendingHotFuckIntoChannel bool = false
// by sunglocto
// license AGPL
type Message struct {
Author string
Content string
ID string
ReplyID string
ImageURL string
Raw oasisSdk.XMPPChatMessage
Author string
Content string
ID string
ReplyID string
ImageURL string
Raw oasisSdk.XMPPChatMessage
Important bool
Readers []jid.JID
}
type MucTab struct {
Jid jid.JID
Nick string
Messages []Message
Scroller *widget.List
isMuc bool
Muc *muc.Channel
type ChatTab struct {
Jid jid.JID
Nick string
Messages []Message
isMuc bool
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 {
@@ -69,97 +111,113 @@ var config piConfig
var login oasisSdk.LoginInfo
var DMs []string
var chatTabs = make(map[string]*MucTab)
var tabs *container.AppTabs
var chatTabs = make(map[string]*ChatTab)
var UITabs = make(map[string]*ChatTabUI)
var AppTabs *container.AppTabs
var selectedId widget.ListItemID
var replying bool = false
var notifications bool
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 {
return adwaita.AdwaitaTheme().Color(name, variant)
return adwaita.AdwaitaTheme().Color(name, fyne.CurrentApp().Settings().ThemeVariant())
}
func (m myTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
return theme.DefaultTheme().Icon(name)
}
/*
func (m myTheme) Font(style fyne.TextStyle) fyne.Resource {
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 w fyne.Window
var a fyne.App
func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
mucJidStr := chatJid.String()
if _, ok := chatTabs[mucJidStr]; ok {
// Tab already exists
return
}
tabData := &MucTab{
Jid: chatJid,
Nick: nick,
Messages: []Message{},
isMuc: isMuc,
}
func CreateUITab(chatJidStr string) ChatTabUI {
var scroller *widget.List
scroller = widget.NewList(
func() int {
return len(tabData.Messages)
return len(chatTabs[chatJidStr].Messages)
},
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.TextStyle.Bold = true
content := widget.NewRichTextWithText("content")
content := widget.NewLabel("content")
content.Wrapping = fyne.TextWrapWord
content.Selectable = true
icon := theme.FileVideoIcon()
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) {
vbox := co.(*fyne.Container)
author := vbox.Objects[0].(*widget.Label)
content := vbox.Objects[1].(*widget.RichText)
authorBox := vbox.Objects[0].(*fyne.Container)
// 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)
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
msgContent := tabData.Messages[i].Content
if tabData.Messages[i].ImageURL != "" {
msgContent := chatTabs[chatJidStr].Messages[i].Content
if chatTabs[chatJidStr].Messages[i].ImageURL != "" {
btn.Hidden = false
btn.OnTapped = func() {
fyne.Do(func() {
u, err := storage.ParseURI(tabData.Messages[i].ImageURL)
go func() {
u, err := storage.ParseURI(chatTabs[chatJidStr].Messages[i].ImageURL)
if err != nil {
dialog.ShowError(err, w)
fyne.Do(func() {
dialog.ShowError(err, w)
})
return
}
if strings.HasSuffix(tabData.Messages[i].ImageURL, "mp4") {
url, err := url.Parse(tabData.Messages[i].ImageURL)
if strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "mp4") || strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "mp3") {
url, err := url.Parse(chatTabs[chatJidStr].Messages[i].ImageURL)
if err != nil {
dialog.ShowError(err, w)
fyne.Do(func() {
dialog.ShowError(err, w)
})
return
}
a.OpenURL(url)
fyne.Do(func() {
a.OpenURL(url)
})
return
}
image := canvas.NewImageFromURI(u)
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
@@ -171,27 +229,71 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
}
msgContent = strings.Join(lines, "\n")
content.ParseMarkdown(msgContent)
if tabData.Messages[i].ReplyID != "PICLIENT:UNAVAILABLE" {
author.SetText(fmt.Sprintf("%s > %s", tabData.Messages[i].Author, jid.MustParse(tabData.Messages[i].Raw.Reply.To).Resourcepart()))
//content.ParseMarkdown(msgContent)
content.SetText(msgContent)
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 {
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.OnSelected = func(id widget.ListItemID) {
selectedId = id
}
myUITab := ChatTabUI{}
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)
tabs.Append(tabItem)
myChatTab := ChatTab{
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) {
@@ -227,8 +329,7 @@ func dropToSignInPage(reason string) {
config.Login.User = userEntry.Text
config.Login.Password = passwordEntry.Text
config.Login.DisplayName = nicknameEntry.Text
config.Notifications = true
config.Login.MucsToJoin = append(config.Login.MucsToJoin, "ringen@muc.isekai.rocks") // DEBUG
config.Notifications = false
bytes, err := xml.MarshalIndent(config, "", "\t")
if err != nil {
@@ -263,8 +364,9 @@ func dropToSignInPage(reason string) {
func main() {
muc.Since(time.Now())
config = piConfig{}
a = app.NewWithID("pi-ism")
a = app.NewWithID("pi-im")
reader, err := a.Storage().Open("pi.xml")
if err != nil {
dropToSignInPage(err.Error())
@@ -305,10 +407,9 @@ func main() {
lines := strings.Split(str, "\n")
for i, line := range lines {
s := strings.Split(line, " ")
for j, v := range s {
for _, v := range s {
_, err := url.Parse(v)
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") {
img = v
}
@@ -335,9 +436,9 @@ func main() {
tab.Messages = append(tab.Messages, myMessage)
fyne.Do(func() {
tab.Scroller.Refresh()
UITabs[userJidStr].Scroller.Refresh()
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
ignore := false
correction := false
important := false
for _, v := range msg.Unknown {
if v.XMLName.Local == "delay" { // CLasic history message
if v.XMLName.Local == "delay" { // Classic history message
//ignore = true
//fmt.Println("ignoring!")
//return //what is blud doing
}
}
for _, v := range msg.Unknown {
if v.XMLName.Local == "replace" {
correction = true
break // dont need to look at more fields
}
}
@@ -364,8 +468,12 @@ func main() {
if tab, ok := chatTabs[mucJidStr]; ok {
chatTabs[mucJidStr].Muc = muc
str := *msg.CleanedBody
if strings.Contains(str, login.DisplayName) {
fmt.Println(str)
important = true
}
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))
}
}
@@ -373,11 +481,10 @@ func main() {
lines := strings.Split(str, "\n")
for i, line := range lines {
s := strings.Split(line, " ")
for j, v := range s {
for _, v := range s {
_, err := url.Parse(v)
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") || strings.HasSuffix(v, ".mp3") {
ImageID = v
}
}
@@ -396,11 +503,11 @@ func main() {
}
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() {
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
fyne.Do(func() {
tab.Scroller.Refresh()
UITabs[mucJidStr].Scroller.Refresh()
})
return
}
@@ -408,20 +515,21 @@ func main() {
}
myMessage := Message{
Author: msg.From.Resourcepart(),
Content: str,
ID: msg.ID,
ReplyID: replyID,
Raw: *msg,
ImageURL: ImageID,
Author: msg.From.Resourcepart(),
Content: str,
ID: msg.ID,
ReplyID: replyID,
Raw: *msg,
ImageURL: ImageID,
Important: important,
}
if !ignore {
tab.Messages = append(tab.Messages, myMessage)
}
fyne.Do(func() {
tab.Scroller.Refresh()
UITabs[mucJidStr].Scroller.Refresh()
if scrollDownOnNewMessage {
tab.Scroller.ScrollToBottom()
UITabs[mucJidStr].Scroller.ScrollToBottom()
}
})
}
@@ -435,7 +543,7 @@ func main() {
case oasisSdk.ChatStatePaused:
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:
fyne.Do(func() {
@@ -447,7 +555,7 @@ func main() {
})
default:
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())
},
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 {
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() {
for connection {
err = client.Connect()
@@ -503,32 +600,32 @@ func main() {
}()
a = app.New()
a.Settings().SetTheme(myTheme{})
//a.Settings().SetTheme(myTheme{})
w = a.NewWindow("pi")
w.Resize(fyne.NewSize(500, 500))
entry := widget.NewMultiLineEntry()
entry.SetPlaceHolder("Say something, you know you want to.")
entry.OnChanged = func(s string) {
}
entry := NewCustomMultiLineEntry()
entry.SetPlaceHolder("Say something, you know you want to.\nCtrl+Enter for newline")
entry.Wrapping = fyne.TextWrapBreak
//entry.TypedShortcut()
SendCallback := func() {
text := entry.Text
if tabs.Selected() == nil || tabs.Selected().Content == nil {
if AppTabs.Selected() == nil || AppTabs.Selected().Content == nil || text == "" {
return
}
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
return
}
var activeMucJid string
var isMuc bool
for jid, tabData := range chatTabs {
for jid, tabData := range UITabs {
if tabData.Scroller == selectedScroller {
activeMucJid = jid
isMuc = tabData.isMuc
isMuc = chatTabs[activeMucJid].isMuc
break
}
}
@@ -540,11 +637,25 @@ func main() {
go func() {
if replying {
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
}
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)
if err != nil {
dialog.ShowError(err, w)
}
@@ -558,7 +669,7 @@ func main() {
})
fyne.Do(func() {
if scrollDownOnNewMessage {
chatTabs[activeMucJid].Scroller.ScrollToBottom()
UITabs[activeMucJid].Scroller.ScrollToBottom()
}
})
}
@@ -566,6 +677,11 @@ func main() {
}
sendbtn := widget.NewButton("Send", SendCallback)
replybtn := widget.NewButton("Reply", func() {
replying = true
SendCallback()
replying = false
})
entry.OnSubmitted = func(s string) {
SendCallback()
// i fucking hate fyne
@@ -614,7 +730,7 @@ func main() {
})
jtb := fyne.NewMenuItem("jump to bottom", func() {
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
return
}
@@ -622,7 +738,7 @@ func main() {
})
jtt := fyne.NewMenuItem("jump to top", func() {
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
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() {
var link string
var toperr error
@@ -691,7 +804,9 @@ func main() {
progress := make(chan oasisSdk.UploadProgress)
myprogressbar := widget.NewProgressBar()
diag := dialog.NewCustom("Uploading file", "Hide", myprogressbar, w)
diag.Show()
fyne.Do(func() {
diag.Show()
})
go func() {
client.UploadFile(client.Ctx, reader.URI().Path(), progress)
}()
@@ -721,32 +836,172 @@ func main() {
}, w)
})
servDisc := fyne.NewMenuItem("Service discovery", func() {
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)
leaveRoom := fyne.NewMenuItem("Leave current room (experimental)", func() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
return
}
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 {
activeMucJid = jid
break
@@ -762,15 +1017,15 @@ func main() {
})
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 {
return
}
var activeChatJid string
for jid, tabData := range chatTabs {
for jid, tabData := range UITabs {
if tabData.Scroller == selectedScroller {
activeChatJid = jid
break
@@ -784,15 +1039,51 @@ func main() {
return
}
pre.SetText(string(bytes))
pre.Selectable = true
pre.Refresh()
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)
tabs = container.NewAppTabs(
AppTabs = container.NewAppTabs(
container.NewTabItem("τίποτα", widget.NewLabel(`
pi
`)),
@@ -813,14 +1104,14 @@ func main() {
}
}
tabs.OnSelected = func(ti *container.TabItem) {
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
AppTabs.OnSelected = func(ti *container.TabItem) {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
return
}
var activeChatJid string
for jid, tabData := range chatTabs {
for jid, tabData := range UITabs {
if tabData.Scroller == selectedScroller {
activeChatJid = jid
break
@@ -828,19 +1119,22 @@ func main() {
}
tab := chatTabs[activeChatJid]
UITab := UITabs[activeChatJid]
if tab.isMuc {
chatInfo = *container.NewHBox(widget.NewLabel(tab.Muc.Addr().String()))
//chatInfo = *container.NewHBox(widget.NewLabel(tab.Muc.Addr().String()))
} else {
chatInfo = *container.NewHBox(widget.NewLabel(tab.Jid.String()))
}
if tab.isMuc {
chatSidebar = *container.NewStack(container.NewVScroll(container.NewVBox(widget.NewRichTextFromMarkdown(fmt.Sprintf("# %s", tab.Jid.String())), widget.NewRichTextFromMarkdown(tab.Muc.Addr().String()))))
//chatSidebar.Refresh()
}
chatSidebar = *UITab.Sidebar
old := chatSidebar.Position()
chatSidebar.Refresh()
chatSidebar.Move(old)
}
statBar.SetText("nothing seems to be happening right now...")
w.SetContent(container.NewVSplit(container.NewVSplit(container.NewHSplit(tabs, &chatSidebar), container.NewHSplit(entry, sendbtn)), container.NewHSplit(&statBar, &chatInfo)))
// HACK - disable chatsidebar because it's currently very buggy
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()
}

12
piloadinggif.go Normal file

File diff suppressed because one or more lines are too long