Compare commits
10 Commits
3i
...
a847ba5cdd
| Author | SHA1 | Date | |
|---|---|---|---|
| a847ba5cdd | |||
| d5ec58be68 | |||
| 22fe1cac46 | |||
| 873d38b3ac | |||
| ff121b42d9 | |||
| d00f266a4e | |||
| 934d06b6b0 | |||
| d612d16e52 | |||
| e9c1f8dbe1 | |||
| 7ec143bc98 |
@@ -32,8 +32,6 @@ If you want to add MUCs or DMs, you must configure the program by editing the pi
|
||||
<DisplayName>sunglocto</DisplayName>
|
||||
<TLSoff>false</TLSoff>
|
||||
<StartTLS>true</StartTLS>
|
||||
<MucsToJoin>room1@muc.example.com</MucsToJoin>
|
||||
<MucsToJoin>room2@muc.example.com</MucsToJoin>
|
||||
</Login>
|
||||
<Notifications>true</Notifications>
|
||||
<DMs>person1@example.com</DMs>
|
||||
|
||||
2
go.mod
2
go.mod
@@ -8,8 +8,8 @@ require (
|
||||
github.com/makeworld-the-better-one/go-isemoji v1.3.0
|
||||
github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d
|
||||
github.com/shreve/musicwand v0.0.1
|
||||
github.com/sunglocto/oasis-sdk v0.0.0-20251128101950-083ea4658d9c
|
||||
mellium.im/xmpp v0.22.0
|
||||
pain.agency/oasis-sdk v0.0.0-20250918002549-5a45c8afedcd
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
8
go.sum
8
go.sum
@@ -78,6 +78,12 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/sunglocto/oasis-sdk v0.0.0-20251128095406-3c003b830815 h1:HsI7kxYauygjRK15Iwf3FlZnU8XJwRPSmbtoNfeyBYk=
|
||||
github.com/sunglocto/oasis-sdk v0.0.0-20251128095406-3c003b830815/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
|
||||
github.com/sunglocto/oasis-sdk v0.0.0-20251128100941-f53b1dabbcac h1:pVarwnSbjNSDpeQer/SERztTEpkl6Cnb5xuyxXmb50k=
|
||||
github.com/sunglocto/oasis-sdk v0.0.0-20251128100941-f53b1dabbcac/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
|
||||
github.com/sunglocto/oasis-sdk v0.0.0-20251128101950-083ea4658d9c h1:peWSGIl0xBvx3sJR3+kHV1xOe1X8dbQ9xtvEXyXogCo=
|
||||
github.com/sunglocto/oasis-sdk v0.0.0-20251128101950-083ea4658d9c/go.mod h1:Y5iPruMLvqNwGVqB+n/+wylQjCTPKQx37I/PyLWhuzk=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
@@ -113,5 +119,3 @@ 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-20250918002549-5a45c8afedcd h1:oLKI4XqaHpJeegwRxRYH9hepFO4GYKCr6C7cLwqXTK8=
|
||||
pain.agency/oasis-sdk v0.0.0-20250918002549-5a45c8afedcd/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=
|
||||
|
||||
572
main.go
572
main.go
@@ -33,11 +33,12 @@ import (
|
||||
"mellium.im/xmpp/bookmarks"
|
||||
"mellium.im/xmpp/jid"
|
||||
"mellium.im/xmpp/muc"
|
||||
oasisSdk "pain.agency/oasis-sdk"
|
||||
"mellium.im/xmpp/pubsub"
|
||||
oasisSdk "github.com/sunglocto/oasis-sdk"
|
||||
// TODO: integrated theme switcher
|
||||
)
|
||||
|
||||
var version string = "3i"
|
||||
var version string = "3.1i"
|
||||
var statBar widget.Label
|
||||
var chatInfo fyne.Container
|
||||
var chatSidebar fyne.Container
|
||||
@@ -45,6 +46,7 @@ var replyNameIcon string = ">"
|
||||
var replyBodyIcon string = ">"
|
||||
var newlineIcon string = " |-> "
|
||||
var agreesToSendingHotFuckIntoChannel bool = false
|
||||
var OccupantIdsToBlock = make(map[string]string)
|
||||
|
||||
// by sunglocto
|
||||
// license AGPL
|
||||
@@ -58,6 +60,7 @@ type Message struct {
|
||||
Raw oasisSdk.XMPPChatMessage
|
||||
Important bool
|
||||
Readers []jid.JID
|
||||
Reactions map[string]string
|
||||
}
|
||||
|
||||
type ChatTab struct {
|
||||
@@ -135,31 +138,12 @@ var DMs []string
|
||||
var chatTabs = make(map[string]*ChatTab)
|
||||
var UITabs = make(map[string]*ChatTabUI)
|
||||
|
||||
var AppTabs *container.AppTabs
|
||||
var AppTabs *container.DocTabs
|
||||
var selectedId widget.ListItemID
|
||||
var replying bool = false
|
||||
var notifications bool
|
||||
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 w fyne.Window
|
||||
var a fyne.App
|
||||
@@ -189,12 +173,18 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
||||
replytext.Hide()
|
||||
replytext.Importance = widget.SuccessImportance
|
||||
replytext.Selectable = true
|
||||
// replytext.Wrapping = fyne.TextWrapWord
|
||||
replytext.Truncation = fyne.TextTruncateEllipsis
|
||||
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) {
|
||||
|
||||
vbox := co.(*fyne.Container)
|
||||
authorBox := vbox.Objects[1].(*fyne.Container)
|
||||
replytext := vbox.Objects[0].(*widget.Label)
|
||||
@@ -211,17 +201,41 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
||||
|
||||
author := authorBox.Objects[1].(*widget.Label)
|
||||
content := vbox.Objects[2].(*widget.Label)
|
||||
unknown_tags := chatTabs[chatJidStr].Messages[i].Raw.Unknown
|
||||
for _, v := range unknown_tags {
|
||||
if v.XMLName.Local == "occupant-id" {
|
||||
for _, attr := range v.Attrs {
|
||||
if attr.Name.Local == "id" {
|
||||
reason, ok := OccupantIdsToBlock[attr.Value]
|
||||
if ok {
|
||||
author.SetText("Ignored user")
|
||||
content.SetText("This user is ignored due to: " + reason)
|
||||
return // message is from blocked user
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
content.Importance = widget.DangerImportance
|
||||
}
|
||||
btn.Hidden = true // Hide by default
|
||||
msgContent := chatTabs[chatJidStr].Messages[i].Content
|
||||
if chatTabs[chatJidStr].Messages[i].ImageURL != "" {
|
||||
if chatTabs[chatJidStr].Messages[i].Raw.OutOfBandMedia != nil {
|
||||
btn.Hidden = false
|
||||
btn.OnTapped = 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 {
|
||||
fyne.Do(func() {
|
||||
dialog.ShowError(err, w)
|
||||
@@ -230,7 +244,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?
|
||||
url, err := url.Parse(chatTabs[chatJidStr].Messages[i].ImageURL)
|
||||
url, err := url.Parse(chatTabs[chatJidStr].Messages[i].Raw.OutOfBandMedia.URL)
|
||||
if err != nil {
|
||||
fyne.Do(func() {
|
||||
dialog.ShowError(err, w)
|
||||
@@ -263,7 +277,12 @@ func CreateUITab(chatJidStr string) ChatTabUI {
|
||||
content.SetText(msgContent)
|
||||
if chatTabs[chatJidStr].Messages[i].ReplyID != "PICLIENT:UNAVAILABLE" {
|
||||
reply := chatTabs[chatJidStr].Messages[i].Raw.Reply
|
||||
guy := jid.MustParse(reply.To).Resourcepart()
|
||||
j, err := jid.Parse(reply.To)
|
||||
if err != nil {
|
||||
log.Println("ERR: " + err.Error()) // FIXME
|
||||
return
|
||||
}
|
||||
guy := j.Resourcepart()
|
||||
// TODO: EXPERIMENTALLY GET REPLIED TO TEXT
|
||||
|
||||
for i := len(chatTabs[chatJidStr].Messages) - 1; i >= 0; i-- {
|
||||
@@ -350,7 +369,8 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
||||
}
|
||||
|
||||
fyne.Do(func() {
|
||||
AppTabs.Append(container.NewTabItemWithIcon(chatJid.String(), icon, myUITab.Scroller))
|
||||
myTab := container.NewTabItemWithIcon(chatJid.String(), icon, myUITab.Scroller)
|
||||
AppTabs.Append(myTab)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -422,10 +442,8 @@ func dropToSignInPage(reason string) {
|
||||
|
||||
func main() {
|
||||
muc.Since(time.Now())
|
||||
|
||||
config = piConfig{}
|
||||
a = app.NewWithID("pi-im")
|
||||
//a.Settings().SetTheme(&myTheme{})
|
||||
reader, err := a.Storage().Open("pi.xml")
|
||||
if err != nil {
|
||||
dropToSignInPage(err.Error())
|
||||
@@ -460,6 +478,7 @@ func main() {
|
||||
client.SetDmHandler(func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) {
|
||||
correction := false
|
||||
userJidStr := msg.From.Bare().String()
|
||||
fmt.Println(userJidStr)
|
||||
tab, ok := chatTabs[userJidStr]
|
||||
if ok {
|
||||
str := *msg.CleanedBody
|
||||
@@ -495,13 +514,14 @@ func main() {
|
||||
if msg.Reply == nil {
|
||||
replyID = "PICLIENT:UNAVAILABLE"
|
||||
} else {
|
||||
fmt.Println("Received reply in DM")
|
||||
replyID = msg.Reply.ID
|
||||
}
|
||||
|
||||
if correction {
|
||||
for i := len(tab.Messages) - 1; i > 0; i-- {
|
||||
if tab.Messages[i].Raw.From.String() == msg.From.String() {
|
||||
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
|
||||
tab.Messages[i].Content = *msg.CleanedBody + " (corrected)"
|
||||
fyne.Do(func() {
|
||||
UITabs[userJidStr].Scroller.Refresh()
|
||||
})
|
||||
@@ -517,21 +537,22 @@ func main() {
|
||||
ReplyID: replyID,
|
||||
Raw: *msg,
|
||||
ImageURL: img,
|
||||
Reactions: make(map[string]string),
|
||||
}
|
||||
|
||||
tab.Messages = append(tab.Messages, myMessage)
|
||||
fyne.Do(func() {
|
||||
UITabs[userJidStr].Scroller.Refresh()
|
||||
//UITabs[userJidStr].Scroller.Refresh()
|
||||
if scrollDownOnNewMessage {
|
||||
UITabs[userJidStr].Scroller.ScrollToBottom()
|
||||
//UITabs[userJidStr].Scroller.ScrollToBottom()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
client.SetGroupChatHandler(func(client *oasisSdk.XmppClient, muc *muc.Channel, msg *oasisSdk.XMPPChatMessage) {
|
||||
// HACK: IGNORING ALL MESSAGES FROM CLASSIC MUC HISTORY IN PREPARATION OF MAM SUPPORT
|
||||
ignore := false
|
||||
reaction := false
|
||||
correction := false
|
||||
important := false
|
||||
donotnotify := false
|
||||
@@ -549,11 +570,17 @@ func main() {
|
||||
correction = true
|
||||
break // dont need to look at more fields
|
||||
}
|
||||
|
||||
if v.XMLName.Local == "reactions" {
|
||||
reaction = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var ImageID string = ""
|
||||
mucJidStr := msg.From.Bare().String()
|
||||
if tab, ok := chatTabs[mucJidStr]; ok {
|
||||
//chatInfo.Objects[0] = widget.NewLabel(fmt.Sprintf("[!] %s", mucJidStr))
|
||||
chatTabs[mucJidStr].Muc = muc
|
||||
str := *msg.CleanedBody
|
||||
if strings.Contains(str, login.DisplayName) {
|
||||
@@ -588,10 +615,22 @@ func main() {
|
||||
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 {
|
||||
for i := len(tab.Messages) - 1; i > 0; i-- {
|
||||
if tab.Messages[i].Raw.From.String() == msg.From.String() {
|
||||
tab.Messages[i].Content = *msg.CleanedBody + " (edited)"
|
||||
tab.Messages[i].Content = *msg.CleanedBody + " (corrected)"
|
||||
fyne.Do(func() {
|
||||
UITabs[mucJidStr].Scroller.Refresh()
|
||||
})
|
||||
@@ -608,6 +647,7 @@ func main() {
|
||||
Raw: *msg,
|
||||
ImageURL: ImageID,
|
||||
Important: important,
|
||||
Reactions: make(map[string]string),
|
||||
}
|
||||
if !ignore {
|
||||
tab.Messages = append(tab.Messages, myMessage)
|
||||
@@ -616,7 +656,10 @@ func main() {
|
||||
fyne.Do(func() {
|
||||
UITabs[mucJidStr].Scroller.Refresh()
|
||||
if scrollDownOnNewMessage {
|
||||
UITabs[mucJidStr].Scroller.ScrollToBottom()
|
||||
tab, ok := UITabs[mucJidStr]
|
||||
if ok {
|
||||
tab.Scroller.ScrollToBottom()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -649,9 +692,12 @@ func main() {
|
||||
})
|
||||
|
||||
client.SetPresenceHandler(func(client *oasisSdk.XmppClient, from jid.JID, p oasisSdk.UserPresence) {
|
||||
log.Println(p.Type)
|
||||
bareAcc := from.Bare()
|
||||
tab, ok := chatTabs[bareAcc.String()]
|
||||
if !ok {
|
||||
// User presence
|
||||
addChatTab(false, bareAcc, client.Login.DisplayName)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -661,12 +707,16 @@ func main() {
|
||||
})
|
||||
|
||||
client.SetBookmarkHandler(false, func(client *oasisSdk.XmppClient, bookmark bookmarks.Channel) {
|
||||
if !config.JoinBookmarks {
|
||||
return
|
||||
}
|
||||
// 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)
|
||||
@@ -723,6 +773,18 @@ func main() {
|
||||
a = app.New()
|
||||
|
||||
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))
|
||||
|
||||
entry := NewCustomMultiLineEntry()
|
||||
@@ -783,6 +845,26 @@ func main() {
|
||||
|
||||
}
|
||||
}, 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())
|
||||
}
|
||||
|
||||
a := pubsub.Fetch(context.TODO(), client.Session, pubsub.Query{})
|
||||
log.Println(a.Item())
|
||||
|
||||
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)
|
||||
|
||||
@@ -965,7 +1047,7 @@ func main() {
|
||||
}, w)
|
||||
})
|
||||
|
||||
leaveRoom := fyne.NewMenuItem("Leave current room", func() {
|
||||
leaveRoom := fyne.NewMenuItem("disconnect from current room", func() {
|
||||
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||
if !ok {
|
||||
return
|
||||
@@ -977,18 +1059,137 @@ func main() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !chatTabs[activeMucJid].isMuc {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
err1, err2 := client.LeaveMuc(activeMucJid, "cya suckers!", context.TODO())
|
||||
if err1 != nil || err2 != nil {
|
||||
dialog.ShowError(errors.Join(err1, err2), w) // beautiful...
|
||||
err := client.DisconnectMuc(activeMucJid, "user left on own accord", context.TODO())
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
}
|
||||
delete(UITabs, activeMucJid)
|
||||
})
|
||||
|
||||
joinARoom := fyne.NewMenuItem("Join a room", func() {
|
||||
dialog.ShowEntryDialog("Join a room", "JID:", func(s string) {
|
||||
manageBookmarks := fyne.NewMenuItem("manage bookmarks", func() {
|
||||
bookmarks := client.BookmarkCache()
|
||||
box := container.NewVBox()
|
||||
box.Add(widget.NewRichTextFromMarkdown("# Manage Bookmarks"))
|
||||
box.Add(widget.NewLabel(fmt.Sprintf("%d bookmarks", len(bookmarks))))
|
||||
for address, bookmark := range bookmarks {
|
||||
bookmarkWidget := container.NewGridWithColumns(7)
|
||||
|
||||
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
|
||||
var setNick *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()
|
||||
}()
|
||||
})
|
||||
|
||||
nickEntry := widget.NewEntry()
|
||||
nickEntry.SetText(bookmark.Nick)
|
||||
|
||||
setNick = widget.NewButton("Set nick", func() {
|
||||
go func() {
|
||||
fyne.Do(func() {setNick.Disable()})
|
||||
newNick := nickEntry.Text
|
||||
bookmark.Nick = newNick
|
||||
err := client.PublishBookmark(bookmark, context.TODO())
|
||||
if err != nil {
|
||||
fyne.Do(func() {
|
||||
setNick.Enable()
|
||||
dialog.ShowError(err, w)
|
||||
})
|
||||
return
|
||||
}
|
||||
client.RefreshBookmarks(false)
|
||||
fyne.Do(func() {setNick.Enable()})
|
||||
}()
|
||||
})
|
||||
|
||||
bookmarkWidget.Add(autojoinCheck)
|
||||
bookmarkWidget.Add(deleteBookmarkButton)
|
||||
bookmarkWidget.Add(joinRoomButton)
|
||||
bookmarkWidget.Add(nickEntry)
|
||||
bookmarkWidget.Add(setNick)
|
||||
|
||||
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
|
||||
gif, err := extraWidgets.NewAnimatedGifFromResource(i)
|
||||
if err != nil {
|
||||
@@ -1049,109 +1250,9 @@ func main() {
|
||||
|
||||
})
|
||||
|
||||
/*
|
||||
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()
|
||||
/*
|
||||
menu_help := fyne.NewMenu("π", mit, reconnect, licensesbtn)
|
||||
|
||||
myBox := container.NewGridWithColumns(1, widget.NewLabel("Items"))
|
||||
info, err := disco.GetInfo(client.Ctx, "", search, client.Session)
|
||||
if err != nil {
|
||||
d.Hide()
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
m := info.Identity
|
||||
bytes, err := xml.MarshalIndent(m, "", "\t")
|
||||
if err != nil {
|
||||
d.Hide()
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
fyne.Do(func() {d.Hide()})
|
||||
myBox.Objects = append(myBox.Objects, widget.NewLabel(string(bytes)))
|
||||
dialog.ShowCustom("Service discovery", "cancel", myBox, w)
|
||||
}()
|
||||
|
||||
}, 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)
|
||||
menu_changeroom := fyne.NewMenu("Α", mic, beginADM, joinARoom, leaveRoom, manageBookmarks)
|
||||
menu_configureview := fyne.NewMenu("Β", mia, jtt, jtb)
|
||||
hafjag := fyne.NewMenuItem("Hafjag", func() {
|
||||
entry.Text = "Hafjag"
|
||||
@@ -1184,6 +1285,40 @@ func main() {
|
||||
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() {
|
||||
entry.Text = fmt.Sprintf("It is currently %s", time.Now().Format(time.RFC850))
|
||||
SendCallback()
|
||||
@@ -1240,7 +1375,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() {
|
||||
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||
if !ok {
|
||||
@@ -1289,6 +1424,48 @@ func main() {
|
||||
dialog.ShowCustom("Message", "Close", pre, w)
|
||||
})
|
||||
|
||||
blck := fyne.NewMenuItem("ignore messages from this user", 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
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
m := chatTabs[activeChatJid].Messages[selectedId]
|
||||
unknown_tags := m.Raw.Unknown
|
||||
for _, v := range unknown_tags {
|
||||
if v.XMLName.Local == "occupant-id" {
|
||||
for _, attr := range v.Attrs {
|
||||
if attr.Name.Local == "id" {
|
||||
occupant_id := attr.Value
|
||||
reason, ok := OccupantIdsToBlock[occupant_id]
|
||||
if !ok {
|
||||
dialog.ShowConfirm("ignore user", "All messages sent by users with an occupant ID of\n"+occupant_id+"\nwill be ignored. Continue?", func(b bool) {
|
||||
if b {
|
||||
OccupantIdsToBlock[occupant_id] = "User requested"
|
||||
}
|
||||
}, w)
|
||||
} else {
|
||||
dialog.ShowConfirm("unignore user", "This user is currently ignored due to:\n"+reason+"\nWould you like to unignore them?", func(b bool) {
|
||||
if b {
|
||||
delete(OccupantIdsToBlock, occupant_id)
|
||||
}
|
||||
}, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
red := fyne.NewMenuItem("show read receipts on message", func() {
|
||||
pre := container.NewVBox()
|
||||
|
||||
@@ -1325,28 +1502,52 @@ func main() {
|
||||
dialog.ShowCustom("Message", "Close", pre, w)
|
||||
})
|
||||
|
||||
menu_messageoptions := fyne.NewMenu("Γ", bit, bia, bic, red)
|
||||
menu_messageoptions := fyne.NewMenu("Γ", bit, bia, bic, red, blck)
|
||||
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions, menu_jokes)
|
||||
w.SetMainMenu(ma)
|
||||
|
||||
desk, ok := a.(desktop.App)
|
||||
if ok {
|
||||
desk.SetSystemTrayMenu(menu_help)
|
||||
desk.SetSystemTrayMenu(fyne.NewMenu("", fyne.NewMenuItem("show", w.Show)))
|
||||
}
|
||||
|
||||
AppTabs = container.NewAppTabs(
|
||||
container.NewTabItem("τίποτα", widget.NewLabel(`
|
||||
AppTabs = container.NewDocTabs(
|
||||
container.NewTabItem("...", widget.NewLabel(`
|
||||
pi
|
||||
|
||||
This tab will be used for displaying certain actions, such as
|
||||
managing your bookmarks and configuring rooms.
|
||||
`)),
|
||||
)
|
||||
|
||||
/*
|
||||
for _, mucJidStr := range login.MucsToJoin {
|
||||
mucJid, err := jid.Parse(mucJidStr)
|
||||
if err == nil {
|
||||
addChatTab(true, mucJid, login.DisplayName)
|
||||
AppTabs.CloseIntercept = func(ti *container.TabItem) {
|
||||
go func() {
|
||||
var activeChatJid string
|
||||
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 {
|
||||
fmt.Println(userJidStr)
|
||||
@@ -1357,6 +1558,10 @@ func main() {
|
||||
}
|
||||
|
||||
AppTabs.OnSelected = func(ti *container.TabItem) {
|
||||
if AppTabs.Selected() == AppTabs.Items[0] {
|
||||
chatSidebar.Hidden = true
|
||||
return
|
||||
}
|
||||
selectedScroller, ok := AppTabs.Selected().Content.(*widget.List)
|
||||
if !ok {
|
||||
return
|
||||
@@ -1372,17 +1577,18 @@ func main() {
|
||||
|
||||
tab := chatTabs[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
|
||||
box := container.NewVBox(widget.NewRichTextFromMarkdown("# "+activeChatJid), widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members))))
|
||||
if tab.isMuc {
|
||||
nameLabel := widget.NewRichTextFromMarkdown("# " + activeChatJid)
|
||||
nameLabel.Truncation = fyne.TextTruncateEllipsis
|
||||
memberLabel := widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members)))
|
||||
memberLabel.Truncation = fyne.TextTruncateEllipsis
|
||||
box := container.NewVBox(nameLabel, )
|
||||
chatSidebar.Objects = []fyne.CanvasObject{}
|
||||
|
||||
for name := range tab.Members {
|
||||
for name, p := range tab.Members {
|
||||
log.Println(string(p.Type))
|
||||
gen, _ := identicon.New("github", 5, 3)
|
||||
userjid, err := jid.Parse(name)
|
||||
if err != nil {
|
||||
@@ -1390,30 +1596,64 @@ func main() {
|
||||
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(25)
|
||||
im := ii.Image(15)
|
||||
imageWidget := canvas.NewImageFromImage(im)
|
||||
imageWidget.FillMode = canvas.ImageFillOriginal
|
||||
imageWidget.Refresh()
|
||||
box.Add(container.NewHBox(imageWidget, widget.NewLabel(nickname), widget.NewButton("Mention", mention)))
|
||||
nickLabel := widget.NewLabel(nickname)
|
||||
nickLabel.Selectable = true
|
||||
nickLabel.Truncation = fyne.TextTruncateClip
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
chatSidebar = *container.NewGridWithColumns(1, container.NewVScroll(box))
|
||||
} else {
|
||||
chatSidebar = *container.NewVBox(widget.NewRichTextFromMarkdown("# " + activeChatJid))
|
||||
}
|
||||
chatSidebar.Hidden = false
|
||||
chatSidebar.Refresh()
|
||||
|
||||
}
|
||||
|
||||
// HACK - disable chatsidebar because it's currently very buggy
|
||||
chatSidebar.Hidden = false
|
||||
statBar.SetText("")
|
||||
w.SetContent(container.NewVSplit(container.NewVSplit(container.NewHSplit(AppTabs, &chatSidebar), 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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user