Compare commits
10 Commits
63bb323ac3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6626d35920 | |||
| 5c76729a6b | |||
| c260b8b231 | |||
| 971147dcb8 | |||
| 777df725b6 | |||
| 6cb8771994 | |||
| ac013e7969 | |||
| 067a74e157 | |||
| 1dcd55d5ff | |||
| e87369912d |
BIN
assets/admin.png
Normal file
|
After Width: | Height: | Size: 600 B |
BIN
assets/cancel.png
Normal file
|
After Width: | Height: | Size: 607 B |
BIN
assets/icon.ico
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
assets/icon.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/member.png
Normal file
|
After Width: | Height: | Size: 597 B |
BIN
assets/noaffiliation.png
Normal file
|
After Width: | Height: | Size: 591 B |
BIN
assets/outcast.png
Normal file
|
After Width: | Height: | Size: 538 B |
BIN
assets/owner.png
Normal file
|
After Width: | Height: | Size: 584 B |
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
"github.com/diamondburned/gotk4/pkg/pango"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
Jid "mellium.im/xmpp/jid"
|
||||
)
|
||||
|
||||
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
||||
@@ -17,10 +17,12 @@ func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
||||
})
|
||||
}
|
||||
|
||||
func createTab(jid string, isMuc bool) {
|
||||
func createTab(jid string, isMuc bool) bool {
|
||||
fmt.Println("Creating tab", jid, "isMuc:", isMuc)
|
||||
_, ok := tabs.Load(jid)
|
||||
if !ok {
|
||||
_, uok := userdevices.Load(jid)
|
||||
_, mok := mucmembers.Load(jid)
|
||||
if !ok && !uok && !mok {
|
||||
newTab := new(chatTab)
|
||||
newTab.isMuc = isMuc
|
||||
newTab.msgs = gtk.NewListBox()
|
||||
@@ -29,7 +31,10 @@ func createTab(jid string, isMuc bool) {
|
||||
|
||||
newTab.msgs.Append(gtk.NewButtonWithLabel("Get past messages..."))
|
||||
tabs.Store(jid, newTab)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func switchToTab(jid string, w *gtk.Window) {
|
||||
@@ -43,8 +48,14 @@ func switchToTab(jid string, w *gtk.Window) {
|
||||
|
||||
scroller.SetChild(typed_tab.msgs)
|
||||
if typed_tab.isMuc {
|
||||
m, _ := mucmembers.Load(jid)
|
||||
ma := m.(mucUnit)
|
||||
m, ok := mucmembers.Load(jid)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ma, ok := m.(mucUnit)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
mm := ma.Members
|
||||
gen := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
|
||||
@@ -57,9 +68,43 @@ func switchToTab(jid string, w *gtk.Window) {
|
||||
u.Get(&mu)
|
||||
u.Get(&ocu)
|
||||
|
||||
nick_label := gtk.NewLabel(Jid.MustParse(u.From).Resourcepart())
|
||||
nick_label := gtk.NewLabel(JidMustParse(u.From).Resource)
|
||||
nick_label.SetEllipsize(pango.EllipsizeEnd)
|
||||
/*
|
||||
affil_label := gtk.NewLabel("")
|
||||
switch mu.MucUserItem.Affiliation {
|
||||
case "owner":
|
||||
affil_label.SetText("O")
|
||||
case "admin":
|
||||
affil_label.SetText("A")
|
||||
case "member":
|
||||
affil_label.SetText("M")
|
||||
case "none":
|
||||
affil_label.SetText("-")
|
||||
}
|
||||
*/
|
||||
|
||||
nick_label.AddCSSClass(mu.MucUserItem.Role)
|
||||
if mu.MucUserItem.Role == "visitor" {
|
||||
nick_label.SetOpacity(0.5)
|
||||
}
|
||||
|
||||
/*
|
||||
affil_label.SetHAlign(gtk.AlignEnd)
|
||||
affil_label.SetHExpand(true)
|
||||
|
||||
|
||||
affil_label.AddCSSClass(mu.MucUserItem.Affiliation)
|
||||
*/
|
||||
|
||||
userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\nRight-click for more information", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation))
|
||||
userbox.Append(nick_label)
|
||||
// userbox.Append(affil_label)
|
||||
|
||||
medal := gtk.NewImageFromPaintable(clientAssets[mu.MucUserItem.Affiliation])
|
||||
medal.SetHAlign(gtk.AlignEnd)
|
||||
medal.SetHExpand(true)
|
||||
userbox.Append(medal)
|
||||
|
||||
gesture := gtk.NewGestureClick()
|
||||
gesture.SetButton(3) // Right click
|
||||
@@ -68,7 +113,9 @@ func switchToTab(jid string, w *gtk.Window) {
|
||||
win := gtk.NewWindow()
|
||||
win.SetDefaultSize(400, 400)
|
||||
profile_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
nick := gtk.NewLabel(Jid.MustParse(u.From).Resourcepart())
|
||||
nick := gtk.NewLabel(JidMustParse(u.From).Resource)
|
||||
|
||||
win.SetTitle(JidMustParse(u.From).Resource)
|
||||
nick.AddCSSClass("author")
|
||||
profile_box.Append(nick)
|
||||
profile_box.Append(gtk.NewLabel(u.From))
|
||||
@@ -96,6 +143,16 @@ func switchToTab(jid string, w *gtk.Window) {
|
||||
}
|
||||
}
|
||||
|
||||
var mu MucUser
|
||||
ok = u.Get(&mu)
|
||||
if ok {
|
||||
if mu.MucUserItem.JID != "" {
|
||||
profile_box.Append(gtk.NewLabel(mu.MucUserItem.JID))
|
||||
}
|
||||
profile_box.Append(gtk.NewLabel("Connected with role " + mu.MucUserItem.Role))
|
||||
profile_box.Append(gtk.NewLabel("Affiliated as " + mu.MucUserItem.Affiliation))
|
||||
}
|
||||
|
||||
go func() {
|
||||
ctx := context.TODO()
|
||||
mychan, err := client.SendIQ(ctx, iqResp)
|
||||
@@ -116,7 +173,7 @@ func switchToTab(jid string, w *gtk.Window) {
|
||||
}()
|
||||
|
||||
go func() {
|
||||
mo, _ := mucmembers.Load(Jid.MustParse(u.From).Bare().String())
|
||||
mo, _ := mucmembers.Load(JidMustParse(u.From).Bare())
|
||||
mm := mo.(mucUnit)
|
||||
mmm := mm.Members
|
||||
mmmm, ok := mmm.Load(ocu.ID)
|
||||
@@ -149,8 +206,11 @@ func switchToTab(jid string, w *gtk.Window) {
|
||||
win.Present()
|
||||
})
|
||||
userbox.AddController(gesture)
|
||||
|
||||
if mu.MucUserItem.Role == "moderator" {
|
||||
gen.Prepend(userbox)
|
||||
} else {
|
||||
gen.Append(userbox)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"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"
|
||||
@@ -32,9 +33,9 @@ func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
||||
}
|
||||
}
|
||||
|
||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " left the room")
|
||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " left the MUC")
|
||||
} else {
|
||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " joined the room")
|
||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " joined the MUC")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +44,35 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
if !ok {
|
||||
return gtk.NewLabel("Unsupported message.")
|
||||
}
|
||||
fmt.Println(m.Body)
|
||||
|
||||
readmarker := Marker{}
|
||||
ok = m.Get(&readmarker)
|
||||
if ok {
|
||||
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
b.Append(gtk.NewLabel(fmt.Sprintf("%s has read to this point", JidMustParse(m.From).Resource)))
|
||||
return b
|
||||
}
|
||||
|
||||
indicator := stanza.StateComposing{}
|
||||
ok = m.Get(&indicator)
|
||||
if ok { // TODO: Display typing indicator in a stat bar or something similar
|
||||
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
b.Append(gtk.NewLabel(fmt.Sprintf("%s is typing...", JidMustParse(m.From).Resource)))
|
||||
return b
|
||||
}
|
||||
|
||||
if m.Error.Type != "" {
|
||||
error_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
cancel_img := gtk.NewImageFromPaintable(clientAssets["cancel"])
|
||||
error_label := gtk.NewLabel(m.Error.Text)
|
||||
|
||||
error_box.Append(cancel_img)
|
||||
error_box.Append(error_label)
|
||||
return error_box
|
||||
}
|
||||
|
||||
|
||||
sid := StanzaID{}
|
||||
m.Get(&sid)
|
||||
|
||||
@@ -50,17 +80,21 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
gesture := gtk.NewGestureClick()
|
||||
gesture.SetButton(3) // Right click
|
||||
|
||||
vis := false
|
||||
reactions := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
reactions.SetVisible(false)
|
||||
|
||||
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")
|
||||
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'>
|
||||
@@ -72,14 +106,30 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
reactions.Append(like)
|
||||
}
|
||||
|
||||
gesture.Connect("pressed", func(n_press, x, y int) {
|
||||
if !vis {
|
||||
vis = true
|
||||
reactions.SetVisible(true)
|
||||
} else {
|
||||
vis = false
|
||||
reactions.SetVisible(false)
|
||||
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)
|
||||
@@ -102,6 +152,9 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
|
||||
// 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)
|
||||
@@ -118,25 +171,24 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
im.AddCSSClass("author_img")
|
||||
authorBox.Append(im)
|
||||
} else {
|
||||
im := newImageFromPath("debug.png")
|
||||
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||
im.SetPixelSize(40)
|
||||
im.AddCSSClass("author_img")
|
||||
authorBox.Append(im)
|
||||
}
|
||||
} else {
|
||||
im := newImageFromPath("debug.png")
|
||||
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")
|
||||
}
|
||||
|
||||
al := gtk.NewLabel(jid.MustParse(m.From).Resourcepart())
|
||||
al.AddCSSClass("author")
|
||||
|
||||
authorBox.Append(al)
|
||||
mlabel := gtk.NewLabel(m.Body)
|
||||
// mlabel.SetMarkup(convertXEPToPango(m.Body))
|
||||
mlabel.SetWrap(true)
|
||||
mlabel.SetSelectable(true)
|
||||
mlabel.SetHAlign(gtk.AlignFill)
|
||||
@@ -174,12 +226,12 @@ func getVAdjustment(scrolledWindow *gtk.ScrolledWindow) *gtk.Adjustment {
|
||||
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 newImageFromPath("debug.png")
|
||||
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||
}
|
||||
|
||||
if hash == "" {
|
||||
fmt.Println("Hash is nil!")
|
||||
return newImageFromPath("debug.png")
|
||||
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||
}
|
||||
|
||||
hash = filepath.Join(p, sanitizefilename.Sanitize(hash))
|
||||
@@ -211,12 +263,12 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou
|
||||
result := <-mychan
|
||||
card, ok := result.Payload.(*VCard)
|
||||
if !ok {
|
||||
return newImageFromPath("debug.png")
|
||||
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||
}
|
||||
|
||||
base64_data := card.Photo.Binval
|
||||
if card.Photo.Binval == "" || (card.Photo.Type == "image/svg+xml" && runtime.GOOS == "windows") {
|
||||
return newImageFromPath("debug.png")
|
||||
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(base64_data)
|
||||
|
||||
@@ -26,25 +26,31 @@ func dropToSignInPage(err error) {
|
||||
insecure_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
|
||||
server_label := gtk.NewLabel("Server: ")
|
||||
username_label := gtk.NewLabel("Username: ")
|
||||
username_label := gtk.NewLabel("JID: ")
|
||||
password_label := gtk.NewLabel("Password: ")
|
||||
nickname_label := gtk.NewLabel("Nickname: ")
|
||||
insecure_label := gtk.NewLabel("Insecure: ")
|
||||
insecure_label := gtk.NewLabel("Insecure: (?)")
|
||||
insecure_label.SetTooltipText("Tick this if you need to connect without TLS, usually for connecting to Tor XMPP servers")
|
||||
|
||||
server_entry := gtk.NewEntry()
|
||||
server_entry.SetHAlign(gtk.AlignEnd)
|
||||
server_entry.SetHExpand(true)
|
||||
|
||||
username_entry := gtk.NewEntry()
|
||||
username_entry.SetHAlign(gtk.AlignEnd)
|
||||
username_entry.SetHExpand(true)
|
||||
|
||||
password_entry := gtk.NewPasswordEntry()
|
||||
password_entry.SetHAlign(gtk.AlignEnd)
|
||||
password_entry.SetHExpand(true)
|
||||
|
||||
nickname_entry := gtk.NewEntry()
|
||||
nickname_entry.SetHAlign(gtk.AlignEnd)
|
||||
nickname_entry.SetHExpand(true)
|
||||
|
||||
insecure_check := gtk.NewCheckButton()
|
||||
insecure_check.SetHAlign(gtk.AlignEnd)
|
||||
insecure_check.SetHExpand(true)
|
||||
|
||||
|
||||
server_box.Append(server_label)
|
||||
|
||||
250
main.go
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/diamondburned/gotk4/pkg/gio/v2"
|
||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
"github.com/kr/pretty"
|
||||
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"encoding/xml"
|
||||
"math/rand/v2"
|
||||
"runtime"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
var loadedConfig lambdaConfig
|
||||
@@ -55,6 +56,37 @@ var mucmembers sync.Map
|
||||
// stores devices of users
|
||||
var userdevices sync.Map
|
||||
|
||||
//go:embed debug.png
|
||||
var defaultAvatarBytes []byte
|
||||
var defaultAvatarB64 string = base64.StdEncoding.EncodeToString(defaultAvatarBytes)
|
||||
|
||||
//go:embed assets/owner.png
|
||||
var ownerMedalBytes []byte
|
||||
var ownerMedalB64 string = base64.StdEncoding.EncodeToString(ownerMedalBytes)
|
||||
|
||||
//go:embed assets/admin.png
|
||||
var adminMedalBytes []byte
|
||||
var adminMedalB64 string = base64.StdEncoding.EncodeToString(adminMedalBytes)
|
||||
|
||||
//go:embed assets/member.png
|
||||
var memberMedalBytes []byte
|
||||
var memberMedalB64 string = base64.StdEncoding.EncodeToString(memberMedalBytes)
|
||||
|
||||
//go:embed assets/noaffiliation.png
|
||||
var noneMedalBytes []byte
|
||||
var noneMedalB64 string = base64.StdEncoding.EncodeToString(noneMedalBytes)
|
||||
|
||||
//go:embed assets/outcast.png
|
||||
var outcastMedalBytes []byte
|
||||
var outcastMedalB64 string = base64.StdEncoding.EncodeToString(outcastMedalBytes)
|
||||
|
||||
//go:embed assets/cancel.png
|
||||
var cancelBytes []byte
|
||||
var cancelB64 string = base64.StdEncoding.EncodeToString(cancelBytes)
|
||||
|
||||
var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
|
||||
var lockedJIDs map[string]bool = make(map[string]bool)
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
for fn := range uiQueue {
|
||||
@@ -65,8 +97,63 @@ func init() {
|
||||
time.Sleep(10 * time.Millisecond) // Small delay between updates
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
loader := gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
defaultAvatarData, _ := base64.StdEncoding.DecodeString(defaultAvatarB64)
|
||||
loader.Write(defaultAvatarData)
|
||||
loader.Close()
|
||||
clientAssets["DefaultAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
ownerMedalData, _ := base64.StdEncoding.DecodeString(ownerMedalB64)
|
||||
loader.Write(ownerMedalData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["owner"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
cancelData, _ := base64.StdEncoding.DecodeString(cancelB64)
|
||||
loader.Write(cancelData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["cancel"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
adminMedalData, _ := base64.StdEncoding.DecodeString(adminMedalB64)
|
||||
loader.Write(adminMedalData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["admin"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
memberMedalData, _ := base64.StdEncoding.DecodeString(memberMedalB64)
|
||||
loader.Write(memberMedalData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["member"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
noneMedalData, _ := base64.StdEncoding.DecodeString(noneMedalB64)
|
||||
loader.Write(noneMedalData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["none"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
|
||||
loader = gdkpixbuf.NewPixbufLoader()
|
||||
|
||||
outcastMedalData, _ := base64.StdEncoding.DecodeString(outcastMedalB64)
|
||||
loader.Write(outcastMedalData)
|
||||
loader.Close()
|
||||
|
||||
clientAssets["outcast"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||
}
|
||||
|
||||
func main() {
|
||||
p, err := ensureConfig()
|
||||
@@ -86,7 +173,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Put 4 random characters in front of lambda
|
||||
// Put 4 random characters at the end
|
||||
chars := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZλ"
|
||||
str := ""
|
||||
for range 4 {
|
||||
@@ -97,7 +184,7 @@ func main() {
|
||||
TransportConfiguration: xmpp.TransportConfiguration{
|
||||
Address: loadedConfig.Server,
|
||||
},
|
||||
Jid: loadedConfig.Username + "/lambda."+str,
|
||||
Jid: loadedConfig.Username + "/lambda." + str,
|
||||
Credential: xmpp.Password(loadedConfig.Password),
|
||||
Insecure: loadedConfig.Insecure,
|
||||
// StreamLogger: os.Stdout,
|
||||
@@ -165,6 +252,12 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
e := stanza.PubSubEvent{}
|
||||
ok = m.Get(&e)
|
||||
if ok {
|
||||
fmt.Println(e)
|
||||
}
|
||||
|
||||
/*
|
||||
if m.Body == "" {
|
||||
return
|
||||
@@ -174,7 +267,7 @@ func main() {
|
||||
originator := jid.MustParse(m.From).Bare().String()
|
||||
|
||||
glib.IdleAdd(func() {
|
||||
uiQueue <- func() {
|
||||
//uiQueue <- func() {
|
||||
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
ba, ok := generateMessageWidget(p).(*gtk.Box)
|
||||
if ok {
|
||||
@@ -190,7 +283,7 @@ func main() {
|
||||
} else {
|
||||
fmt.Println("Got message when the tab does not exist!")
|
||||
}
|
||||
}
|
||||
//}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -200,8 +293,6 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
pretty.Println(presence)
|
||||
|
||||
if presence.Error != *new(stanza.Err) {
|
||||
return
|
||||
}
|
||||
@@ -213,7 +304,8 @@ func main() {
|
||||
|
||||
if ok { // This is a presence stanza from a user in a MUC
|
||||
presence.Get(&ocu)
|
||||
muc := jid.MustParse(presence.From).Bare().String()
|
||||
from, _ := stanza.NewJid(presence.From)
|
||||
muc := from.Bare()
|
||||
_, ok = mucmembers.Load(muc)
|
||||
if !ok {
|
||||
mucmembers.Store(muc, mucUnit{})
|
||||
@@ -231,7 +323,6 @@ func main() {
|
||||
} else {
|
||||
typed_unit.Members.Delete(ocu.ID)
|
||||
glib.IdleAdd(func() {
|
||||
uiQueue <- func() {
|
||||
b := gtk.NewLabel("")
|
||||
ba, ok := generatePresenceWidget(p).(*gtk.Label)
|
||||
if ok {
|
||||
@@ -247,7 +338,6 @@ func main() {
|
||||
} else {
|
||||
fmt.Println("Got message when the tab does not exist!")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -259,8 +349,9 @@ func main() {
|
||||
_, ok := userdevices.Load(user)
|
||||
_, mok := mucmembers.Load(user)
|
||||
if !ok && !mok { // FIXME: The initial muc presence gets picked up from this check
|
||||
ok := createTab(user, false)
|
||||
if ok && !lockedJIDs[user]{
|
||||
userdevices.Store(user, userUnit{})
|
||||
createTab(user, false)
|
||||
|
||||
b := gtk.NewButtonWithLabel(user)
|
||||
b.ConnectClicked(func() {
|
||||
@@ -269,10 +360,10 @@ func main() {
|
||||
})
|
||||
menu.Append(b)
|
||||
}
|
||||
}
|
||||
|
||||
unit, ok := userdevices.Load(user)
|
||||
if !ok {
|
||||
fmt.Println("Could not load user presence even after recreating it! Something weird is going on!")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -287,7 +378,9 @@ func main() {
|
||||
}
|
||||
|
||||
userdevices.Store(user, typed_unit)
|
||||
lockedJIDs[user] = true
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
})
|
||||
|
||||
c, err := xmpp.NewClient(&config, router, func(err error) {
|
||||
@@ -332,38 +425,83 @@ func activate(app *gtk.Application) {
|
||||
)
|
||||
|
||||
window = gtk.NewApplicationWindow(app)
|
||||
the_menu := gio.NewMenu()
|
||||
|
||||
fileMenu := gio.NewMenu()
|
||||
fileMenu.Append("Join MUC", "app.join")
|
||||
fileMenu.Append("Start DM", "app.dm")
|
||||
|
||||
joinAction := gio.NewSimpleAction("join", nil)
|
||||
joinAction.ConnectActivate(func(p *glib.Variant) {
|
||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
jid_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
nick_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
|
||||
jid_entry := gtk.NewEntry()
|
||||
nick_entry := gtk.NewEntry()
|
||||
|
||||
jid_entry.SetHAlign(gtk.AlignEnd)
|
||||
jid_entry.SetHExpand(true)
|
||||
|
||||
nick_entry.SetHAlign(gtk.AlignEnd)
|
||||
nick_entry.SetHExpand(true)
|
||||
|
||||
nick_entry.SetText(loadedConfig.Nick)
|
||||
|
||||
jid_box.Append(gtk.NewLabel("MUC JID:"))
|
||||
jid_box.Append(jid_entry)
|
||||
|
||||
nick_box.Append(gtk.NewLabel("Nick:"))
|
||||
nick_box.Append(nick_entry)
|
||||
|
||||
box.Append(jid_box)
|
||||
box.Append(nick_box)
|
||||
|
||||
btn := gtk.NewButtonWithLabel("Submit")
|
||||
btn.SetVAlign(gtk.AlignBaseline)
|
||||
|
||||
box.Append(btn)
|
||||
|
||||
win := gtk.NewWindow()
|
||||
win.SetTitle("Join MUC")
|
||||
win.SetDefaultSize(200, 200)
|
||||
win.SetChild(box)
|
||||
|
||||
btn.ConnectClicked(func() {
|
||||
t := jid_entry.Text()
|
||||
_, ok := tabs.Load(t)
|
||||
if !ok {
|
||||
err := joinMuc(client, clientroot.Session.BindJid, t, nick_entry.Text())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
createTab(t, true)
|
||||
b := gtk.NewButtonWithLabel(t)
|
||||
b.ConnectClicked(func() {
|
||||
b.AddCSSClass("accent")
|
||||
switchToTab(t, &window.Window)
|
||||
})
|
||||
menu.Append(b)
|
||||
}
|
||||
win.SetVisible(false)
|
||||
})
|
||||
|
||||
win.SetTransientFor(win)
|
||||
win.Present()
|
||||
})
|
||||
|
||||
app.AddAction(joinAction)
|
||||
|
||||
the_menu.AppendSubmenu("File", fileMenu)
|
||||
|
||||
the_menuBar := gtk.NewPopoverMenuBarFromModel(the_menu)
|
||||
app.SetMenubar(gio.NewMenu())
|
||||
|
||||
window.SetTitle("Lambda")
|
||||
window.Window.AddCSSClass("ssd")
|
||||
window.Window.SetDefaultSize(500, 500)
|
||||
menu = gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
/*
|
||||
f_menu := gtk.NewMenuButton()
|
||||
f_menu.SetLabel("File")
|
||||
f_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
e_menu := gtk.NewMenuButton()
|
||||
e_menu.SetLabel("Edit")
|
||||
e_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
v_menu := gtk.NewMenuButton()
|
||||
v_menu.SetLabel("View")
|
||||
v_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
b_menu := gtk.NewMenuButton()
|
||||
b_menu.SetLabel("Bookmarks")
|
||||
b_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
h_menu := gtk.NewMenuButton()
|
||||
h_menu.SetLabel("Help")
|
||||
h_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
menu.Append(f_menu)
|
||||
menu.Append(e_menu)
|
||||
menu.Append(v_menu)
|
||||
menu.Append(b_menu)
|
||||
menu.Append(h_menu)
|
||||
*/
|
||||
|
||||
empty_dialog = gtk.NewLabel("You are not focused on any chats.")
|
||||
empty_dialog.SetVExpand(true)
|
||||
@@ -375,6 +513,8 @@ func activate(app *gtk.Application) {
|
||||
memberList.SetHExpand(true)
|
||||
|
||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
box.Append(the_menuBar)
|
||||
|
||||
// scroller.SetChild(empty_dialog)
|
||||
scroller.SetChild(empty_dialog)
|
||||
menu_scroll := gtk.NewScrolledWindow()
|
||||
@@ -388,10 +528,12 @@ func activate(app *gtk.Application) {
|
||||
chat_pane := gtk.NewPaned(gtk.OrientationHorizontal)
|
||||
chat_pane.SetStartChild(scroller)
|
||||
chat_pane.SetEndChild(memberList)
|
||||
chat_pane.SetPosition(225)
|
||||
|
||||
main_pane := gtk.NewPaned(gtk.OrientationHorizontal)
|
||||
main_pane.SetStartChild(menu_scroll)
|
||||
main_pane.SetEndChild(chat_pane)
|
||||
main_pane.SetPosition(135)
|
||||
|
||||
chatbox.Append(main_pane)
|
||||
box.Append(chatbox)
|
||||
@@ -437,37 +579,9 @@ func activate(app *gtk.Application) {
|
||||
|
||||
en.SetHExpand(true)
|
||||
|
||||
m_entry := gtk.NewEntry()
|
||||
|
||||
entry_box.Append(en)
|
||||
entry_box.Append(b)
|
||||
|
||||
entry_box.Append(m_entry)
|
||||
|
||||
debug_btn := gtk.NewButtonWithLabel("Join muc")
|
||||
|
||||
debug_btn.ConnectClicked(func() {
|
||||
t := en.Text()
|
||||
_, ok := tabs.Load(t)
|
||||
if !ok {
|
||||
err := joinMuc(client, clientroot.Session.BindJid, t, m_entry.Text())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
createTab(t, true)
|
||||
b := gtk.NewButtonWithLabel(t)
|
||||
b.ConnectClicked(func() {
|
||||
b.AddCSSClass("accent")
|
||||
switchToTab(t, &window.Window)
|
||||
})
|
||||
menu.Append(b)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
entry_box.Append(debug_btn)
|
||||
|
||||
box.Append(entry_box)
|
||||
|
||||
window.SetChild(box)
|
||||
|
||||
BIN
rsrc_windows_amd64.syso
Normal file
23
style.css
@@ -10,3 +10,26 @@
|
||||
.author_img {
|
||||
border-radius 100%;
|
||||
}
|
||||
|
||||
.owner {
|
||||
background-color: red;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.admin {
|
||||
background-color: orange;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.member {
|
||||
background-color: lime;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.moderator {
|
||||
color: magenta;
|
||||
}
|
||||
|
||||
.visitor {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
19
xmpp-displayed_markers.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
// Partial implementation of XEP-0333: Displayed Markers
|
||||
// https://xmpp.org/extensions/xep-0333.html
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
)
|
||||
|
||||
type Marker struct {
|
||||
stanza.MsgExtension
|
||||
XMLName xml.Name `xml:"urn:xmpp:chat-markers:0 displayed"`
|
||||
ID string `xml:"id,attr"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:chat-markers:0", Local: "displayed"}, Marker{})
|
||||
}
|
||||
@@ -46,3 +46,14 @@ func joinMuc(c xmpp.Sender, jid string, muc string, nick string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// jid MustParse but using gosrc's instead of mellium
|
||||
// This function will panic if its an invalid JID
|
||||
|
||||
func JidMustParse(s string) (*stanza.Jid) {
|
||||
j, err := stanza.NewJid(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||