Compare commits
21 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 |
29
README.md
29
README.md
@@ -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)
|
||||
|
18
go.mod
18
go.mod
@@ -7,14 +7,14 @@ require (
|
||||
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
|
||||
@@ -37,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
|
||||
|
36
go.sum
36
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/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=
|
||||
@@ -70,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=
|
||||
@@ -99,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=
|
||||
|
301
main.go
301
main.go
@@ -4,9 +4,10 @@ import (
|
||||
//core - required
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"image/color"
|
||||
_ "image/color"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand/v2"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -22,24 +23,25 @@ import (
|
||||
"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.14a"
|
||||
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
|
||||
|
||||
@@ -51,6 +53,7 @@ type Message struct {
|
||||
ImageURL string
|
||||
Raw oasisSdk.XMPPChatMessage
|
||||
Important bool
|
||||
Readers []jid.JID
|
||||
}
|
||||
|
||||
type ChatTab struct {
|
||||
@@ -68,6 +71,36 @@ type ChatTabUI struct {
|
||||
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 {
|
||||
Login oasisSdk.LoginInfo
|
||||
DMs []string
|
||||
@@ -87,26 +120,24 @@ 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
|
||||
@@ -160,25 +191,33 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
||||
if chatTabs[chatJidStr].Messages[i].ImageURL != "" {
|
||||
btn.Hidden = false
|
||||
btn.OnTapped = func() {
|
||||
fyne.Do(func() {
|
||||
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(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
|
||||
@@ -198,11 +237,9 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
||||
author.SetText(chatTabs[chatJidStr].Messages[i].Author)
|
||||
}
|
||||
|
||||
|
||||
if strings.Split(msgContent," ")[0] == "/me" {
|
||||
sl := strings.Split(msgContent, " ")
|
||||
sl[0] = ""
|
||||
author.SetText(author.Text + strings.Join(sl, " "))
|
||||
sl := strings.Split(msgContent, " ")
|
||||
if sl[0] == "/me" {
|
||||
author.SetText(author.Text + " " + strings.Join(sl[1:], " "))
|
||||
content.SetText(" ")
|
||||
}
|
||||
|
||||
@@ -218,11 +255,11 @@ func CreateUITab(chatJidStr string) 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
|
||||
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)
|
||||
|
||||
return myUITab
|
||||
@@ -247,9 +284,15 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
||||
|
||||
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.NewTabItem(chatJid.String(), myUITab.Scroller))
|
||||
AppTabs.Append(container.NewTabItemWithIcon(chatJid.String(), icon, myUITab.Scroller))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -406,15 +449,17 @@ func main() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,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() {
|
||||
@@ -518,7 +563,19 @@ 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 {
|
||||
@@ -543,14 +600,14 @@ 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
|
||||
@@ -580,6 +637,7 @@ func main() {
|
||||
go func() {
|
||||
if replying {
|
||||
m := chatTabs[activeMucJid].Messages[selectedId].Raw
|
||||
fmt.Println(selectedId)
|
||||
err = client.ReplyToEvent(&m, text)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
@@ -589,7 +647,8 @@ 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.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)
|
||||
}
|
||||
@@ -618,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
|
||||
@@ -715,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
|
||||
@@ -775,6 +836,86 @@ func main() {
|
||||
}, w)
|
||||
})
|
||||
|
||||
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 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
|
||||
@@ -784,7 +925,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
myBox := container.NewGridWithColumns(1, widget.NewLabel("Items\na\na\na\na\na"))
|
||||
myBox := container.NewGridWithColumns(1, widget.NewLabel("Items"))
|
||||
info, err := disco.GetInfo(client.Ctx, "", search, client.Session)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
@@ -815,7 +956,8 @@ func main() {
|
||||
os.WriteFile("text.xml", b, os.ModeAppend)
|
||||
})
|
||||
menu_help := fyne.NewMenu("π", mit, reconnect, savedata)
|
||||
menu_changeroom := fyne.NewMenu("Α", mic, servDisc)
|
||||
|
||||
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"
|
||||
@@ -824,16 +966,35 @@ func main() {
|
||||
})
|
||||
|
||||
hotfuck := fyne.NewMenuItem("Hot Fuck", func() {
|
||||
entry.Text = "Hot Fuck"
|
||||
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 = "Oh Yeah."
|
||||
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)
|
||||
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 {
|
||||
@@ -856,7 +1017,7 @@ func main() {
|
||||
})
|
||||
|
||||
bic := fyne.NewMenuItem("show message XML", func() {
|
||||
pre := widget.NewLabel("")
|
||||
pre := widget.NewMultiLineEntry()
|
||||
|
||||
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||
if !ok {
|
||||
@@ -878,11 +1039,47 @@ 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)
|
||||
|
||||
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)
|
||||
|
||||
@@ -924,7 +1121,7 @@ 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()))
|
||||
}
|
||||
@@ -938,6 +1135,6 @@ func main() {
|
||||
// HACK - disable chatsidebar because it's currently very buggy
|
||||
chatSidebar.Hidden = true
|
||||
statBar.SetText("")
|
||||
w.SetContent(container.NewVSplit(container.NewVSplit(AppTabs, container.NewHSplit(entry, sendbtn)), container.NewHSplit(&statBar, &chatInfo)))
|
||||
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
12
piloadinggif.go
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user