diff --git a/main.go b/main.go index 72a3494..7c756d1 100644 --- a/main.go +++ b/main.go @@ -58,6 +58,7 @@ type Message struct { Raw oasisSdk.XMPPChatMessage Important bool Readers []jid.JID + Reactions map[string]string } type ChatTab struct { @@ -135,7 +136,7 @@ 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 @@ -173,7 +174,10 @@ func CreateUITab(chatJidStr string) ChatTabUI { 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) @@ -193,16 +197,25 @@ func CreateUITab(chatJidStr string) ChatTabUI { author := authorBox.Objects[1].(*widget.Label) content := vbox.Objects[2].(*widget.Label) 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) @@ -211,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? - 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) @@ -331,7 +344,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) }) } @@ -492,12 +506,13 @@ func main() { } myMessage := Message{ - Author: msg.From.Resourcepart(), - Content: str, - ID: msg.ID, - ReplyID: replyID, - Raw: *msg, - ImageURL: img, + Author: msg.From.Resourcepart(), + Content: str, + ID: msg.ID, + ReplyID: replyID, + Raw: *msg, + ImageURL: img, + Reactions: make(map[string]string), } tab.Messages = append(tab.Messages, myMessage) @@ -512,6 +527,7 @@ func main() { client.SetGroupChatHandler(func(client *oasisSdk.XmppClient, muc *muc.Channel, msg *oasisSdk.XMPPChatMessage) { ignore := false + reaction := false correction := false important := false donotnotify := false @@ -529,13 +545,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)) - chatInfo.Refresh() chatTabs[mucJidStr].Muc = muc str := *msg.CleanedBody if strings.Contains(str, login.DisplayName) { @@ -570,6 +590,18 @@ 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() { @@ -590,6 +622,7 @@ func main() { Raw: *msg, ImageURL: ImageID, Important: important, + Reactions: make(map[string]string), } if !ignore { tab.Messages = append(tab.Messages, myMessage) @@ -709,6 +742,7 @@ func main() { 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() @@ -990,6 +1024,11 @@ 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) @@ -1370,7 +1409,7 @@ func main() { desk.SetSystemTrayMenu(fyne.NewMenu("", fyne.NewMenuItem("show", w.Show))) } - AppTabs = container.NewAppTabs( + AppTabs = container.NewDocTabs( container.NewTabItem("...", widget.NewLabel(` pi @@ -1379,6 +1418,35 @@ func main() { `)), ) + 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) DMjid, err := jid.Parse(userJidStr) @@ -1410,7 +1478,9 @@ func main() { chatSidebar = *UITab.Sidebar if tab.isMuc { - box := container.NewVBox(widget.NewRichTextFromMarkdown("# "+activeChatJid), widget.NewLabel(fmt.Sprintf("%d members ", len(tab.Members)))) + nameLabel := widget.NewRichTextFromMarkdown("# " + activeChatJid) + 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 { @@ -1432,7 +1502,7 @@ func main() { 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() @@ -1466,6 +1536,17 @@ func main() { chatSidebar.Hidden = false statBar.SetText("") chatInfo = *container.NewHBox(widget.NewLabel("")) - w.SetContent(container.NewVSplit(container.NewVSplit(container.NewHSplit(AppTabs, &chatSidebar), container.NewHSplit(entry, container.NewGridWithRows(1, sendbtn, replybtn))), container.NewHSplit(&statBar, &chatInfo))) + 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() }