45 Commits

Author SHA1 Message Date
3f6b87623d Add license button 2025-09-02 19:40:35 +01:00
d3bffcef81 Finalize join bookmarks button, fix now playing button and make messages containing a single emoji bigger 2025-09-02 19:25:54 +01:00
53367e5982 Add MPRIS player grabbing to Delta menu 2025-09-02 14:47:39 +01:00
b765e8f11c lol 2025-09-02 13:21:40 +01:00
969199b0d7 lol 2025-09-02 13:20:46 +01:00
5b0072a488 lol 2025-09-02 13:17:59 +01:00
636df591b8 dep info 2025-09-02 13:15:13 +01:00
d4433a20ed Merge branch 'master' of https://forge.sunglocto.net/sunglocto/pi-im 2025-09-02 09:55:13 +01:00
80489d1877 some changes 2025-09-02 09:55:04 +01:00
713bde2d23 Merge pull request 'meow' (#18) from nyx/pi-im:nyx-patch-1 into master
Reviewed-on: #18
Reviewed-by: sunglocto <sunglocto@protonmail.com>
2025-09-02 08:53:59 +00:00
nyx
ffd6023ae2 meow 2025-09-02 08:52:23 +00:00
003a156c50 performance 2025-09-02 09:27:20 +01:00
402383f516 Add experimental and very buggy reply text 2025-09-02 09:26:10 +01:00
c0d19e4335 Reset module tracking, in order to fix emojis 2025-09-02 08:18:02 +01:00
853bed70e7 Add experimental functionality to join bookmarks 2025-09-01 20:05:06 +01:00
21564cc70e Fix capitalization of pi 2025-09-01 14:51:38 +01:00
f9ac50392e +netbsd support 2025-09-01 14:48:34 +01:00
fa11b3ee87 Add bookmark join button 2025-08-31 17:58:05 +01:00
7eabb0c30e Bump all dependencies 2025-08-31 15:58:17 +01:00
1da705a1b0 Update oasisSdk and apply breaking changes 2025-08-31 15:53:00 +01:00
5988eead04 fix logo in readme 2025-08-28 08:53:09 +01:00
2fd5f48334 Remove all workflows 2025-08-27 12:03:22 +01:00
d2b9f9843f Some changes
Some checks failed
build for freebsd / build (push) Has been cancelled
build Linux / build (push) Has been cancelled
build for macos / build (push) Has been cancelled
build for windows / build (push) Has been cancelled
2025-08-27 12:02:38 +01:00
65e4e3380c Update README.md
Some checks failed
build for freebsd / build (push) Has been cancelled
build Linux / build (push) Has been cancelled
build for macos / build (push) Has been cancelled
build for windows / build (push) Has been cancelled
2025-08-27 11:01:36 +00:00
8e58139fcb Update README.md
Some checks failed
build for freebsd / build (push) Has been cancelled
build Linux / build (push) Has been cancelled
build for macos / build (push) Has been cancelled
build for windows / build (push) Has been cancelled
2025-08-27 11:00:27 +00:00
sunglocto
50caef2f39 Update README.md 2025-08-19 08:50:06 +00:00
4b64de882d Add more information to README 2025-08-18 09:38:43 +01:00
dfaf56a330 Merge branch 'master' of https://github.com/sunglocto/pi-im 2025-08-16 10:57:41 +01:00
02f6adad02 Fix message XML popup 2025-08-16 10:57:36 +01:00
sunglocto
a6285b0d8a Update README.md 2025-08-12 11:04:53 +00:00
6267a4a072 Attempt to fix freebsd workflow 2025-08-12 11:57:32 +01:00
ac5d0aa2bf Attempt to fix freebsd workflow 2025-08-12 11:50:20 +01:00
c20f77ce0f Attempt to fix freebsd workflow 2025-08-12 11:42:31 +01:00
59ef99f200 Attempt to fix freebsd workflow 2025-08-12 11:36:27 +01:00
56797b92eb Attempt to fix freebsd workflow 2025-08-12 11:29:17 +01:00
e391ea9ad6 Attempt to fix freebsd workflow 2025-08-12 11:23:02 +01:00
a02a043808 Attempt to fix freebsd workflow 2025-08-12 11:15:48 +01:00
bd34df424b Add 2 more workflows (hopefully they work) 2025-08-12 11:06:53 +01:00
efa801b393 Revert youtube embed attempt 2025-08-12 11:03:25 +01:00
f65cf4a17d Merge branch 'master' of https://github.com/sunglocto/pi-im 2025-08-12 10:55:24 +01:00
7e8ceb2b2a Attempt to add YouTube embed handling, revise .webp support, support corrections in 1:1 DMs, and a whole load of other changes. Also bump version number to 3i and begin Indev phase 2025-08-12 10:55:15 +01:00
6ab9dd8ff8 Attempt to add YouTube embed handling, revise .webp support, support corrections in 1:1 DMs, and a whole load of other changes. Also bump version number to 3i and begin Indev phase 2025-08-12 10:55:00 +01:00
sunglocto
91881439a4 Update README.md 2025-08-10 12:40:45 +00:00
sunglocto
2972c86641 Update README.md - Add "Windows" screenshot 2025-08-10 12:36:27 +00:00
e5f391edfd add windows ci (which literally nobody will use) 2025-08-10 10:13:54 +01:00
6 changed files with 4564 additions and 299 deletions

View File

@@ -1,34 +0,0 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: build this now
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24'
- name: Install X11 dependencies
run: sudo apt-get update && sudo apt-get install -y libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxxf86vm-dev
- name: Build
run: go build .
- name: Artifact
uses: actions/upload-artifact@v4
with:
name: pi-binary
path: pi-im

123
README.md
View File

@@ -1,9 +1,7 @@
<center>
<img width="100" height="100" src="https://github.com/sunglocto/pi/blob/255bc3749c089e3945871ddf19dd17d14a83f9ff/pi.png">
<img width="100" height="100" src="https://forge.sunglocto.net/sunglocto/pi-im/raw/branch/master/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="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/9e2d9209-6ad5-4f22-94d0-4cc18c835372" />
<img alt="Pi screenshot" src="https://github.com/user-attachments/assets/9e2d9209-6ad5-4f22-94d0-4cc18c835372" />
## the XMPP client from hell
@@ -58,27 +56,65 @@ As of writing, pi supports basic message sending and receiving, replies, file up
To build pi, you will need the latest version of Go, at least 1.21. You can grab it [here](https://go.dev).
You must have a C compiler on your system also present. This will be the case for virtually all PC operating systems except Windows.
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:
```bash
git clone https://github.com/sunglocto/pi-im
git clone https://forge.sunglocto.net/sunglocto/pi-im
cd pi-im
go mod tidy
go build .
./pi-im
```
> Uh, Windows???
Eventually. Don't count on it.
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.
Windows requires more steps to setup, specifically you need an MSYS2 environment. As per [the Fyne documentation](https://docs.fyne.io/started/)
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.
The MSYS2 platform is the recommended approach for working on Windows. Proceed as follows:
Install MSYS2 from msys2.org
Once installed do not use the MSYS terminal that opens
Open “MSYS2 MinGW 64-bit” from the start menu
Execute the following commands (if asked for install options be sure to choose “all”):
$ pacman -Syu
$ pacman -S git mingw-w64-x86_64-toolchain mingw-w64-x86_64-go
You will need to add ~/Go/bin to your $PATH, for MSYS2 you can paste the following command into your terminal:
$ echo "export PATH=\$PATH:~/Go/bin" >> ~/.bashrc
For the compiler to work on other terminals you will need to set up the windows %PATH% variable to find these tools. Go to the “Edit the system environment variables” control panel, tap “Advanced” and add “C:\msys64\mingw64\bin” to the Path list.
<img alt="image" src="https://github.com/user-attachments/assets/5a6c188f-e890-4398-856c-e88f5804e9d2" />
pi has been tested on FreeBSD and NetBSD with no issues. Please read their respective Wiki entries.
Static executable snapshots are also provided for GNU/Linux systems on every new version.
## Wayland
On BSD and Linux systems, you may want to build an executable for Wayland only, meaning it will not run through XWayland. This will increase performance and also allow drag-on-drop functionality. Most Wayland setups can run X11 apps, but no X11 setup can run Wayland apps. Because of this, all executables are shipped for use with X11. This may change in the future, but if you want to build for Wayland, it's as simple as the commands above, but just adding a `wayland` tag to the build command.
A Wayland-only executable will also substantially decrease the level of screen tearing and lag.
```bash
git clone https://forge.sunglocto.net/sunglocto/pi-im
cd pi-im
go mod tidy
go build -tags wayland .
./pi-im
```
## εγκατάσταση
(installation)
Pi currently has no consolidated way of installing it. There is an [Arch User Repository package available](https://aur.archlinux.org/pi-im), which is maintained by [snit](https://isekai.rocks/~snit).
pi currently has no consolidated way of installing it. There is an [Arch User Repository package available](https://aur.archlinux.org/pi-im), which is maintained by [snit](https://isekai.rocks/~snit).
## υποστήριξη
(support)
@@ -87,6 +123,71 @@ You can file an issue and explain the problem you are having.
If you would like a more instant method of communication, join the [pi XMPP room.](xmpp:pi@room.sunglocto.net?join)
## εξαρτήσεις
(dependencies)
### Running
Pi will require no additional dependencies to run on MacOS and Windows systems.
On other *NIX systems, `dbus` is needed for notification support, but is not required.
If your system has an X11 or Wayland setup, Pi will most likely work without issues.
Pi does not require GTK, Qt or any other graphical layout.
### Compiling
In general, you need Go, a C compiler and graphical libraries.
*This information is from the [the Fyne documentation.](https://docs.fyne.io/started) Windows and MacOS users are heavily reccomended to simply use pre-compiled binaries, due a more complex set of dependencies.*
Linux systems:
Debian, Ubuntu and Raspberry Pi OS: `sudo apt-get install golang gcc libgl1-mesa-dev xorg-dev libxkbcommon-dev`
Fedora: `sudo dnf install golang golang-misc gcc libXcursor-devel libXrandr-devel mesa-libGL-devel libXi-devel libXinerama-devel libXxf86vm-devel libxkbcommon-devel wayland-devel`
Arch Linux: `sudo pacman -S go xorg-server-devel libxcursor libxrandr libxinerama libxi libxkbcommon`
Solus: `sudo eopkg it -c system.devel golang mesalib-devel libxrandr-devel libxcursor-devel libxi-devel libxinerama-devel libxkbcommon-devel`
openSUSE: `sudo zypper install go gcc libXcursor-devel libXrandr-devel Mesa-libGL-devel libXi-devel libXinerama-devel libXxf86vm-devel libxkbcommon-devel`
Void Linux: `sudo xbps-install -S go base-devel xorg-server-devel libXrandr-devel libXcursor-devel libXinerama-devel libXxf86vm-devel libxkbcommon-devel wayland-devel`
Alpine Linux: `sudo apk add go gcc libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev linux-headers mesa-dev libxkbcommon-dev wayland-dev``
NixOS: `nix-shell -p libGL pkg-config xorg.libX11.dev xorg.libXcursor xorg.libXi xorg.libXinerama xorg.libXrandr xorg.libXxf86vm libxkbcommon wayland`
BSD systems:
FreeBSD: `sudo pkg install go gcc xorg pkgconf`
OpenBSD: `sudo pkg_add go`
NetBSD: `sudo pkgin install go pkgconf`
## αρμονία
(compatibility)
pi will run on the following operating systems:
- Windows 10 and up
- Most modern GNU/Linux installations
- FreeBSD
- NetBSD
- OpenBSD
- MacOS
pi has experimental support for the following operating system:
- Android [1]
pi most likely will **not** run on the following operating systems:
- 32-bit systems
- iOS
- ARM systems
If you use pi on a specific operating system and it does not work, file an issue and describe the problem you have having.
## μαρτυρίες
(testimonials)
From fellow insane and schizophrenic XMPP users:
@@ -108,7 +209,7 @@ From fellow insane and schizophrenic XMPP users:
## επιπλέον
(extra)
Pi version numbers are the digits of Pi followed by a letter indicating the phase of development the program is in.
pi version numbers are the digits of Pi followed by a letter indicating the phase of development the program is in.
For example, the version string:
@@ -121,3 +222,5 @@ The digits of Pi will reset back to `3` when moving to a new phase.
If the number gets too long, it will reset to one digit of 2π. Once that gets too long, it will be digits of 3π and etc.
Named after [Psi](https://github.com/psi-im/psi).
[1] Android is not a supported platform, however it's possible to compile and run pi for it. If you want to become suicidal, you can go for it, but pi is a primarily PC-oriented client. There are multiple mobile clients that already exist which will give you a substantially better experience.

4016
credits.go Normal file

File diff suppressed because it is too large Load Diff

42
go.mod
View File

@@ -1,50 +1,52 @@
module pi-im
go 1.24.5
go 1.24.6
require (
fyne.io/fyne/v2 v2.6.2
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb
fyne.io/fyne/v2 v2.6.3
fyne.io/x/fyne v0.0.0-20250827163406-39fd826f385e
github.com/makeworld-the-better-one/go-isemoji v1.3.0
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
github.com/shreve/musicwand v0.0.1
mellium.im/xmpp v0.22.0
pain.agency/oasis-sdk v0.0.0-20250809192709-a3e5dff1aa61
pain.agency/oasis-sdk v0.0.0-20250831105702-85385dca3a95
)
require (
fyne.io/systray v1.11.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fredbi/uri v1.1.1 // indirect
github.com/fredbi/uri v1.1.0 // 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
github.com/fyne-io/image v0.1.1 // indirect
github.com/fyne-io/oksvg v0.1.0 // indirect
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
github.com/go-text/render v0.2.0 // indirect
github.com/go-text/typesetting v0.3.0 // indirect
github.com/go-text/typesetting v0.2.1 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
github.com/hack-pad/safejs v0.1.1 // indirect
github.com/hack-pad/safejs v0.1.0 // indirect
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.4.2 // indirect
github.com/rymdport/portal v0.4.1 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/yuin/goldmark v1.7.13 // 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.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/tools v0.36.0 // indirect
github.com/yuin/goldmark v1.7.8 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/image v0.24.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.25.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
mellium.im/reader v0.1.0 // indirect
mellium.im/sasl v0.3.2 // indirect

94
go.sum
View File

@@ -1,17 +1,20 @@
fyne.io/fyne/v2 v2.6.2 h1:RPgwmXWn+EuP/TKwO7w5p73ILVC26qHD9j3CZUZNwgM=
fyne.io/fyne/v2 v2.6.2/go.mod h1:9IJ8uWgzfcMossFoUkLiOrUIEtaDvF4nML114WiCtXU=
fyne.io/fyne/v2 v2.6.3 h1:cvtM2KHeRuH+WhtHiA63z5wJVBkQ9+Ay0UMl9PxFHyA=
fyne.io/fyne/v2 v2.6.3/go.mod h1:NGSurpRElVoI1G3h+ab2df3O5KLGh1CGbsMMcX0bPIs=
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb h1:2BazNmb/kwgqRdvE9L+NgW8sfoWGn3iy1Ox8R4+CSmc=
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb/go.mod h1:u3LF1EkElytjOT8OHxft16trctGndF9qpsoH6YIDOUU=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
fyne.io/x/fyne v0.0.0-20250827163406-39fd826f385e h1:oJM+HGkpSuq1J+JqUq/jo7KPrKj2K2/VIZlyus04w3Y=
fyne.io/x/fyne v0.0.0-20250827163406-39fd826f385e/go.mod h1:u3LF1EkElytjOT8OHxft16trctGndF9qpsoH6YIDOUU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o=
github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
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=
@@ -24,34 +27,35 @@ github.com/fyne-io/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw=
github.com/fyne-io/oksvg v0.1.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 h1:RkGhqHxEVAvPM0/R+8g7XRwQnHatO0KAuVcwHo8q9W8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728/go.mod h1:SyRD8YfuKk+ZXlDqYiqe1qMSqjNgtHzBTG810KUagMc=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4=
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY=
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A=
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/makeworld-the-better-one/go-isemoji v1.3.0 h1:vrxfd0W0Xs8t7BnIYXkvSK7m+GDk/0x1ClXPbWjQ5A0=
github.com/makeworld-the-better-one/go-isemoji v1.3.0/go.mod h1:FBjkPl9rr0G4vlZCc+Mr+QcnOfGCTbGWYW8/1sp06I0=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
@@ -60,35 +64,45 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
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/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/shreve/musicwand v0.0.1 h1:uF1GDmk6b9xZPAGJU3iWBSqKruyVeyVs/cqM6YzaJ3k=
github.com/shreve/musicwand v0.0.1/go.mod h1:y7oBkMLTfGmpIxoKQFwXlzWLtffcQIpX/1no+iNPxwQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.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.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.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=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
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=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mellium.im/reader v0.1.0 h1:UUEMev16gdvaxxZC7fC08j7IzuDKh310nB6BlwnxTww=
@@ -99,5 +113,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-20250809192709-a3e5dff1aa61 h1:7zb69SAfLAJhXoqXZaS0pq/p1Y9W19Pm4FjcwWjTVoE=
pain.agency/oasis-sdk v0.0.0-20250809192709-a3e5dff1aa61/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=
pain.agency/oasis-sdk v0.0.0-20250831105702-85385dca3a95 h1:BcB7/hnMnQIU+pERvQRGFMt9i0/o8XCnHbK1kYG3/K4=
pain.agency/oasis-sdk v0.0.0-20250831105702-85385dca3a95/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=

554
main.go
View File

@@ -3,6 +3,7 @@ package main
import (
//core - required
"encoding/xml"
"errors"
"fmt"
_ "image/color"
"io"
@@ -22,20 +23,23 @@ 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"
"github.com/makeworld-the-better-one/go-isemoji"
"github.com/rrivera/identicon"
"github.com/shreve/musicwand/pkg/mpris"
// xmpp - required
"mellium.im/xmpp/disco"
_ "mellium.im/xmpp/disco"
"mellium.im/xmpp/jid"
"mellium.im/xmpp/muc"
_ "mellium.im/xmpp/stanza"
oasisSdk "pain.agency/oasis-sdk"
// gui - optional
// catppuccin "github.com/mbaklor/fyne-catppuccin"
// TODO: integrated theme switcher
)
var version string = "3.142a"
var version string = "3i"
var statBar widget.Label
var chatInfo fyne.Container
var chatSidebar fyne.Container
@@ -105,6 +109,7 @@ type piConfig struct {
Login oasisSdk.LoginInfo
DMs []string
Notifications bool
JoinBookmarks bool
}
var config piConfig
@@ -161,14 +166,18 @@ func CreateUITab(chatJidStr string) ChatTabUI {
content.Wrapping = fyne.TextWrapWord
content.Selectable = true
icon := theme.FileVideoIcon()
replytext := widget.NewLabel("> fallback reply text")
replytext.Hide()
replytext.Importance = widget.SuccessImportance
btn := widget.NewButtonWithIcon("View media", icon, func() {
})
return container.NewVBox(container.NewHBox(ico, author), content, btn)
return container.NewVBox(replytext, container.NewHBox(ico, author), content, btn)
},
func(i widget.ListItemID, co fyne.CanvasObject) {
vbox := co.(*fyne.Container)
authorBox := vbox.Objects[0].(*fyne.Container)
authorBox := vbox.Objects[1].(*fyne.Container)
replytext := vbox.Objects[0].(*widget.Label)
// generate a Icon
gen, _ := identicon.New("github", 5, 3)
@@ -181,8 +190,8 @@ func CreateUITab(chatJidStr string) ChatTabUI {
// Icon generate end
author := authorBox.Objects[1].(*widget.Label)
content := vbox.Objects[1].(*widget.Label)
btn := vbox.Objects[2].(*widget.Button)
content := vbox.Objects[2].(*widget.Label)
btn := vbox.Objects[3].(*widget.Button)
if chatTabs[chatJidStr].Messages[i].Important {
//content.Importance = widget.DangerImportance TODO: Fix highlighting messages with mentions, it's currently broken
}
@@ -199,7 +208,8 @@ func CreateUITab(chatJidStr string) ChatTabUI {
})
return
}
if strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "mp4") || strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "mp3") {
if strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "mp4") || strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "mp3") || strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "gif") || strings.HasSuffix(chatTabs[chatJidStr].Messages[i].ImageURL, "webp") { // FIXME: This code is fucking terrible // TODO: Could check mime?
url, err := url.Parse(chatTabs[chatJidStr].Messages[i].ImageURL)
if err != nil {
fyne.Do(func() {
@@ -232,18 +242,39 @@ func CreateUITab(chatJidStr string) ChatTabUI {
//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()))
reply := chatTabs[chatJidStr].Messages[i].Raw.Reply
guy := jid.MustParse(reply.To).Resourcepart()
// TODO: EXPERIMENTALLY GET REPLIED TO TEXT
for i := len(chatTabs[chatJidStr].Messages) - 1; i >= 0; i-- {
if reply.ID == chatTabs[chatJidStr].Messages[i].Raw.StanzaID.ID {
replytext.Show()
replytext.SetText(fmt.Sprintf("> %s", chatTabs[chatJidStr].Messages[i].Content))
guy = chatTabs[chatJidStr].Messages[i].Author
break
}
}
author.SetText(fmt.Sprintf("%s > %s", chatTabs[chatJidStr].Messages[i].Author, guy))
} else {
author.SetText(chatTabs[chatJidStr].Messages[i].Author)
replytext.Hide()
}
sl := strings.Split(msgContent, " ")
if len(sl) == 1 && isemoji.IsEmoji(sl[0]) {
content.SizeName = fyne.ThemeSizeName(theme.SizeNameHeadingText)
content.Refresh()
}
if sl[0] == "/me" {
author.SetText(author.Text + " " + strings.Join(sl[1:], " "))
content.SetText(" ")
}
scroller.SetItemHeight(i, vbox.MinSize().Height)
vbox.Refresh()
},
)
@@ -287,7 +318,7 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
var icon fyne.Resource
if isMuc {
icon = theme.HomeIcon()
} else{
} else {
icon = theme.AccountIcon()
}
@@ -297,7 +328,7 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
}
func dropToSignInPage(reason string) {
w = a.NewWindow("Welcome to Pi")
w = a.NewWindow("Welcome to pi")
w.Resize(fyne.NewSize(500, 500))
rt := widget.NewRichTextFromMarkdown("# Welcome to pi\nIt appears you do not have a valid account configured. Let's create one!")
footer := widget.NewRichTextFromMarkdown(fmt.Sprintf("Reason for being dropped to the sign-in page:\n\n```%s```", reason))
@@ -367,6 +398,7 @@ func main() {
config = piConfig{}
a = app.NewWithID("pi-im")
//a.Settings().SetTheme(&myTheme{})
reader, err := a.Storage().Open("pi.xml")
if err != nil {
dropToSignInPage(err.Error())
@@ -391,69 +423,16 @@ func main() {
notifications = config.Notifications
client, err := oasisSdk.CreateClient(
&login,
func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) {
fmt.Println(msg)
userJidStr := msg.From.Bare().String()
tab, ok := chatTabs[userJidStr]
fmt.Println(msg.From.String())
if ok {
str := *msg.CleanedBody
if notifications {
a.SendNotification(fyne.NewNotification(fmt.Sprintf("%s says", userJidStr), str))
}
var img string = ""
if strings.Contains(str, "https://") {
lines := strings.Split(str, "\n")
for i, line := range lines {
s := strings.Split(line, " ")
for _, v := range s {
_, err := url.Parse(v)
if err == nil && strings.HasPrefix(v, "https://") {
if strings.HasSuffix(v, ".png") || strings.HasSuffix(v, ".jpg") || strings.HasSuffix(v, ".jpeg") || strings.HasSuffix(v, ".webp") || strings.HasSuffix(v, ".mp4") {
img = v
}
}
}
lines[i] = strings.Join(s, " ")
}
str = strings.Join(lines, " ")
}
var replyID string
if msg.Reply == nil {
replyID = "PICLIENT:UNAVAILABLE"
} else {
replyID = msg.Reply.ID
}
myMessage := Message{
Author: msg.From.Resourcepart(),
Content: str,
ID: msg.ID,
ReplyID: replyID,
Raw: *msg,
ImageURL: img,
}
&login)
tab.Messages = append(tab.Messages, myMessage)
fyne.Do(func() {
UITabs[userJidStr].Scroller.Refresh()
if scrollDownOnNewMessage {
UITabs[userJidStr].Scroller.ScrollToBottom()
}
})
}
},
func(client *oasisSdk.XmppClient, muc *muc.Channel, msg *oasisSdk.XMPPChatMessage) {
// 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" { // Classic history message
//ignore = true
//fmt.Println("ignoring!")
//return //what is blud doing
}
client.SetDmHandler(func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) {
correction := false
userJidStr := msg.From.Bare().String()
tab, ok := chatTabs[userJidStr]
if ok {
str := *msg.CleanedBody
if notifications {
a.SendNotification(fyne.NewNotification(fmt.Sprintf("%s says", userJidStr), str))
}
for _, v := range msg.Unknown {
@@ -463,105 +442,186 @@ func main() {
}
}
var ImageID string = ""
mucJidStr := msg.From.Bare().String()
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 && 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))
}
}
if strings.Contains(str, "https://") {
lines := strings.Split(str, "\n")
for i, line := range lines {
s := strings.Split(line, " ")
for _, v := range s {
_, err := url.Parse(v)
if err == nil && strings.HasPrefix(v, "https://") {
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
}
var img string = ""
if strings.Contains(str, "https://") {
lines := strings.Split(str, "\n")
for i, line := range lines {
s := strings.Split(line, " ")
for _, v := range s {
_, err := url.Parse(v)
if err == nil && strings.HasPrefix(v, "https://") {
if strings.HasSuffix(v, ".png") || strings.HasSuffix(v, ".jpg") || strings.HasSuffix(v, ".jpeg") || strings.HasSuffix(v, ".webp") || strings.HasSuffix(v, ".mp4") || strings.HasSuffix(v, ".gif") {
img = v
}
}
lines[i] = strings.Join(s, " ")
}
str = strings.Join(lines, " ")
fmt.Println(str)
}
fmt.Println(msg.ID)
var replyID string
if msg.Reply == nil {
replyID = "PICLIENT:UNAVAILABLE"
} else {
replyID = msg.Reply.To
lines[i] = strings.Join(s, " ")
}
str = strings.Join(lines, " ")
}
var replyID string
if msg.Reply == nil {
replyID = "PICLIENT:UNAVAILABLE"
} else {
replyID = msg.Reply.ID
}
if correction {
for i := len(tab.Messages) - 1; i > 0; i-- {
if tab.Messages[i].Raw.From.String() == msg.From.String() {
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
fyne.Do(func() {
UITabs[mucJidStr].Scroller.Refresh()
})
return
if correction {
for i := len(tab.Messages) - 1; i > 0; i-- {
if tab.Messages[i].Raw.From.String() == msg.From.String() {
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
fyne.Do(func() {
UITabs[userJidStr].Scroller.Refresh()
})
return
}
}
}
myMessage := Message{
Author: msg.From.Resourcepart(),
Content: str,
ID: msg.ID,
ReplyID: replyID,
Raw: *msg,
ImageURL: img,
}
tab.Messages = append(tab.Messages, myMessage)
fyne.Do(func() {
UITabs[userJidStr].Scroller.Refresh()
if scrollDownOnNewMessage {
UITabs[userJidStr].Scroller.ScrollToBottom()
}
})
}
})
client.SetGroupChatHandler(func(client *oasisSdk.XmppClient, muc *muc.Channel, msg *oasisSdk.XMPPChatMessage) {
// HACK: IGNORING ALL MESSAGES FROM CLASSIC MUC HISTORY IN PREPARATION OF MAM SUPPORT
ignore := false
correction := false
important := false
donotnotify := false
for _, v := range msg.Unknown {
if v.XMLName.Local == "delay" { // Classic history message
donotnotify = true
//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
}
}
var ImageID string = ""
mucJidStr := msg.From.Bare().String()
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 !donotnotify && !ignore && notifications {
if !correction && msg.From.String() != client.JID.String() && strings.Contains(str, login.DisplayName) || (msg.Reply != nil && strings.Contains(msg.Reply.To, login.DisplayName)) {
a.SendNotification(fyne.NewNotification(fmt.Sprintf("Mentioned in %s", mucJidStr), str))
}
}
if strings.Contains(str, "https://") {
lines := strings.Split(str, "\n")
for i, line := range lines {
s := strings.Split(line, " ")
for _, v := range s {
_, err := url.Parse(v)
if err == nil && strings.HasPrefix(v, "https://") {
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") || strings.HasSuffix(v, ".gif") {
ImageID = v
}
}
}
lines[i] = strings.Join(s, " ")
}
str = strings.Join(lines, " ")
fmt.Println(str)
}
fmt.Println(msg.ID)
var replyID string
if msg.Reply == nil {
replyID = "PICLIENT:UNAVAILABLE"
} else {
replyID = msg.Reply.To
}
myMessage := Message{
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() {
UITabs[mucJidStr].Scroller.Refresh()
if scrollDownOnNewMessage {
UITabs[mucJidStr].Scroller.ScrollToBottom()
if correction {
for i := len(tab.Messages) - 1; i > 0; i-- {
if tab.Messages[i].Raw.From.String() == msg.From.String() {
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
fyne.Do(func() {
UITabs[mucJidStr].Scroller.Refresh()
})
return
}
})
}
}
},
func(_ *oasisSdk.XmppClient, from jid.JID, state oasisSdk.ChatState) {
switch state {
case oasisSdk.ChatStateComposing:
fyne.Do(func() {
statBar.SetText(fmt.Sprintf("%s is typing...", from.Resourcepart()))
})
case oasisSdk.ChatStatePaused:
fyne.Do(func() {
statBar.SetText(fmt.Sprintf("%s has stopped typing.", from.Resourcepart()))
})
case oasisSdk.ChatStateInactive:
fyne.Do(func() {
statBar.SetText(fmt.Sprintf("%s is idle", from.Resourcepart()))
})
case oasisSdk.ChatStateGone:
fyne.Do(func() {
statBar.SetText(fmt.Sprintf("%s is gone", from.Resourcepart()))
})
default:
fyne.Do(func() {
statBar.SetText("")
})
myMessage := Message{
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() {
UITabs[mucJidStr].Scroller.Refresh()
if scrollDownOnNewMessage {
UITabs[mucJidStr].Scroller.ScrollToBottom()
}
})
}
})
client.SetChatstateHandler(func(_ *oasisSdk.XmppClient, from jid.JID, state oasisSdk.ChatState) {
switch state {
case oasisSdk.ChatStateComposing:
fyne.Do(func() {
statBar.SetText(fmt.Sprintf("%s is typing...", from.Resourcepart()))
})
case oasisSdk.ChatStatePaused:
fyne.Do(func() {
statBar.SetText(fmt.Sprintf("%s has stopped typing.", from.Resourcepart()))
})
case oasisSdk.ChatStateInactive:
fyne.Do(func() {
statBar.SetText(fmt.Sprintf("%s is idle", from.Resourcepart()))
})
case oasisSdk.ChatStateGone:
fyne.Do(func() {
statBar.SetText(fmt.Sprintf("%s is gone", from.Resourcepart()))
})
default:
fyne.Do(func() {
statBar.SetText("")
})
}
})
client.SetDeliveryReceiptHandler(
func(_ *oasisSdk.XmppClient, from jid.JID, id string) {
fmt.Printf("Delivered %s to %s", id, from.String())
},
})
client.SetReadReceiptHandler(
func(_ *oasisSdk.XmppClient, from jid.JID, id string) {
for _, tab := range chatTabs {
for i := len(tab.Messages) - 1; i >= 0; i-- {
@@ -576,8 +636,8 @@ func main() {
}
}
fmt.Printf("%s has seen %s\n", from.String(), id)
},
)
})
if err != nil {
log.Fatalln("Could not create client - " + err.Error())
}
@@ -600,7 +660,7 @@ func main() {
}()
a = app.New()
//a.Settings().SetTheme(myTheme{})
//a.Settings().SetTheme(&myTheme{})
w = a.NewWindow("pi")
w.Resize(fyne.NewSize(500, 500))
@@ -647,12 +707,15 @@ func main() {
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
dialog.ShowConfirm("Confirm", "Do you want to embed this link into your message?", func(b bool) {
if b {
err = client.SendSingleFileMessage(jid.MustParse(activeMucJid).Bare(), url.String(), nil)
if err != nil {
dialog.ShowError(err, w)
}
return
}
}, w)
}
err = client.SendText(jid.MustParse(activeMucJid).Bare(), text)
@@ -691,6 +754,10 @@ func main() {
dialog.ShowInformation("about pi", fmt.Sprintf("the XMPP client from hell\n\npi is an experimental XMPP client\nwritten by Sunglocto in Go.\n\nVersion %s", version), w)
})
licensesbtn := fyne.NewMenuItem("license", func() {
CreditsWindow(fyne.CurrentApp(), fyne.NewSize(800, 400)).Show()
})
reconnect := fyne.NewMenuItem("reconnect", func() {
go func() {
err := client.Connect()
@@ -735,6 +802,7 @@ func main() {
return
}
selectedScroller.ScrollToBottom()
selectedScroller.Refresh()
})
jtt := fyne.NewMenuItem("jump to top", func() {
@@ -743,6 +811,7 @@ func main() {
return
}
selectedScroller.ScrollToTop()
selectedScroller.Refresh()
})
w.SetOnDropped(func(p fyne.Position, u []fyne.URI) {
@@ -916,31 +985,66 @@ func main() {
})
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
}
/*
servDisc := fyne.NewMenuItem("Disco features", func() {
//var search jid.JID
dialog.ShowEntryDialog("Disco features", "JID: ", func(s string) { // TODO: replace with undeprecated widgetd
d := dialog.NewCustom("Please wait", "Close", widget.NewLabel("..."), w)
d.Show()
go func() {
//search, err = jid.Parse(s)
//if err != nil {
// d.Hide()
// dialog.ShowError(err, w)
// return
//}
txt := `<iq from='ringen@muc.isekai.rocks/sunglocto'
to='ringen@muc.iskai.rocks/snit'
type='get'
id='vc2'>
<vCard xmlns='vcard-temp'/>
</iq>`
var stan stanza.IQ
xml.Unmarshal([]byte(txt), &stan)
if err != nil {
d.Hide()
dialog.ShowError(err, w)
return
}
r, err := client.Session.EncodeIQ(client.Ctx, stan)
if err != nil {
d.Hide()
dialog.ShowError(err, w)
return
}
ra, _ := r.Token()
t, _ := xml.MarshalIndent(ra, "", "\t")
fmt.Println(string(t))
d.Hide()
/*
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()
}
myBox := container.NewGridWithColumns(1, widget.NewLabel("Items"))
info, err := disco.GetInfo(client.Ctx, "", search, client.Session)
if err != nil {
d.Hide()
dialog.ShowError(err, w)
return
}
m := info.Identity
bytes, err := xml.MarshalIndent(m, "", "\t")
if err != nil {
d.Hide()
dialog.ShowError(err, w)
return
}
fyne.Do(func() {d.Hide()})
myBox.Objects = append(myBox.Objects, widget.NewLabel(string(bytes)))
dialog.ShowCustom("Service discovery", "cancel", myBox, w)
}()
dialog.ShowCustom("Features", "cancel", myBox, w)
}, w)
})
}, w)
})
*/
savedata := fyne.NewMenuItem("DEBUG: Save tab data to disk", func() {
d := []ChatTab{}
@@ -955,9 +1059,34 @@ func main() {
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)
jbookmarks := fyne.NewMenuItem("Join rooms in bookmarks", func() {
// FIXME: Race condition
client.FetchBookmarks()
rooms := client.BookmarkCache()
for _, v := range rooms {
go func() {
if v.Autojoin == true {
joinjid, err := v.JID.WithResource(login.DisplayName)
if err != nil {
dialog.ShowError(err, w)
return
}
room, err := client.MucClient.Join(client.Ctx, joinjid, client.Session)
if err != nil {
dialog.ShowError(err, w)
return
}
client.MucChannels[v.JID.String()] = room
addChatTab(true, v.JID, login.DisplayName)
}
}()
}
})
menu_help := fyne.NewMenu("π", mit, reconnect, licensesbtn, savedata)
menu_changeroom := fyne.NewMenu("Α", mic, beginADM, joinARoom, leaveRoom, jbookmarks)
menu_configureview := fyne.NewMenu("Β", mia, mis, jtt, jtb)
hafjag := fyne.NewMenuItem("Hafjag", func() {
entry.Text = "Hafjag"
@@ -994,7 +1123,42 @@ func main() {
entry.Text = fmt.Sprintf("It is currently %s", time.Now().Format(time.RFC850))
SendCallback()
})
menu_jokes := fyne.NewMenu("Δ", mycurrenttime, hafjag, hotfuck, agree)
mycurrentplayingsong := fyne.NewMenuItem("Get currently playing song", func() {
client, err := mpris.NewClient()
if err != nil {
dialog.ShowError(err, w)
return
}
present := false
for _, player := range client.Players() {
fmt.Println(player.RawMetadata())
old := entry.Text
newtext := ""
title, t_ok := player.RawMetadata()["xesam:title"]
artist, a_ok := player.RawMetadata()["xesam:artist"]
if t_ok && a_ok {
newtext = fmt.Sprintf("I'm currently listening to %s by %s", title.String(), artist.String())
} else if t_ok {
newtext = fmt.Sprintf("I'm currently listening to %s", title.String())
} else if a_ok {
newtext = fmt.Sprintf("I'm currently listening to a song by %s", artist.String())
} else {
dialog.ShowError(errors.New("error: There's a playing song, but we could not get the artist or title information."), w)
return
}
entry.SetText(newtext)
SendCallback()
entry.SetText(old)
present = true
}
if !present {
dialog.ShowInformation("Failed", "Could not find any open players. You might need an MPRIS plugin for players such as mpv.\nSee the MPRIS ArchWiki article for more information:\nhttps://wiki.archlinux.org/title/MPRIS", w)
}
})
menu_jokes := fyne.NewMenu("Δ", mycurrenttime, hafjag, hotfuck, agree, mycurrentplayingsong)
bit := fyne.NewMenuItem("mark selected message as read", func() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
@@ -1017,8 +1181,7 @@ func main() {
})
bic := fyne.NewMenuItem("show message XML", func() {
pre := widget.NewMultiLineEntry()
pre := widget.NewLabel("")
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
return
@@ -1039,11 +1202,12 @@ func main() {
return
}
pre.SetText(string(bytes))
pre.Selectable = true
pre.Refresh()
dialog.ShowCustom("Message", "Close", pre, w)
})
red := fyne.NewMenuItem("show people who have read this message", func() {
red := fyne.NewMenuItem("show read receipts on message", func() {
pre := container.NewVBox()
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)