266 lines
6.4 KiB
Go
266 lines
6.4 KiB
Go
package main
|
|
|
|
// This file contains the function that generates message widgets depending on message stanza
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
|
"github.com/google/uuid"
|
|
"github.com/jacoblockett/sanitizefilename"
|
|
"github.com/jasonlovesdoggo/gopen"
|
|
"gosrc.io/xmpp/stanza"
|
|
"mellium.im/xmpp/jid"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
|
presence, ok := p.(stanza.Presence)
|
|
if !ok {
|
|
return gtk.NewLabel("Unsupported message.")
|
|
}
|
|
|
|
if presence.Type == stanza.PresenceTypeUnavailable {
|
|
var mu MucUser
|
|
ok := presence.Get(&mu)
|
|
if ok {
|
|
if mu.MucUserItem.Affiliation == "outcast" {
|
|
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " has been banned!")
|
|
}
|
|
}
|
|
|
|
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " left the MUC")
|
|
} else {
|
|
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " joined the MUC")
|
|
}
|
|
}
|
|
|
|
func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|
m, ok := p.(stanza.Message)
|
|
if !ok {
|
|
return gtk.NewLabel("Unsupported message.")
|
|
}
|
|
fmt.Println(m.Body)
|
|
|
|
readmarker := Marker{}
|
|
ok = m.Get(&readmarker)
|
|
if ok {
|
|
return gtk.NewLabel(fmt.Sprintf("%s has read to this point", JidMustParse(m.From).Resource))
|
|
}
|
|
|
|
|
|
sid := StanzaID{}
|
|
m.Get(&sid)
|
|
|
|
mainBox := gtk.NewBox(gtk.OrientationVertical, 0)
|
|
gesture := gtk.NewGestureClick()
|
|
gesture.SetButton(3) // Right click
|
|
|
|
|
|
popover := gtk.NewPopover()
|
|
popover.SetParent(mainBox)
|
|
popover.SetHasArrow(false)
|
|
|
|
rc_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
|
|
|
reactions := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
|
reaction := []string{"👍", "👎", "♥️", "🤣", "😭"}
|
|
for _, v := range reaction {
|
|
like := gtk.NewButton()
|
|
like.SetLabel(v)
|
|
like.SetHExpand(true)
|
|
like.ConnectClicked(func() {
|
|
fmt.Println("licked") // TODO: Implement proper support for reactions via extension
|
|
client.SendRaw(fmt.Sprintf(`
|
|
<message from='%s' to='%s' id='%s' type='%s'>
|
|
<reactions id='%s' xmlns='urn:xmpp:reactions:0'>
|
|
<reaction>%s</reaction>
|
|
</reactions>
|
|
</message>
|
|
`, m.To, jid.MustParse(m.From).Bare().String(), uuid.New().String(), m.Type, sid.ID, v))
|
|
})
|
|
reactions.Append(like)
|
|
}
|
|
|
|
rc_box.Append(reactions)
|
|
|
|
if m.Type == stanza.MessageTypeGroupchat {
|
|
moderate := gtk.NewButtonWithLabel("Moderate") // TODO: Implement proper support for moderations via extension
|
|
moderate.ConnectClicked(func() {
|
|
client.SendRaw(fmt.Sprintf(`
|
|
<iq type='set' to='%s' id='%s'>
|
|
<moderate id='%s' xmlns='urn:xmpp:message-moderate:1'>
|
|
<retract xmlns='urn:xmpp-message-retract:1'/>
|
|
<reason>Retracted</reason>
|
|
</moderate>
|
|
</iq>
|
|
`, jid.MustParse(m.From).Bare().String(), uuid.New().String(), sid.ID))
|
|
})
|
|
rc_box.Append(moderate)
|
|
}
|
|
|
|
|
|
popover.SetChild(rc_box)
|
|
|
|
gesture.Connect("pressed", func(n_press, x, y int) {
|
|
rect := gdk.NewRectangle(x, y, 1, 1)
|
|
popover.SetPointingTo(&rect)
|
|
popover.Popup()
|
|
})
|
|
|
|
mainBox.AddController(gesture)
|
|
|
|
reply := Reply{}
|
|
ok = m.Get(&reply)
|
|
if ok {
|
|
replyBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
|
replyBox.Append(gtk.NewLabel("↱ " + jid.MustParse(reply.To).Resourcepart()))
|
|
mainBox.Append(replyBox)
|
|
}
|
|
|
|
ocu := OccupantID{}
|
|
|
|
m.Get(&ocu)
|
|
|
|
authorBox := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
|
contentBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
|
// im := newImageFromPath("debug.png")
|
|
|
|
// authorBox.Append(im)
|
|
|
|
al := gtk.NewLabel(jid.MustParse(m.From).Resourcepart())
|
|
al.AddCSSClass("author")
|
|
|
|
if m.Type == stanza.MessageTypeGroupchat {
|
|
mo, _ := mucmembers.Load(jid.MustParse(m.From).Bare().String())
|
|
mm := mo.(mucUnit)
|
|
mmm := mm.Members
|
|
mmmm, ok := mmm.Load(ocu.ID)
|
|
if ok {
|
|
pres := mmmm.(stanza.Presence)
|
|
|
|
var vu VCardUpdate
|
|
pres.Get(&vu)
|
|
if vu.Photo != "" {
|
|
im := getAvatar(m.From, vu.Photo)
|
|
im.SetPixelSize(40)
|
|
im.AddCSSClass("author_img")
|
|
authorBox.Append(im)
|
|
} else {
|
|
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
|
im.SetPixelSize(40)
|
|
im.AddCSSClass("author_img")
|
|
authorBox.Append(im)
|
|
}
|
|
} else {
|
|
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
|
im.SetPixelSize(40)
|
|
im.AddCSSClass("author_img")
|
|
authorBox.Append(im)
|
|
}
|
|
} else if m.Type == stanza.MessageTypeChat {
|
|
al.SetText(al.Text() + " whispers")
|
|
}
|
|
|
|
|
|
authorBox.Append(al)
|
|
mlabel := gtk.NewLabel(m.Body)
|
|
mlabel.SetWrap(true)
|
|
mlabel.SetSelectable(true)
|
|
mlabel.SetHAlign(gtk.AlignFill)
|
|
|
|
contentBox.Append(mlabel)
|
|
|
|
mainBox.Append(authorBox)
|
|
mainBox.Append(contentBox)
|
|
|
|
mainBox.Append(reactions)
|
|
|
|
oob := stanza.OOB{}
|
|
ok = m.Get(&oob)
|
|
if ok {
|
|
// media := newPictureFromWeb(oob.URL)
|
|
// media.SetCanShrink(false)
|
|
// mainBox.Append(media)
|
|
// media.AddCSSClass("chat_image")
|
|
mbtn := gtk.NewButtonWithLabel("🖼️")
|
|
// mbtn.SetChild(newImageFromWeb(oob.URL))
|
|
mbtn.ConnectClicked(func() {
|
|
gopen.Open(oob.URL)
|
|
})
|
|
authorBox.Append(mbtn)
|
|
}
|
|
|
|
return mainBox
|
|
}
|
|
|
|
func getVAdjustment(scrolledWindow *gtk.ScrolledWindow) *gtk.Adjustment {
|
|
val := scrolledWindow.ObjectProperty("vadjustment").(*gtk.Adjustment)
|
|
return val
|
|
}
|
|
|
|
func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shouldn't be here, and should probably be in xmpp-helpers or somewhere similar.
|
|
p, err := ensureCache()
|
|
if err != nil {
|
|
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
|
}
|
|
|
|
if hash == "" {
|
|
fmt.Println("Hash is nil!")
|
|
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
|
}
|
|
|
|
hash = filepath.Join(p, sanitizefilename.Sanitize(hash))
|
|
|
|
_, err = os.ReadFile(hash)
|
|
if err == nil {
|
|
return newImageFromPath(hash)
|
|
}
|
|
|
|
iqResp, err := stanza.NewIQ(stanza.Attrs{
|
|
Type: "get",
|
|
From: clientroot.Session.BindJid,
|
|
To: j,
|
|
Id: "vc2",
|
|
Lang: "en",
|
|
})
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
iqResp.Payload = &VCard{}
|
|
|
|
ctx := context.TODO()
|
|
mychan, err := client.SendIQ(ctx, iqResp)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
result := <-mychan
|
|
card, ok := result.Payload.(*VCard)
|
|
if !ok {
|
|
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
|
}
|
|
|
|
base64_data := card.Photo.Binval
|
|
if card.Photo.Binval == "" || (card.Photo.Type == "image/svg+xml" && runtime.GOOS == "windows") {
|
|
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
|
}
|
|
|
|
data, err := base64.StdEncoding.DecodeString(base64_data)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = os.WriteFile(hash, data, 0644)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return newImageFromPath(hash)
|
|
}
|