Compare commits
20 Commits
ed559c09c4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ff121b42d9 | |||
| d00f266a4e | |||
| 934d06b6b0 | |||
| d612d16e52 | |||
| e9c1f8dbe1 | |||
| 7ec143bc98 | |||
| adf86896e9 | |||
| 3f157d06c7 | |||
| f4d3f76841 | |||
| d2ec518846 | |||
| d63fc073de | |||
| 0c26b7a8cf | |||
| d45ed57572 | |||
| c98cbe81ed | |||
| ea47f10b5c | |||
| 91215094e4 | |||
| 082d2757f6 | |||
| 596bd6bdba | |||
| 221ee9dcd9 | |||
| dc8e2632f4 |
36
.gitea/issue_template.yml
Normal file
36
.gitea/issue_template.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: File a bug report.
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels: ["bug"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
- type: textarea
|
||||||
|
id: what-happened
|
||||||
|
attributes:
|
||||||
|
label: What happened?
|
||||||
|
description: Also tell us, what did you expect to happen?
|
||||||
|
placeholder: Tell us what you see!
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
description: What version of our software are you running?
|
||||||
|
placeholder: Latest commit
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: platform
|
||||||
|
attributes:
|
||||||
|
label: What platform are you seeing the problem on?
|
||||||
|
placeholder: Arch Linux
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant log output
|
||||||
|
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||||
|
render: shell
|
||||||
@@ -32,8 +32,6 @@ If you want to add MUCs or DMs, you must configure the program by editing the pi
|
|||||||
<DisplayName>sunglocto</DisplayName>
|
<DisplayName>sunglocto</DisplayName>
|
||||||
<TLSoff>false</TLSoff>
|
<TLSoff>false</TLSoff>
|
||||||
<StartTLS>true</StartTLS>
|
<StartTLS>true</StartTLS>
|
||||||
<MucsToJoin>room1@muc.example.com</MucsToJoin>
|
|
||||||
<MucsToJoin>room2@muc.example.com</MucsToJoin>
|
|
||||||
</Login>
|
</Login>
|
||||||
<Notifications>true</Notifications>
|
<Notifications>true</Notifications>
|
||||||
<DMs>person1@example.com</DMs>
|
<DMs>person1@example.com</DMs>
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -1,15 +1,15 @@
|
|||||||
module pi-im
|
module pi-im
|
||||||
|
|
||||||
go 1.24.6
|
go 1.25.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/fyne/v2 v2.6.3
|
fyne.io/fyne/v2 v2.6.3
|
||||||
fyne.io/x/fyne v0.0.0-20250827163406-39fd826f385e
|
fyne.io/x/fyne v0.0.0-20250910205345-ecc79984d005
|
||||||
github.com/makeworld-the-better-one/go-isemoji v1.3.0
|
github.com/makeworld-the-better-one/go-isemoji v1.3.0
|
||||||
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
|
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
|
||||||
github.com/shreve/musicwand v0.0.1
|
github.com/shreve/musicwand v0.0.1
|
||||||
mellium.im/xmpp v0.22.0
|
mellium.im/xmpp v0.22.0
|
||||||
pain.agency/oasis-sdk v0.0.0-20250831105702-85385dca3a95
|
pain.agency/oasis-sdk v0.0.0-20250918002549-5a45c8afedcd
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -2,8 +2,8 @@ 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/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 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
|
||||||
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
|
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
|
||||||
fyne.io/x/fyne v0.0.0-20250827163406-39fd826f385e h1:oJM+HGkpSuq1J+JqUq/jo7KPrKj2K2/VIZlyus04w3Y=
|
fyne.io/x/fyne v0.0.0-20250910205345-ecc79984d005 h1:CmdApAnt07juL0dhcFReFGpADUdRjjm0eDVJDS01uKE=
|
||||||
fyne.io/x/fyne v0.0.0-20250827163406-39fd826f385e/go.mod h1:u3LF1EkElytjOT8OHxft16trctGndF9qpsoH6YIDOUU=
|
fyne.io/x/fyne v0.0.0-20250910205345-ecc79984d005/go.mod h1:kQFmF5meMIXnyCioLoCrXol5opruSS/PHYGKMBIE3SU=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
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 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
@@ -113,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/xmlstream v0.15.4/go.mod h1:yXaCW2++fmVO4L9piKVkyLDqnCmictVYF7FDQW8prb4=
|
||||||
mellium.im/xmpp v0.22.0 h1:UthQVSwEAr7SNrmyc90c2ykGpVHxjn/3yw8Ey4+Im8s=
|
mellium.im/xmpp v0.22.0 h1:UthQVSwEAr7SNrmyc90c2ykGpVHxjn/3yw8Ey4+Im8s=
|
||||||
mellium.im/xmpp v0.22.0/go.mod h1:WSjq12nhREFD88Vy/0WD6Q8inE8t6a8w7QjzwivWitw=
|
mellium.im/xmpp v0.22.0/go.mod h1:WSjq12nhREFD88Vy/0WD6Q8inE8t6a8w7QjzwivWitw=
|
||||||
pain.agency/oasis-sdk v0.0.0-20250831105702-85385dca3a95 h1:BcB7/hnMnQIU+pERvQRGFMt9i0/o8XCnHbK1kYG3/K4=
|
pain.agency/oasis-sdk v0.0.0-20250918002549-5a45c8afedcd h1:oLKI4XqaHpJeegwRxRYH9hepFO4GYKCr6C7cLwqXTK8=
|
||||||
pain.agency/oasis-sdk v0.0.0-20250831105702-85385dca3a95/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=
|
pain.agency/oasis-sdk v0.0.0-20250918002549-5a45c8afedcd/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=
|
||||||
|
|||||||
641
main.go
641
main.go
@@ -2,10 +2,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
//core - required
|
//core - required
|
||||||
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "image/color"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/dialog"
|
"fyne.io/fyne/v2/dialog"
|
||||||
|
"fyne.io/fyne/v2/driver/desktop"
|
||||||
"fyne.io/fyne/v2/storage"
|
"fyne.io/fyne/v2/storage"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
@@ -29,21 +30,20 @@ import (
|
|||||||
"github.com/shreve/musicwand/pkg/mpris"
|
"github.com/shreve/musicwand/pkg/mpris"
|
||||||
|
|
||||||
// xmpp - required
|
// xmpp - required
|
||||||
_ "mellium.im/xmpp/disco"
|
"mellium.im/xmpp/bookmarks"
|
||||||
"mellium.im/xmpp/jid"
|
"mellium.im/xmpp/jid"
|
||||||
"mellium.im/xmpp/muc"
|
"mellium.im/xmpp/muc"
|
||||||
_ "mellium.im/xmpp/stanza"
|
|
||||||
oasisSdk "pain.agency/oasis-sdk"
|
oasisSdk "pain.agency/oasis-sdk"
|
||||||
// gui - optional
|
|
||||||
// catppuccin "github.com/mbaklor/fyne-catppuccin"
|
|
||||||
// TODO: integrated theme switcher
|
// TODO: integrated theme switcher
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "3i"
|
var version string = "3.1i"
|
||||||
var statBar widget.Label
|
var statBar widget.Label
|
||||||
var chatInfo fyne.Container
|
var chatInfo fyne.Container
|
||||||
var chatSidebar fyne.Container
|
var chatSidebar fyne.Container
|
||||||
|
var replyNameIcon string = ">"
|
||||||
|
var replyBodyIcon string = ">"
|
||||||
|
var newlineIcon string = " |-> "
|
||||||
var agreesToSendingHotFuckIntoChannel bool = false
|
var agreesToSendingHotFuckIntoChannel bool = false
|
||||||
|
|
||||||
// by sunglocto
|
// by sunglocto
|
||||||
@@ -58,6 +58,7 @@ type Message struct {
|
|||||||
Raw oasisSdk.XMPPChatMessage
|
Raw oasisSdk.XMPPChatMessage
|
||||||
Important bool
|
Important bool
|
||||||
Readers []jid.JID
|
Readers []jid.JID
|
||||||
|
Reactions map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatTab struct {
|
type ChatTab struct {
|
||||||
@@ -67,6 +68,7 @@ type ChatTab struct {
|
|||||||
isMuc bool
|
isMuc bool
|
||||||
Muc *muc.Channel
|
Muc *muc.Channel
|
||||||
UpdateSidebar bool
|
UpdateSidebar bool
|
||||||
|
Members map[string]oasisSdk.UserPresence
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatTabUI struct {
|
type ChatTabUI struct {
|
||||||
@@ -79,6 +81,21 @@ type CustomMultiLineEntry struct {
|
|||||||
widget.Entry
|
widget.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isUTF8Locale() bool {
|
||||||
|
localeVars := []string{"LC_ALL", "LC_CTYPE", "LANG"}
|
||||||
|
for _, envVar := range localeVars {
|
||||||
|
value := os.Getenv(envVar)
|
||||||
|
if strings.Contains(strings.ToLower(value), "utf-8") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isWindows() bool {
|
||||||
|
return os.PathSeparator == '\\' && os.PathListSeparator == ';'
|
||||||
|
}
|
||||||
|
|
||||||
func NewCustomMultiLineEntry() *CustomMultiLineEntry {
|
func NewCustomMultiLineEntry() *CustomMultiLineEntry {
|
||||||
entry := &CustomMultiLineEntry{}
|
entry := &CustomMultiLineEntry{}
|
||||||
entry.ExtendBaseWidget(entry)
|
entry.ExtendBaseWidget(entry)
|
||||||
@@ -119,31 +136,12 @@ var DMs []string
|
|||||||
var chatTabs = make(map[string]*ChatTab)
|
var chatTabs = make(map[string]*ChatTab)
|
||||||
var UITabs = make(map[string]*ChatTabUI)
|
var UITabs = make(map[string]*ChatTabUI)
|
||||||
|
|
||||||
var AppTabs *container.AppTabs
|
var AppTabs *container.DocTabs
|
||||||
var selectedId widget.ListItemID
|
var selectedId widget.ListItemID
|
||||||
var replying bool = false
|
var replying bool = false
|
||||||
var notifications bool
|
var notifications bool
|
||||||
var connection bool = true
|
var connection bool = true
|
||||||
|
|
||||||
/*
|
|
||||||
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, 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)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
var scrollDownOnNewMessage bool = true
|
var scrollDownOnNewMessage bool = true
|
||||||
var w fyne.Window
|
var w fyne.Window
|
||||||
var a fyne.App
|
var a fyne.App
|
||||||
@@ -162,17 +160,24 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
|||||||
ico.FillMode = canvas.ImageFillOriginal
|
ico.FillMode = canvas.ImageFillOriginal
|
||||||
author := widget.NewLabel("author")
|
author := widget.NewLabel("author")
|
||||||
author.TextStyle.Bold = true
|
author.TextStyle.Bold = true
|
||||||
|
author.Selectable = true
|
||||||
content := widget.NewLabel("content")
|
content := widget.NewLabel("content")
|
||||||
content.Wrapping = fyne.TextWrapWord
|
content.Wrapping = fyne.TextWrapWord
|
||||||
content.Selectable = true
|
content.Selectable = true
|
||||||
|
content.Importance = widget.MediumImportance
|
||||||
|
//content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
|
||||||
icon := theme.FileVideoIcon()
|
icon := theme.FileVideoIcon()
|
||||||
replytext := widget.NewLabel(">fallback reply text")
|
replytext := widget.NewLabel(">fallback reply text")
|
||||||
replytext.Hide()
|
replytext.Hide()
|
||||||
replytext.Importance = widget.SuccessImportance
|
replytext.Importance = widget.SuccessImportance
|
||||||
|
replytext.Selectable = true
|
||||||
btn := widget.NewButtonWithIcon("View media", icon, func() {
|
btn := widget.NewButtonWithIcon("View media", icon, func() {
|
||||||
|
|
||||||
})
|
})
|
||||||
return container.NewVBox(replytext, container.NewHBox(ico, author), content, btn)
|
|
||||||
|
reactions := container.NewHBox()
|
||||||
|
|
||||||
|
return container.NewVBox(replytext, container.NewHBox(ico, author), content, btn, reactions)
|
||||||
},
|
},
|
||||||
func(i widget.ListItemID, co fyne.CanvasObject) {
|
func(i widget.ListItemID, co fyne.CanvasObject) {
|
||||||
vbox := co.(*fyne.Container)
|
vbox := co.(*fyne.Container)
|
||||||
@@ -192,16 +197,25 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
|||||||
author := authorBox.Objects[1].(*widget.Label)
|
author := authorBox.Objects[1].(*widget.Label)
|
||||||
content := vbox.Objects[2].(*widget.Label)
|
content := vbox.Objects[2].(*widget.Label)
|
||||||
btn := vbox.Objects[3].(*widget.Button)
|
btn := vbox.Objects[3].(*widget.Button)
|
||||||
|
reactions := vbox.Objects[4].(*fyne.Container)
|
||||||
|
reactions = container.NewVBox()
|
||||||
|
|
||||||
|
for _, reaction := range chatTabs[chatJidStr].Messages[i].Reactions {
|
||||||
|
reactions.Add(widget.NewLabel(reaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
reactions.Refresh()
|
||||||
|
|
||||||
if chatTabs[chatJidStr].Messages[i].Important {
|
if chatTabs[chatJidStr].Messages[i].Important {
|
||||||
//content.Importance = widget.DangerImportance TODO: Fix highlighting messages with mentions, it's currently broken
|
content.Importance = widget.DangerImportance
|
||||||
}
|
}
|
||||||
btn.Hidden = true // Hide by default
|
btn.Hidden = true // Hide by default
|
||||||
msgContent := chatTabs[chatJidStr].Messages[i].Content
|
msgContent := chatTabs[chatJidStr].Messages[i].Content
|
||||||
if chatTabs[chatJidStr].Messages[i].ImageURL != "" {
|
if chatTabs[chatJidStr].Messages[i].Raw.OutOfBandMedia != nil {
|
||||||
btn.Hidden = false
|
btn.Hidden = false
|
||||||
btn.OnTapped = func() {
|
btn.OnTapped = func() {
|
||||||
go func() {
|
go func() {
|
||||||
u, err := storage.ParseURI(chatTabs[chatJidStr].Messages[i].ImageURL)
|
u, err := storage.ParseURI(chatTabs[chatJidStr].Messages[i].Raw.OutOfBandMedia.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
@@ -210,7 +224,7 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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?
|
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)
|
url, err := url.Parse(chatTabs[chatJidStr].Messages[i].Raw.OutOfBandMedia.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
@@ -249,13 +263,13 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
|||||||
for i := len(chatTabs[chatJidStr].Messages) - 1; i >= 0; i-- {
|
for i := len(chatTabs[chatJidStr].Messages) - 1; i >= 0; i-- {
|
||||||
if reply.ID == chatTabs[chatJidStr].Messages[i].Raw.StanzaID.ID {
|
if reply.ID == chatTabs[chatJidStr].Messages[i].Raw.StanzaID.ID {
|
||||||
replytext.Show()
|
replytext.Show()
|
||||||
replytext.SetText(fmt.Sprintf(">%s", chatTabs[chatJidStr].Messages[i].Content))
|
replytext.SetText(fmt.Sprintf("%s %s", replyBodyIcon, strings.ReplaceAll(chatTabs[chatJidStr].Messages[i].Content, "\n", newlineIcon)))
|
||||||
guy = chatTabs[chatJidStr].Messages[i].Author
|
guy = chatTabs[chatJidStr].Messages[i].Author
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
author.SetText(fmt.Sprintf("%s > %s", chatTabs[chatJidStr].Messages[i].Author, guy))
|
author.SetText(fmt.Sprintf("%s %s %s", chatTabs[chatJidStr].Messages[i].Author, replyNameIcon, guy))
|
||||||
} else {
|
} else {
|
||||||
author.SetText(chatTabs[chatJidStr].Messages[i].Author)
|
author.SetText(chatTabs[chatJidStr].Messages[i].Author)
|
||||||
replytext.Hide()
|
replytext.Hide()
|
||||||
@@ -274,7 +288,13 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scroller.SetItemHeight(i, vbox.MinSize().Height)
|
scroller.SetItemHeight(i, vbox.MinSize().Height)
|
||||||
|
scroller.CreateItem().Refresh()
|
||||||
vbox.Refresh()
|
vbox.Refresh()
|
||||||
|
/*
|
||||||
|
fyne.Do(func() {
|
||||||
|
scroller.RefreshItem(i)
|
||||||
|
})
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -308,6 +328,7 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
|||||||
Nick: nick,
|
Nick: nick,
|
||||||
Messages: []Message{},
|
Messages: []Message{},
|
||||||
isMuc: isMuc,
|
isMuc: isMuc,
|
||||||
|
Members: make(map[string]oasisSdk.UserPresence),
|
||||||
}
|
}
|
||||||
|
|
||||||
myUITab := CreateUITab(chatJid.String())
|
myUITab := CreateUITab(chatJid.String())
|
||||||
@@ -323,7 +344,8 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
AppTabs.Append(container.NewTabItemWithIcon(chatJid.String(), icon, myUITab.Scroller))
|
myTab := container.NewTabItemWithIcon(chatJid.String(), icon, myUITab.Scroller)
|
||||||
|
AppTabs.Append(myTab)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,10 +417,8 @@ func dropToSignInPage(reason string) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
muc.Since(time.Now())
|
muc.Since(time.Now())
|
||||||
|
|
||||||
config = piConfig{}
|
config = piConfig{}
|
||||||
a = app.NewWithID("pi-im")
|
a = app.NewWithID("pi-im")
|
||||||
//a.Settings().SetTheme(&myTheme{})
|
|
||||||
reader, err := a.Storage().Open("pi.xml")
|
reader, err := a.Storage().Open("pi.xml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dropToSignInPage(err.Error())
|
dropToSignInPage(err.Error())
|
||||||
@@ -422,12 +442,18 @@ func main() {
|
|||||||
login = config.Login
|
login = config.Login
|
||||||
notifications = config.Notifications
|
notifications = config.Notifications
|
||||||
|
|
||||||
client, err := oasisSdk.CreateClient(
|
if isUTF8Locale() {
|
||||||
&login)
|
replyBodyIcon = "↱"
|
||||||
|
replyNameIcon = "→ "
|
||||||
|
newlineIcon = " ⮡ "
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := oasisSdk.CreateClient(&login)
|
||||||
|
|
||||||
client.SetDmHandler(func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) {
|
client.SetDmHandler(func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) {
|
||||||
correction := false
|
correction := false
|
||||||
userJidStr := msg.From.Bare().String()
|
userJidStr := msg.From.Bare().String()
|
||||||
|
fmt.Println(userJidStr)
|
||||||
tab, ok := chatTabs[userJidStr]
|
tab, ok := chatTabs[userJidStr]
|
||||||
if ok {
|
if ok {
|
||||||
str := *msg.CleanedBody
|
str := *msg.CleanedBody
|
||||||
@@ -463,13 +489,14 @@ func main() {
|
|||||||
if msg.Reply == nil {
|
if msg.Reply == nil {
|
||||||
replyID = "PICLIENT:UNAVAILABLE"
|
replyID = "PICLIENT:UNAVAILABLE"
|
||||||
} else {
|
} else {
|
||||||
|
fmt.Println("Received reply in DM")
|
||||||
replyID = msg.Reply.ID
|
replyID = msg.Reply.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if correction {
|
if correction {
|
||||||
for i := len(tab.Messages) - 1; i > 0; i-- {
|
for i := len(tab.Messages) - 1; i > 0; i-- {
|
||||||
if tab.Messages[i].Raw.From.String() == msg.From.String() {
|
if tab.Messages[i].Raw.From.String() == msg.From.String() {
|
||||||
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
|
tab.Messages[i].Content = *msg.CleanedBody + " (corrected)"
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
UITabs[userJidStr].Scroller.Refresh()
|
UITabs[userJidStr].Scroller.Refresh()
|
||||||
})
|
})
|
||||||
@@ -485,21 +512,22 @@ func main() {
|
|||||||
ReplyID: replyID,
|
ReplyID: replyID,
|
||||||
Raw: *msg,
|
Raw: *msg,
|
||||||
ImageURL: img,
|
ImageURL: img,
|
||||||
|
Reactions: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.Messages = append(tab.Messages, myMessage)
|
tab.Messages = append(tab.Messages, myMessage)
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
UITabs[userJidStr].Scroller.Refresh()
|
//UITabs[userJidStr].Scroller.Refresh()
|
||||||
if scrollDownOnNewMessage {
|
if scrollDownOnNewMessage {
|
||||||
UITabs[userJidStr].Scroller.ScrollToBottom()
|
//UITabs[userJidStr].Scroller.ScrollToBottom()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
client.SetGroupChatHandler(func(client *oasisSdk.XmppClient, muc *muc.Channel, msg *oasisSdk.XMPPChatMessage) {
|
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
|
ignore := false
|
||||||
|
reaction := false
|
||||||
correction := false
|
correction := false
|
||||||
important := false
|
important := false
|
||||||
donotnotify := false
|
donotnotify := false
|
||||||
@@ -517,15 +545,21 @@ func main() {
|
|||||||
correction = true
|
correction = true
|
||||||
break // dont need to look at more fields
|
break // dont need to look at more fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.XMLName.Local == "reactions" {
|
||||||
|
reaction = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ImageID string = ""
|
var ImageID string = ""
|
||||||
mucJidStr := msg.From.Bare().String()
|
mucJidStr := msg.From.Bare().String()
|
||||||
if tab, ok := chatTabs[mucJidStr]; ok {
|
if tab, ok := chatTabs[mucJidStr]; ok {
|
||||||
|
chatInfo.Objects[0] = widget.NewLabel(fmt.Sprintf("[!] %s", mucJidStr))
|
||||||
chatTabs[mucJidStr].Muc = muc
|
chatTabs[mucJidStr].Muc = muc
|
||||||
str := *msg.CleanedBody
|
str := *msg.CleanedBody
|
||||||
if strings.Contains(str, login.DisplayName) {
|
if strings.Contains(str, login.DisplayName) {
|
||||||
fmt.Println(str)
|
fmt.Println(str, login.DisplayName)
|
||||||
important = true
|
important = true
|
||||||
}
|
}
|
||||||
if !donotnotify && !ignore && notifications {
|
if !donotnotify && !ignore && notifications {
|
||||||
@@ -548,9 +582,7 @@ func main() {
|
|||||||
lines[i] = strings.Join(s, " ")
|
lines[i] = strings.Join(s, " ")
|
||||||
}
|
}
|
||||||
str = strings.Join(lines, " ")
|
str = strings.Join(lines, " ")
|
||||||
fmt.Println(str)
|
|
||||||
}
|
}
|
||||||
fmt.Println(msg.ID)
|
|
||||||
var replyID string
|
var replyID string
|
||||||
if msg.Reply == nil {
|
if msg.Reply == nil {
|
||||||
replyID = "PICLIENT:UNAVAILABLE"
|
replyID = "PICLIENT:UNAVAILABLE"
|
||||||
@@ -558,10 +590,22 @@ func main() {
|
|||||||
replyID = msg.Reply.To
|
replyID = msg.Reply.To
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if reaction {
|
||||||
|
for i := len(tab.Messages) - 1; i > 0; i-- {
|
||||||
|
if tab.Messages[i].Raw.StanzaID.ID == msg.Reply.ID {
|
||||||
|
tab.Messages[i].Reactions[msg.From.String()] = *msg.CleanedBody
|
||||||
|
fyne.Do(func() {
|
||||||
|
UITabs[mucJidStr].Scroller.Refresh()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if correction {
|
if correction {
|
||||||
for i := len(tab.Messages) - 1; i > 0; i-- {
|
for i := len(tab.Messages) - 1; i > 0; i-- {
|
||||||
if tab.Messages[i].Raw.From.String() == msg.From.String() {
|
if tab.Messages[i].Raw.From.String() == msg.From.String() {
|
||||||
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
|
tab.Messages[i].Content = *msg.CleanedBody + " (corrected)"
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
UITabs[mucJidStr].Scroller.Refresh()
|
UITabs[mucJidStr].Scroller.Refresh()
|
||||||
})
|
})
|
||||||
@@ -578,10 +622,12 @@ func main() {
|
|||||||
Raw: *msg,
|
Raw: *msg,
|
||||||
ImageURL: ImageID,
|
ImageURL: ImageID,
|
||||||
Important: important,
|
Important: important,
|
||||||
|
Reactions: make(map[string]string),
|
||||||
}
|
}
|
||||||
if !ignore {
|
if !ignore {
|
||||||
tab.Messages = append(tab.Messages, myMessage)
|
tab.Messages = append(tab.Messages, myMessage)
|
||||||
}
|
}
|
||||||
|
important = false
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
UITabs[mucJidStr].Scroller.Refresh()
|
UITabs[mucJidStr].Scroller.Refresh()
|
||||||
if scrollDownOnNewMessage {
|
if scrollDownOnNewMessage {
|
||||||
@@ -616,6 +662,39 @@ func main() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
client.SetPresenceHandler(func(client *oasisSdk.XmppClient, from jid.JID, p oasisSdk.UserPresence) {
|
||||||
|
bareAcc := from.Bare()
|
||||||
|
tab, ok := chatTabs[bareAcc.String()]
|
||||||
|
if !ok {
|
||||||
|
// User presence
|
||||||
|
addChatTab(false, bareAcc, client.Login.DisplayName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tab.isMuc {
|
||||||
|
tab.Members[from.String()] = p
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
client.SetBookmarkHandler(false, func(client *oasisSdk.XmppClient, bookmark bookmarks.Channel) {
|
||||||
|
// FIXME
|
||||||
|
if bookmark.JID.String() == "conversations-offtopic-reloaded@conference.trashserver.net" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bookmark.Autojoin {
|
||||||
|
if bookmark.Nick == "" {
|
||||||
|
fmt.Println("WARNING: Bookmark has no name")
|
||||||
|
bookmark.Nick = client.Login.DisplayName
|
||||||
|
}
|
||||||
|
addChatTab(true, bookmark.JID, client.Login.DisplayName)
|
||||||
|
_, err := client.ConnectMuc(bookmark, oasisSdk.MucLegacyHistoryConfig{}, context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("ERROR: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
client.SetDeliveryReceiptHandler(
|
client.SetDeliveryReceiptHandler(
|
||||||
func(_ *oasisSdk.XmppClient, from jid.JID, id string) {
|
func(_ *oasisSdk.XmppClient, from jid.JID, id string) {
|
||||||
fmt.Printf("Delivered %s to %s", id, from.String())
|
fmt.Printf("Delivered %s to %s", id, from.String())
|
||||||
@@ -625,7 +704,6 @@ func main() {
|
|||||||
func(_ *oasisSdk.XmppClient, from jid.JID, id string) {
|
func(_ *oasisSdk.XmppClient, from jid.JID, id string) {
|
||||||
for _, tab := range chatTabs {
|
for _, tab := range chatTabs {
|
||||||
for i := len(tab.Messages) - 1; i >= 0; i-- {
|
for i := len(tab.Messages) - 1; i >= 0; i-- {
|
||||||
fmt.Println(tab.Messages[i])
|
|
||||||
if tab.Messages[i].Raw.StanzaID == nil {
|
if tab.Messages[i].Raw.StanzaID == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -635,7 +713,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Printf("%s has seen %s\n", from.String(), id)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -647,11 +724,13 @@ func main() {
|
|||||||
err = client.Connect()
|
err = client.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
responseChan := make(chan bool)
|
responseChan := make(chan bool)
|
||||||
fyne.Do(func() {
|
|
||||||
|
//fyne.Do(func() {
|
||||||
|
fmt.Println(err)
|
||||||
dialog.ShowConfirm("disconnected", fmt.Sprintf("the client disconnected. would you like to try and reconnect?\nreason:\n%s", err.Error()), func(b bool) {
|
dialog.ShowConfirm("disconnected", fmt.Sprintf("the client disconnected. would you like to try and reconnect?\nreason:\n%s", err.Error()), func(b bool) {
|
||||||
responseChan <- b
|
responseChan <- b
|
||||||
}, w)
|
}, w)
|
||||||
})
|
//})
|
||||||
if !<-responseChan {
|
if !<-responseChan {
|
||||||
connection = false
|
connection = false
|
||||||
}
|
}
|
||||||
@@ -660,8 +739,20 @@ func main() {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
a = app.New()
|
a = app.New()
|
||||||
//a.Settings().SetTheme(&myTheme{})
|
|
||||||
w = a.NewWindow("pi")
|
w = a.NewWindow("pi")
|
||||||
|
w.SetCloseIntercept(func() {
|
||||||
|
w.RequestFocus()
|
||||||
|
dialog.ShowConfirm("Close pi", "You hit the close button. Do you want Pi to close completely (confirm) or minimize to the tray? (cancel)", func(b bool) {
|
||||||
|
if b {
|
||||||
|
w.Close()
|
||||||
|
a.Quit()
|
||||||
|
log.Fatalln("Goodbye!")
|
||||||
|
} else {
|
||||||
|
w.Hide()
|
||||||
|
}
|
||||||
|
}, w)
|
||||||
|
})
|
||||||
w.Resize(fyne.NewSize(500, 500))
|
w.Resize(fyne.NewSize(500, 500))
|
||||||
|
|
||||||
entry := NewCustomMultiLineEntry()
|
entry := NewCustomMultiLineEntry()
|
||||||
@@ -697,7 +788,6 @@ func main() {
|
|||||||
go func() {
|
go func() {
|
||||||
if replying {
|
if replying {
|
||||||
m := chatTabs[activeMucJid].Messages[selectedId].Raw
|
m := chatTabs[activeMucJid].Messages[selectedId].Raw
|
||||||
fmt.Println(selectedId)
|
|
||||||
err = client.ReplyToEvent(&m, text)
|
err = client.ReplyToEvent(&m, text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
@@ -714,14 +804,39 @@ func main() {
|
|||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
} else {
|
||||||
}, w)
|
|
||||||
}
|
|
||||||
err = client.SendText(jid.MustParse(activeMucJid).Bare(), text)
|
err = client.SendText(jid.MustParse(activeMucJid).Bare(), text)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, w)
|
||||||
|
|
||||||
|
} else if text == "@here" && chatTabs[activeMucJid].isMuc {
|
||||||
|
tab := chatTabs[activeMucJid]
|
||||||
|
dialog.ShowConfirm("WARNING", fmt.Sprintf("There are %d members in this room.\nYou are about to mention every single one of them.\nYou may be punished if you are not a moderator of this chat.\nWould you like to continue?", len(tab.Members)), func(b bool) {
|
||||||
|
|
||||||
|
if b {
|
||||||
|
text = ""
|
||||||
|
for name := range tab.Members {
|
||||||
|
text = fmt.Sprintf("%s %s", text, jid.MustParse(name).Resourcepart())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.SendText(jid.MustParse(activeMucJid).Bare(), text)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, w)
|
||||||
|
} else {
|
||||||
|
err = client.SendText(jid.MustParse(activeMucJid).Bare(), text)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if !isMuc {
|
if !isMuc {
|
||||||
@@ -788,14 +903,6 @@ func main() {
|
|||||||
}, w)
|
}, w)
|
||||||
})
|
})
|
||||||
|
|
||||||
mis := fyne.NewMenuItem("clear chat window", func() {
|
|
||||||
dialog.ShowConfirm("clear chat window", "are you sure you want to clear the chat window?", func(b bool) {
|
|
||||||
if b {
|
|
||||||
fmt.Println("clearing chat")
|
|
||||||
}
|
|
||||||
}, w)
|
|
||||||
})
|
|
||||||
|
|
||||||
jtb := fyne.NewMenuItem("jump to bottom", func() {
|
jtb := fyne.NewMenuItem("jump to bottom", func() {
|
||||||
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -905,7 +1012,7 @@ func main() {
|
|||||||
}, w)
|
}, w)
|
||||||
})
|
})
|
||||||
|
|
||||||
leaveRoom := fyne.NewMenuItem("Leave current room (experimental)", func() {
|
leaveRoom := fyne.NewMenuItem("disconnect from current room", func() {
|
||||||
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@@ -917,14 +1024,112 @@ func main() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !chatTabs[activeMucJid].isMuc {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
AppTabs.Selected().Text = fmt.Sprintf("%s (disconnected)", AppTabs.Selected().Text)
|
AppTabs.Selected().Text = fmt.Sprintf("%s (disconnected)", AppTabs.Selected().Text)
|
||||||
|
AppTabs.Items = append(AppTabs.Items[:AppTabs.SelectedIndex()], AppTabs.Items[AppTabs.SelectedIndex()+1:]...)
|
||||||
AppTabs.SelectIndex(0)
|
AppTabs.SelectIndex(0)
|
||||||
delete(client.MucChannels, activeMucJid)
|
err := client.DisconnectMuc(activeMucJid, "user left on own accord", context.TODO())
|
||||||
//delete(chatTabs, activeMucJid)
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
}
|
||||||
|
delete(UITabs, activeMucJid)
|
||||||
})
|
})
|
||||||
|
|
||||||
joinARoom := fyne.NewMenuItem("Join a room", func() {
|
manageBookmarks := fyne.NewMenuItem("manage bookmarks", func() {
|
||||||
dialog.ShowEntryDialog("Join a room", "JID:", func(s string) {
|
bookmarks := client.BookmarkCache()
|
||||||
|
box := container.NewVBox()
|
||||||
|
box.Add(widget.NewRichTextFromMarkdown("# Manage Bookmarks"))
|
||||||
|
for address, bookmark := range bookmarks {
|
||||||
|
bookmarkWidget := container.NewGridWithColumns(5)
|
||||||
|
|
||||||
|
nameLabel := widget.NewLabel(bookmark.Name)
|
||||||
|
nameLabel.Wrapping = fyne.TextWrapBreak
|
||||||
|
|
||||||
|
bookmarkWidget.Add(nameLabel)
|
||||||
|
bookmarkJidWidget := widget.NewLabel(address)
|
||||||
|
bookmarkJidWidget.TextStyle.Monospace = true
|
||||||
|
bookmarkJidWidget.Selectable = true
|
||||||
|
bookmarkJidWidget.Wrapping = fyne.TextWrapWord
|
||||||
|
|
||||||
|
bookmarkWidget.Add(bookmarkJidWidget)
|
||||||
|
var autojoinCheck *widget.Check
|
||||||
|
var deleteBookmarkButton *widget.Button
|
||||||
|
var joinRoomButton *widget.Button
|
||||||
|
joinRoomButton = widget.NewButtonWithIcon("Join", theme.AccountIcon(), func() {
|
||||||
|
fyne.Do(func() { joinRoomButton.Disable() })
|
||||||
|
go func() {
|
||||||
|
if bookmark.Nick == "" {
|
||||||
|
bookmark.Nick = client.Login.DisplayName
|
||||||
|
}
|
||||||
|
var zero uint64 = uint64(0)
|
||||||
|
addChatTab(true, bookmark.JID, bookmark.Nick)
|
||||||
|
_, err := client.ConnectMuc(bookmark, oasisSdk.MucLegacyHistoryConfig{MaxCount: &zero}, context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
fyne.Do(func() {
|
||||||
|
joinRoomButton.Enable()
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.RefreshBookmarks(false)
|
||||||
|
fyne.Do(func() { joinRoomButton.Enable() })
|
||||||
|
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
autojoinCheck = widget.NewCheck("Autojoin", func(b bool) {
|
||||||
|
go func() {
|
||||||
|
fyne.Do(func() { autojoinCheck.Disable() })
|
||||||
|
bookmark.Autojoin = b
|
||||||
|
err := client.PublishBookmark(bookmark, context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
fyne.Do(func() {
|
||||||
|
autojoinCheck.Enable()
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.RefreshBookmarks(false)
|
||||||
|
fyne.Do(func() { autojoinCheck.Enable() })
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
autojoinCheck.Checked = bookmark.Autojoin
|
||||||
|
|
||||||
|
deleteBookmarkButton = widget.NewButtonWithIcon("Delete", theme.CancelIcon(), func() {
|
||||||
|
go func() {
|
||||||
|
err := client.DeleteBookmark(bookmark.JID, context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
fyne.Do(func() {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.RefreshBookmarks(false)
|
||||||
|
bookmarkWidget.RemoveAll()
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
|
||||||
|
bookmarkWidget.Add(autojoinCheck)
|
||||||
|
bookmarkWidget.Add(deleteBookmarkButton)
|
||||||
|
bookmarkWidget.Add(joinRoomButton)
|
||||||
|
|
||||||
|
box.Add(bookmarkWidget)
|
||||||
|
}
|
||||||
|
myScroller := container.NewScroll(box)
|
||||||
|
AppTabs.Items[0].Content = myScroller
|
||||||
|
AppTabs.Items[0].Text = "Bookmarks"
|
||||||
|
AppTabs.SelectIndex(0)
|
||||||
|
chatSidebar.Hidden = true
|
||||||
|
//d := dialog.NewCustom("manage bookmarks", "cancel", myScroller, w)
|
||||||
|
//d.Show()
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
joinARoom := fyne.NewMenuItem("connect to a room", func() {
|
||||||
|
dialog.ShowEntryDialog("connect to a room", "JID:", func(s string) {
|
||||||
i := resourcePiloadingGif
|
i := resourcePiloadingGif
|
||||||
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
|
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -941,20 +1146,20 @@ func main() {
|
|||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
joinjid, err := myjid.WithResource(login.DisplayName)
|
|
||||||
if err != nil {
|
mychannel := new(bookmarks.Channel)
|
||||||
d.Hide()
|
mychannel.JID = myjid
|
||||||
dialog.ShowError(err, w)
|
mychannel.Nick = login.DisplayName
|
||||||
return
|
//ch, err := client.MucClient.Join(client.Ctx, joinjid, client.Session)
|
||||||
}
|
|
||||||
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)
|
addChatTab(true, myjid, login.DisplayName)
|
||||||
|
num := uint64(0)
|
||||||
|
_, err = client.ConnectMuc(*mychannel, oasisSdk.MucLegacyHistoryConfig{MaxCount: &num}, context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
d.Hide()
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//client.MucChannels[s] = ch
|
||||||
d.Hide()
|
d.Hide()
|
||||||
}()
|
}()
|
||||||
}, w)
|
}, w)
|
||||||
@@ -985,109 +1190,10 @@ func main() {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
menu_help := fyne.NewMenu("π", mit, reconnect, licensesbtn)
|
||||||
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"))
|
menu_changeroom := fyne.NewMenu("Α", mic, beginADM, joinARoom, leaveRoom, manageBookmarks)
|
||||||
info, err := disco.GetInfo(client.Ctx, "", search, client.Session)
|
menu_configureview := fyne.NewMenu("Β", mia, jtt, jtb)
|
||||||
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)
|
|
||||||
}()
|
|
||||||
|
|
||||||
}, 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)
|
|
||||||
})
|
|
||||||
|
|
||||||
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() {
|
hafjag := fyne.NewMenuItem("Hafjag", func() {
|
||||||
entry.Text = "Hafjag"
|
entry.Text = "Hafjag"
|
||||||
SendCallback()
|
SendCallback()
|
||||||
@@ -1119,6 +1225,40 @@ func main() {
|
|||||||
entry.Text = old
|
entry.Text = old
|
||||||
})
|
})
|
||||||
|
|
||||||
|
kai := fyne.NewMenuItem("kai cenat beg", func() {
|
||||||
|
old := entry.Text
|
||||||
|
entry.Text = "chat will you subscribe to save the kai cenat mafiathon 3"
|
||||||
|
SendCallback()
|
||||||
|
entry.Text = old
|
||||||
|
})
|
||||||
|
|
||||||
|
mipipiemi := fyne.NewMenuItem("mi pipi e mi", func() {
|
||||||
|
old := entry.Text
|
||||||
|
entry.Text = "mi pipi e mi"
|
||||||
|
SendCallback()
|
||||||
|
entry.Text = old
|
||||||
|
})
|
||||||
|
|
||||||
|
exposed_suffix := fyne.NewMenuItem(".exposed", func() {
|
||||||
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var activeChatJid string
|
||||||
|
for jid, tabData := range UITabs {
|
||||||
|
if tabData.Scroller == selectedScroller {
|
||||||
|
activeChatJid = jid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LatestMessage := chatTabs[activeChatJid].Messages[len(chatTabs[activeChatJid].Messages)-1]
|
||||||
|
|
||||||
|
old := entry.Text
|
||||||
|
entry.Text = fmt.Sprintf("%s.exposed", LatestMessage.Content)
|
||||||
|
SendCallback()
|
||||||
|
entry.Text = old
|
||||||
|
})
|
||||||
|
|
||||||
mycurrenttime := fyne.NewMenuItem("Current time", func() {
|
mycurrenttime := fyne.NewMenuItem("Current time", func() {
|
||||||
entry.Text = fmt.Sprintf("It is currently %s", time.Now().Format(time.RFC850))
|
entry.Text = fmt.Sprintf("It is currently %s", time.Now().Format(time.RFC850))
|
||||||
SendCallback()
|
SendCallback()
|
||||||
@@ -1126,8 +1266,8 @@ func main() {
|
|||||||
|
|
||||||
mycurrentplayingsong := fyne.NewMenuItem("Get currently playing song", func() {
|
mycurrentplayingsong := fyne.NewMenuItem("Get currently playing song", func() {
|
||||||
// BEGIN PLATFORM SPECIFIC CODE
|
// BEGIN PLATFORM SPECIFIC CODE
|
||||||
if os.PathSeparator == '\\' && os.PathListSeparator == ';' { // Windows
|
if isWindows() {
|
||||||
dialog.ShowError(errors.New("This feature is not supported on your operating system"), w)
|
dialog.ShowError(errors.New("this feature is not supported on your operating system"), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// END PLATFORM SPECIFIC CODE
|
// END PLATFORM SPECIFIC CODE
|
||||||
@@ -1160,7 +1300,7 @@ func main() {
|
|||||||
} else if a_ok {
|
} else if a_ok {
|
||||||
newtext = fmt.Sprintf("I'm currently listening to a song by %s", artists[0])
|
newtext = fmt.Sprintf("I'm currently listening to a song by %s", artists[0])
|
||||||
} else {
|
} else {
|
||||||
dialog.ShowError(errors.New("error: There's a playing song, but we could not get any information."), w)
|
dialog.ShowError(errors.New("error: There's a playing song, but we could not get any information"), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1175,7 +1315,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
menu_jokes := fyne.NewMenu("Δ", mycurrenttime, hafjag, hotfuck, agree, mycurrentplayingsong)
|
menu_jokes := fyne.NewMenu("Δ", mycurrenttime, mycurrentplayingsong, hafjag, hotfuck, agree, kai, mipipiemi, exposed_suffix)
|
||||||
bit := fyne.NewMenuItem("mark selected message as read", func() {
|
bit := fyne.NewMenuItem("mark selected message as read", func() {
|
||||||
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -1264,17 +1404,47 @@ func main() {
|
|||||||
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions, menu_jokes)
|
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions, menu_jokes)
|
||||||
w.SetMainMenu(ma)
|
w.SetMainMenu(ma)
|
||||||
|
|
||||||
AppTabs = container.NewAppTabs(
|
desk, ok := a.(desktop.App)
|
||||||
container.NewTabItem("τίποτα", widget.NewLabel(`
|
if ok {
|
||||||
|
desk.SetSystemTrayMenu(fyne.NewMenu("", fyne.NewMenuItem("show", w.Show)))
|
||||||
|
}
|
||||||
|
|
||||||
|
AppTabs = container.NewDocTabs(
|
||||||
|
container.NewTabItem("...", widget.NewLabel(`
|
||||||
pi
|
pi
|
||||||
|
|
||||||
|
This tab will be used for displaying certain actions, such as
|
||||||
|
managing your bookmarks and configuring rooms.
|
||||||
`)),
|
`)),
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, mucJidStr := range login.MucsToJoin {
|
AppTabs.CloseIntercept = func(ti *container.TabItem) {
|
||||||
mucJid, err := jid.Parse(mucJidStr)
|
go func() {
|
||||||
if err == nil {
|
var activeChatJid string
|
||||||
addChatTab(true, mucJid, login.DisplayName)
|
scroller, ok := ti.Content.(*widget.List)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
for jid, tabData := range UITabs {
|
||||||
|
if tabData.Scroller == scroller {
|
||||||
|
activeChatJid = jid
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !chatTabs[activeChatJid].isMuc {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.DisconnectMuc(activeChatJid, "user left on own accord", context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
}
|
||||||
|
AppTabs.Selected().Text = fmt.Sprintf("%s (disconnected)", AppTabs.Selected().Text)
|
||||||
|
AppTabs.Remove(ti)
|
||||||
|
delete(UITabs, activeChatJid)
|
||||||
|
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, userJidStr := range DMs {
|
for _, userJidStr := range DMs {
|
||||||
@@ -1286,6 +1456,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AppTabs.OnSelected = func(ti *container.TabItem) {
|
AppTabs.OnSelected = func(ti *container.TabItem) {
|
||||||
|
if AppTabs.Selected() == AppTabs.Items[0] {
|
||||||
|
chatSidebar.Hidden = true
|
||||||
|
return
|
||||||
|
}
|
||||||
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@@ -1301,21 +1475,78 @@ func main() {
|
|||||||
|
|
||||||
tab := chatTabs[activeChatJid]
|
tab := chatTabs[activeChatJid]
|
||||||
UITab := UITabs[activeChatJid]
|
UITab := UITabs[activeChatJid]
|
||||||
if tab.isMuc {
|
|
||||||
//chatInfo = *container.NewHBox(widget.NewLabel(tab.Muc.Addr().String()))
|
|
||||||
} else {
|
|
||||||
chatInfo = *container.NewHBox(widget.NewLabel(tab.Jid.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
chatSidebar = *UITab.Sidebar
|
chatSidebar = *UITab.Sidebar
|
||||||
old := chatSidebar.Position()
|
if tab.isMuc {
|
||||||
chatSidebar.Refresh()
|
nameLabel := widget.NewRichTextFromMarkdown("# " + activeChatJid)
|
||||||
chatSidebar.Move(old)
|
nameLabel.Truncation = fyne.TextTruncateEllipsis
|
||||||
|
box := container.NewVBox(nameLabel, widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members))))
|
||||||
|
chatSidebar.Objects = []fyne.CanvasObject{}
|
||||||
|
|
||||||
|
for name, p := range tab.Members {
|
||||||
|
gen, _ := identicon.New("github", 5, 3)
|
||||||
|
userjid, err := jid.Parse(name)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("ERROR: " + err.Error())
|
||||||
|
continue // unrecoverable
|
||||||
|
}
|
||||||
|
nickname := userjid.Resourcepart()
|
||||||
|
if nickname == "" {
|
||||||
|
continue // we got the MUC presence, do not include it in the member list
|
||||||
|
}
|
||||||
|
ii, err := gen.Draw(nickname)
|
||||||
|
mention := func() {
|
||||||
|
entry.SetText(fmt.Sprintf("%s %s", entry.Text, nickname))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("ERROR: " + err.Error())
|
||||||
|
box.Add(container.NewHBox(widget.NewLabel(nickname), widget.NewButton("Mention", mention)))
|
||||||
|
} else {
|
||||||
|
im := ii.Image(15)
|
||||||
|
imageWidget := canvas.NewImageFromImage(im)
|
||||||
|
imageWidget.FillMode = canvas.ImageFillOriginal
|
||||||
|
imageWidget.Refresh()
|
||||||
|
nickLabel := widget.NewLabel(nickname)
|
||||||
|
nickLabel.Selectable = true
|
||||||
|
|
||||||
|
box.Add(container.NewHBox(imageWidget, nickLabel))
|
||||||
|
if p.Status != "" {
|
||||||
|
s := widget.NewLabel(fmt.Sprintf("\"%s\"", p.Status))
|
||||||
|
s.Importance = widget.WarningImportance
|
||||||
|
s.TextStyle = fyne.TextStyle{
|
||||||
|
Bold: false,
|
||||||
|
Italic: true,
|
||||||
|
Monospace: false,
|
||||||
|
}
|
||||||
|
s.Truncation = fyne.TextTruncateEllipsis
|
||||||
|
box.Add(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK - disable chatsidebar because it's currently very buggy
|
}
|
||||||
chatSidebar.Hidden = true
|
chatSidebar = *container.NewGridWithColumns(1, container.NewVScroll(box))
|
||||||
|
} else {
|
||||||
|
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid))
|
||||||
|
}
|
||||||
|
chatSidebar.Hidden = false
|
||||||
|
chatSidebar.Refresh()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
chatSidebar.Hidden = false
|
||||||
statBar.SetText("")
|
statBar.SetText("")
|
||||||
w.SetContent(container.NewVSplit(container.NewVSplit(AppTabs, container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn))), container.NewHSplit(&statBar, &chatInfo)))
|
chatInfo = *container.NewHBox(widget.NewLabel(""))
|
||||||
|
MainSplit := container.NewHSplit(AppTabs, &chatSidebar)
|
||||||
|
DownSplit := container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn))
|
||||||
|
|
||||||
|
BigSplit := container.NewVSplit(MainSplit, DownSplit)
|
||||||
|
BigSplit.SetOffset(1)
|
||||||
|
|
||||||
|
SmallSplit := container.NewHSplit(&statBar, &chatInfo)
|
||||||
|
|
||||||
|
MasterSplit := container.NewVSplit(BigSplit, SmallSplit)
|
||||||
|
|
||||||
|
MasterSplit.SetOffset(1)
|
||||||
|
w.SetContent(MasterSplit)
|
||||||
w.ShowAndRun()
|
w.ShowAndRun()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user