diff --git a/gtk-helpers.go b/gtk-helpers.go index b08edab..602f5f8 100644 --- a/gtk-helpers.go +++ b/gtk-helpers.go @@ -17,7 +17,8 @@ func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) { } func createTab(jid string, isMuc bool) { - _, ok := tabs[jid] + fmt.Println("Creating tab", jid, "isMuc:", isMuc) + _, ok := tabs.Load(jid) if !ok { newTab := new(chatTab) newTab.isMuc = isMuc @@ -26,36 +27,47 @@ func createTab(jid string, isMuc bool) { newTab.msgs.SetShowSeparators(true) newTab.msgs.Append(gtk.NewButtonWithLabel("Get past messages...")) - tabs[jid] = newTab + tabs.Store(jid, newTab) } } func switchToTab(jid string) { current = jid - scroller.SetChild(tabs[current].msgs) - m, _ := mucmembers.Load(jid) - ma := m.(mucUnit) - mm := ma.Members - gen := gtk.NewBox(gtk.OrientationVertical, 0) + tab, ok := tabs.Load(current) + if !ok { + return + } - mm.Range(func(k, v any) bool { - userbox := gtk.NewBox(gtk.OrientationHorizontal, 0) + typed_tab := tab.(*chatTab) - u := v.(stanza.Presence) - var mu MucUser - var ocu OccupantID - u.Get(&mu) - u.Get(&ocu) + scroller.SetChild(typed_tab.msgs) + if typed_tab.isMuc { + m, _ := mucmembers.Load(jid) + ma := m.(mucUnit) + mm := ma.Members + gen := gtk.NewBox(gtk.OrientationVertical, 0) - nick_label := gtk.NewLabel(Jid.MustParse(u.From).Resourcepart()) + mm.Range(func(k, v any) bool { + userbox := gtk.NewBox(gtk.OrientationHorizontal, 0) - userbox.Append(nick_label) + u := v.(stanza.Presence) + var mu MucUser + var ocu OccupantID + u.Get(&mu) + u.Get(&ocu) - gen.Append(userbox) - return true - }) + nick_label := gtk.NewLabel(Jid.MustParse(u.From).Resourcepart()) - memberList.SetChild(gen) + userbox.Append(nick_label) + + gen.Append(userbox) + return true + }) + + memberList.SetChild(gen) + } else { + memberList.SetChild(gtk.NewLabel(jid)) + } } diff --git a/gtk-message.go b/gtk-message.go index 7aa3e3f..213e29a 100644 --- a/gtk-message.go +++ b/gtk-message.go @@ -80,29 +80,33 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { // authorBox.Append(im) - 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) + 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) + 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 := newImageFromPath("debug.png") + im.SetPixelSize(40) + im.AddCSSClass("author_img") + authorBox.Append(im) + } } else { im := newImageFromPath("debug.png") im.SetPixelSize(40) im.AddCSSClass("author_img") + authorBox.Append(im) } - } else { - im := newImageFromPath("debug.png") - im.SetPixelSize(40) - im.AddCSSClass("author_img") } al := gtk.NewLabel(jid.MustParse(m.From).Resourcepart()) diff --git a/main.go b/main.go index 13b2f67..0119777 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,6 @@ import ( "encoding/xml" "runtime" - "github.com/kr/pretty" ) var loadedConfig lambdaConfig @@ -31,11 +30,13 @@ var empty_dialog *gtk.Label // var msgs *gtk.ListBox var content *gtk.Widgetter -var tabs map[string]*chatTab = make(map[string]*chatTab) +// var tabs map[string]*chatTab = make(map[string]*chatTab) +var tabs sync.Map var current string var scroller *gtk.ScrolledWindow var memberList *gtk.ScrolledWindow +var menu *gtk.Box //go:embed style.css var styleCSS string @@ -47,6 +48,9 @@ var uiQueue = make(chan func(), 100) // stores members of mucs var mucmembers sync.Map +// stores devices of users +var userdevices sync.Map + func init() { go func() { for fn := range uiQueue { @@ -154,10 +158,11 @@ func main() { b = ba } - _, ok = tabs[originator] + tab, ok := tabs.Load(originator) + typed_tab := tab.(*chatTab) if ok { - tabs[originator].msgs.Append(b) + typed_tab.msgs.Append(b) scrollToBottomAfterUpdate(scroller) } else { fmt.Println("Got message when the tab does not exist!") @@ -172,7 +177,10 @@ func main() { return } - pretty.Println(presence) + if presence.Error != *new(stanza.Err) { + return + } + var mu MucUser var ocu OccupantID @@ -193,22 +201,48 @@ func main() { } typed_unit := unit.(mucUnit) - /* - if typed_unit.Members == nil { - typed_unit.Members = make(map[string]stanza.Presence) - mucmembers.Store(muc, typed_unit) - } - */ if presence.Type != "unavailable" { typed_unit.Members.Store(ocu.ID, presence) } else { typed_unit.Members.Delete(ocu.ID) - // delete(typed_unit.Members, ocu.ID) } mucmembers.Store(muc, typed_unit) + } else { // This is a presence stanza from a regular user + // The code is basically the exact same as above, we just don't check for mucuser + user := jid.MustParse(presence.From).Bare().String() + _, ok := userdevices.Load(user) + if !ok { + userdevices.Store(user, userUnit{}) + createTab(user, false) + + b := gtk.NewButtonWithLabel(user) + b.ConnectClicked(func() { + b.AddCSSClass("accent") + switchToTab(user) + }) + 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 + } + + resource := jid.MustParse(presence.From).Resourcepart() + + typed_unit := unit.(userUnit) + + if presence.Type != "unavailable" { + typed_unit.Devices.Store(resource, presence) + } else { + typed_unit.Devices.Delete(resource) + } + + userdevices.Store(user, typed_unit) } }) @@ -257,7 +291,7 @@ func activate(app *gtk.Application) { window.SetTitle("Lambda") window.Window.AddCSSClass("ssd") - menu := gtk.NewBox(gtk.OrientationHorizontal, 0) + menu = gtk.NewBox(gtk.OrientationHorizontal, 0) /* f_menu := gtk.NewMenuButton() f_menu.SetLabel("File") @@ -323,7 +357,18 @@ func activate(app *gtk.Application) { dialog.Choose(context.TODO(), &window.Window, nil) } - err := sendMessage(client, current, stanza.MessageTypeGroupchat, t, "", "") + message_type := stanza.MessageTypeChat + tab, ok := tabs.Load(current) + if !ok { + return + } + + typed_tab := tab.(*chatTab) + if typed_tab.isMuc { + message_type = stanza.MessageTypeGroupchat + } + + err := sendMessage(client, current, message_type, t, "", "") if err != nil { panic(err) // TODO: Show error message via GTK } @@ -348,7 +393,7 @@ func activate(app *gtk.Application) { debug_btn.ConnectClicked(func() { t := en.Text() - _, ok := tabs[t] + _, ok := tabs.Load(t) if !ok { err := joinMuc(client, clientroot.Session.BindJid, t, m_entry.Text()) if err != nil { diff --git a/types.go b/types.go index c8ea861..78b2146 100644 --- a/types.go +++ b/types.go @@ -23,3 +23,9 @@ type mucUnit struct { // value: last user presence Members sync.Map } + +type userUnit struct { + // key: Resource + // value: last presence of this device + Devices sync.Map +}