12 Commits

3 changed files with 111 additions and 70 deletions

View File

@@ -112,7 +112,7 @@ go build -tags wayland .
## εγκατάσταση ## εγκατάσταση
(installation) (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/packages/pi-im), which is maintained by [snit](https://isekai.rocks/~snit).
## υποστήριξη ## υποστήριξη
(support) (support)

137
main.go
View File

@@ -3,19 +3,20 @@ package main
import ( import (
//core - required //core - required
"context" "context"
_ "embed"
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt" "fmt"
_ "image/jpeg"
_ "image/png"
"io" "io"
"log" "log"
"math/rand/v2" "math/rand/v2"
"net/url" "net/url"
"image/color"
"os" "os"
"strings" "strings"
"time" "time"
_ "embed"
_ "image/png"
_ "image/jpeg"
// gui - required // gui - required
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
@@ -27,25 +28,25 @@ import (
"fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
extraWidgets "fyne.io/x/fyne/widget"
"github.com/makeworld-the-better-one/go-isemoji" "github.com/makeworld-the-better-one/go-isemoji"
"github.com/rrivera/identicon" "github.com/rrivera/identicon"
"github.com/shreve/musicwand/pkg/mpris" "github.com/shreve/musicwand/pkg/mpris"
// xmpp - required // xmpp - required
oasisSdk "github.com/sunglocto/oasis-sdk"
"mellium.im/xmpp/bookmarks" "mellium.im/xmpp/bookmarks"
"mellium.im/xmpp/jid" "mellium.im/xmpp/jid"
"mellium.im/xmpp/muc" "mellium.im/xmpp/muc"
"mellium.im/xmpp/stanza"
"mellium.im/xmpp/pubsub" "mellium.im/xmpp/pubsub"
oasisSdk "github.com/sunglocto/oasis-sdk" "mellium.im/xmpp/stanza"
xmppColor "mellium.im/xmpp/color"
// TODO: integrated theme switcher // TODO: integrated theme switcher
) )
//go:embed pi.png //go:embed pi.png
var iconBytes []byte var iconBytes []byte
var version string = "3.1i" var version string = "3.14i"
var statBar widget.Label var statBar widget.Label
var chatInfo fyne.Container var chatInfo fyne.Container
var chatSidebar fyne.Container var chatSidebar fyne.Container
@@ -167,14 +168,14 @@ func CreateUITab(chatJidStr string) ChatTabUI {
im := ii.Image(25) im := ii.Image(25)
ico := canvas.NewImageFromImage(im) ico := canvas.NewImageFromImage(im)
ico.FillMode = canvas.ImageFillOriginal ico.FillMode = canvas.ImageFillOriginal
author := widget.NewLabel("author") author := canvas.NewText("author", color.RGBA{255, 255, 255, 255})
author.TextStyle.Bold = true author.TextStyle.Bold = true
author.Selectable = 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.Importance = widget.MediumImportance
//content.SizeName = fyne.ThemeSizeName(theme.SizeNameText) 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()
@@ -207,7 +208,10 @@ func CreateUITab(chatJidStr string) ChatTabUI {
// Icon generate end // Icon generate end
author := authorBox.Objects[1].(*widget.Label) author := authorBox.Objects[1].(*canvas.Text)
col := xmppColor.String(chatTabs[chatJidStr].Messages[i].Author, 0, xmppColor.None)
author.Color = col
content := vbox.Objects[2].(*widget.Label) content := vbox.Objects[2].(*widget.Label)
unknown_tags := chatTabs[chatJidStr].Messages[i].Raw.Unknown unknown_tags := chatTabs[chatJidStr].Messages[i].Raw.Unknown
for _, v := range unknown_tags { for _, v := range unknown_tags {
@@ -216,8 +220,9 @@ func CreateUITab(chatJidStr string) ChatTabUI {
if attr.Name.Local == "id" { if attr.Name.Local == "id" {
reason, ok := OccupantIdsToBlock[attr.Value] reason, ok := OccupantIdsToBlock[attr.Value]
if ok { if ok {
author.SetText("Ignored user") author.Text = ("Ignored user")
content.SetText("This user is ignored due to: " + reason) content.SetText("This user is ignored due to: " + reason)
content.Importance = widget.LowImportance
return // message is from blocked user return // message is from blocked user
} }
} }
@@ -236,6 +241,8 @@ func CreateUITab(chatJidStr string) ChatTabUI {
if chatTabs[chatJidStr].Messages[i].Important { if chatTabs[chatJidStr].Messages[i].Important {
content.Importance = widget.DangerImportance content.Importance = widget.DangerImportance
} else {
content.Importance = widget.MediumImportance
} }
btn.Hidden = true // Hide by default btn.Hidden = true // Hide by default
msgContent := chatTabs[chatJidStr].Messages[i].Content msgContent := chatTabs[chatJidStr].Messages[i].Content
@@ -296,27 +303,31 @@ 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 %s", replyBodyIcon, strings.ReplaceAll(chatTabs[chatJidStr].Messages[i].Content, "\n", newlineIcon))) replytext.Text = 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 %s", chatTabs[chatJidStr].Messages[i].Author, replyNameIcon, guy)) author.Text = (fmt.Sprintf("%s %s %s", chatTabs[chatJidStr].Messages[i].Author, replyNameIcon, guy))
} else { } else {
author.SetText(chatTabs[chatJidStr].Messages[i].Author) author.Text = (chatTabs[chatJidStr].Messages[i].Author)
replytext.Hide() replytext.Hide()
} }
sl := strings.Split(msgContent, " ") sl := strings.Split(msgContent, " ")
content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
if len(sl) == 1 && isemoji.IsEmoji(sl[0]) { if len(sl) == 1 && isemoji.IsEmoji(sl[0]) {
content.SizeName = fyne.ThemeSizeName(theme.SizeNameHeadingText) content.SizeName = fyne.ThemeSizeName(theme.SizeNameHeadingText)
content.Refresh() content.Refresh()
} else {
content.SizeName = fyne.ThemeSizeName(theme.SizeNameText)
content.Refresh()
} }
if sl[0] == "/me" { if sl[0] == "/me" {
author.SetText(author.Text + " " + strings.Join(sl[1:], " ")) author.Text = (author.Text + " " + strings.Join(sl[1:], " "))
content.SetText(" ") content.SetText(" ")
} }
@@ -797,6 +808,7 @@ func main() {
}, w) }, w)
}) })
w.Resize(fyne.NewSize(500, 500)) w.Resize(fyne.NewSize(500, 500))
w.SetIcon(res)
entry := NewCustomMultiLineEntry() entry := NewCustomMultiLineEntry()
entry.SetPlaceHolder("Say something, you know you want to.\nCtrl+Enter for newline") entry.SetPlaceHolder("Say something, you know you want to.\nCtrl+Enter for newline")
@@ -1170,7 +1182,7 @@ func main() {
setNick = widget.NewButton("Set nick", func() { setNick = widget.NewButton("Set nick", func() {
go func() { go func() {
fyne.Do(func() {setNick.Disable()}) fyne.Do(func() { setNick.Disable() })
newNick := nickEntry.Text newNick := nickEntry.Text
bookmark.Nick = newNick bookmark.Nick = newNick
err := client.PublishBookmark(bookmark, context.TODO()) err := client.PublishBookmark(bookmark, context.TODO())
@@ -1182,7 +1194,7 @@ func main() {
return return
} }
client.RefreshBookmarks(false) client.RefreshBookmarks(false)
fyne.Do(func() {setNick.Enable()}) fyne.Do(func() { setNick.Enable() })
}() }()
}) })
@@ -1206,14 +1218,7 @@ func main() {
joinARoom := fyne.NewMenuItem("connect to a room", func() { joinARoom := fyne.NewMenuItem("connect to a room", func() {
dialog.ShowEntryDialog("connect to a room", "JID:", func(s string) { dialog.ShowEntryDialog("connect to a room", "JID:", func(s string) {
i := resourcePiloadingGif d := dialog.NewCustom("Please wait", "Close", widget.NewLabel("Connecting to "+s), w)
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
if err != nil {
panic(err)
}
gif.Start()
gif.Show()
d := dialog.NewCustom("Please wait", "Close", gif, w)
d.Show() d.Show()
go func() { go func() {
myjid, err := jid.Parse(s) myjid, err := jid.Parse(s)
@@ -1242,14 +1247,7 @@ func main() {
beginADM := fyne.NewMenuItem("start a DM", func() { beginADM := fyne.NewMenuItem("start a DM", func() {
dialog.ShowEntryDialog("Start a DM", "JID:", func(s string) { dialog.ShowEntryDialog("Start a DM", "JID:", func(s string) {
i := resourcePiloadingGif d := dialog.NewCustom("Please wait", "Close", widget.NewLabel("Opening a DM with " + s), w)
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
if err != nil {
panic(err)
}
gif.Start()
gif.Show()
d := dialog.NewCustom("Please wait", "Close", gif, w)
d.Show() d.Show()
go func() { go func() {
myjid, err := jid.Parse(s) myjid, err := jid.Parse(s)
@@ -1300,6 +1298,8 @@ func main() {
entry.Text = old entry.Text = old
}) })
kai := fyne.NewMenuItem("kai cenat beg", func() { kai := fyne.NewMenuItem("kai cenat beg", func() {
old := entry.Text old := entry.Text
entry.Text = "chat will you subscribe to save the kai cenat mafiathon 3" entry.Text = "chat will you subscribe to save the kai cenat mafiathon 3"
@@ -1314,6 +1314,13 @@ func main() {
entry.Text = old entry.Text = old
}) })
wspeed := fyne.NewMenuItem("W Speed ❤️‍🩹❤️‍🩹", func() {
old := entry.Text
entry.Text = "W Speed ❤️‍🩹❤️‍🩹"
SendCallback()
entry.Text = old
})
exposed_suffix := fyne.NewMenuItem(".exposed", func() { exposed_suffix := fyne.NewMenuItem(".exposed", func() {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List) selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok { if !ok {
@@ -1390,7 +1397,7 @@ func main() {
} }
}) })
menu_jokes := fyne.NewMenu("Δ", mycurrenttime, mycurrentplayingsong, hafjag, hotfuck, agree, kai, mipipiemi, exposed_suffix) menu_jokes := fyne.NewMenu("Δ", mycurrenttime, mycurrentplayingsong, hafjag, hotfuck, agree, kai, mipipiemi, exposed_suffix, wspeed)
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 {
@@ -1408,6 +1415,36 @@ func main() {
client.MarkAsRead(&m) client.MarkAsRead(&m)
}) })
rec := fyne.NewMenuItem("retract selected message", 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
}
}
if !chatTabs[activeMucJid].isMuc {
return // TODO: For 1:1 DMs we use OccupantID for the ID and send message type 'chat'
}
id := chatTabs[activeMucJid].Messages[selectedId].Raw.StanzaID
if id == nil {
return
}
msg := fmt.Sprintf("<message type='groupchat' to='%s' id='retract-message-1'><retract id='%s' xmlns='urn:xmpp:message-retract:1'/><fallback xmlns='urn:xmpp:fallback:0' for='urn:xmpp:message-retract:1' /><body>This user retracted a previous message, but it's unsupported by your client.</body><store xmlns='urn:xmpp:hints'/></message>", activeMucJid, id.ID)
fmt.Println(msg)
err := client.Session.Send(context.TODO(), xml.NewDecoder(strings.NewReader(msg)))
if err != nil {
dialog.ShowError(err, w)
}
})
bia := fyne.NewMenuItem("toggle replying to message", func() { bia := fyne.NewMenuItem("toggle replying to message", func() {
replying = !replying replying = !replying
}) })
@@ -1517,7 +1554,7 @@ func main() {
dialog.ShowCustom("Message", "Close", pre, w) dialog.ShowCustom("Message", "Close", pre, w)
}) })
menu_messageoptions := fyne.NewMenu("Γ", bit, bia, bic, red, blck) menu_messageoptions := fyne.NewMenu("Γ", bit, bia, bic, rec, red, blck)
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)
@@ -1573,6 +1610,16 @@ func main() {
} }
} }
AppTabs.OnUnselected = func(ti *container.TabItem) {
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
if !ok {
return
}
selectedScroller.Hidden = true
}
var MainSplit *container.Split
AppTabs.OnSelected = func(ti *container.TabItem) { AppTabs.OnSelected = func(ti *container.TabItem) {
chatSidebar.Resize(chatSidebar.Size()) chatSidebar.Resize(chatSidebar.Size())
if AppTabs.Selected() == AppTabs.Items[0] { if AppTabs.Selected() == AppTabs.Items[0] {
@@ -1584,6 +1631,8 @@ func main() {
return return
} }
selectedScroller.Hidden = false
var activeChatJid string var activeChatJid string
for jid, tabData := range UITabs { for jid, tabData := range UITabs {
if tabData.Scroller == selectedScroller { if tabData.Scroller == selectedScroller {
@@ -1601,8 +1650,8 @@ func main() {
nameLabel.Truncation = fyne.TextTruncateEllipsis nameLabel.Truncation = fyne.TextTruncateEllipsis
memberLabel := widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members))) memberLabel := widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members)))
memberLabel.Truncation = fyne.TextTruncateEllipsis memberLabel.Truncation = fyne.TextTruncateEllipsis
box := container.NewVBox(nameLabel, ) box := container.NewVBox(nameLabel)
chatSidebar.Objects = []fyne.CanvasObject{} // chatSidebar.Objects = []fyne.CanvasObject{}
for name, p := range tab.Members { for name, p := range tab.Members {
log.Println(string(p.Type)) log.Println(string(p.Type))
@@ -1644,24 +1693,28 @@ func main() {
} }
s.Truncation = fyne.TextTruncateEllipsis s.Truncation = fyne.TextTruncateEllipsis
box.Add(s) box.Add(s)
box.Refresh()
} }
} }
} }
chatSidebar = *container.NewGridWithColumns(1, container.NewVScroll(box)) chatSidebar = *container.NewGridWithColumns(1, container.NewVScroll(box))
} else {
if activeChatJid == config.Login.User {
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid), widget.NewLabel("Hey, that's you!"))
} else { } else {
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid)) chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid))
} }
}
chatSidebar.Hidden = false chatSidebar.Hidden = false
chatSidebar.Refresh() chatSidebar.Show()
chatSidebar.Resize(chatSidebar.Size()) MainSplit.Resize(MainSplit.Size())
} }
chatSidebar.Hidden = false chatSidebar.Hidden = false
statBar.SetText("") statBar.SetText("")
chatInfo = *container.NewHBox(widget.NewLabel("")) chatInfo = *container.NewHBox(widget.NewLabel(""))
MainSplit := container.NewHSplit(AppTabs, &chatSidebar) MainSplit = container.NewHSplit(AppTabs, &chatSidebar)
DownSplit := container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn)) DownSplit := container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn))
BigSplit := container.NewVSplit(MainSplit, DownSplit) BigSplit := container.NewVSplit(MainSplit, DownSplit)

File diff suppressed because one or more lines are too long