Path: blob/main/internal/sidebar/directbutton/view.go
366 views
package directbutton12import (3"context"4"log/slog"5"sort"67"github.com/diamondburned/arikawa/v3/discord"8"github.com/diamondburned/arikawa/v3/gateway"9"github.com/diamondburned/gotk4/pkg/gtk/v4"10"github.com/diamondburned/gotkit/gtkutil"11"github.com/diamondburned/gotkit/gtkutil/cssutil"12"github.com/diamondburned/ningen/v3"13"github.com/diamondburned/ningen/v3/states/read"14"libdb.so/dissent/internal/gtkcord"15"libdb.so/dissent/internal/sidebar/sidebutton"16)1718type View struct {19*gtk.Box20DM *Button2122mentioned struct {23IDs []discord.ChannelID24Buttons map[discord.ChannelID]*ChannelButton25}2627ctx context.Context28}2930var viewCSS = cssutil.Applier("dmbutton-view", `31`)3233func NewView(ctx context.Context) *View {34v := View{35Box: gtk.NewBox(gtk.OrientationVertical, 0),36DM: NewButton(ctx),37ctx: ctx,38}3940v.mentioned.IDs = make([]discord.ChannelID, 0, 4)41v.mentioned.Buttons = make(map[discord.ChannelID]*ChannelButton, 4)4243v.Append(v.DM)44viewCSS(v)4546vis := gtkutil.WithVisibility(ctx, v)4748state := gtkcord.FromContext(ctx)49state.BindHandler(vis, func(ev gateway.Event) {50switch ev := ev.(type) {51case *read.UpdateEvent:52if !ev.GuildID.IsValid() {53v.Invalidate()54}55case *gateway.MessageCreateEvent:56if !ev.GuildID.IsValid() {57v.Invalidate()58}59}60},61(*read.UpdateEvent)(nil),62(*gateway.MessageCreateEvent)(nil),63)6465return &v66}6768type channelUnreadStatus struct {69*discord.Channel70UnreadCount int71}7273func (v *View) Invalidate() {74state := gtkcord.FromContext(v.ctx)7576// This is slow, but whatever.77dms, err := state.PrivateChannels()78if err != nil {79slog.Error(80"failed to get private channels for DM button view",81"err", err)8283// Clear all DMs.84v.update(nil)85return86}8788var unreads map[discord.ChannelID]channelUnreadStatus89for i, dm := range dms {90count := state.ChannelCountUnreads(dm.ID, ningen.UnreadOpts{})91if count == 0 {92continue93}9495if unreads == nil {96unreads = make(map[discord.ChannelID]channelUnreadStatus, 4)97}9899unreads[dm.ID] = channelUnreadStatus{100Channel: &dms[i],101UnreadCount: count,102}103}104105v.update(unreads)106}107108func (v *View) update(unreads map[discord.ChannelID]channelUnreadStatus) {109for _, unread := range unreads {110button, ok := v.mentioned.Buttons[unread.Channel.ID]111if !ok {112button = NewChannelButton(v.ctx, unread.Channel.ID)113v.mentioned.Buttons[unread.Channel.ID] = button114}115116button.Update(unread.Channel)117button.InvalidateUnread()118}119120// Purge all buttons off the widget.121gtkutil.RemoveChildren(v)122123// Delete unused buttons.124for id := range v.mentioned.Buttons {125if _, ok := unreads[id]; !ok {126delete(v.mentioned.Buttons, id)127}128}129130// Recreate the IDs slice.131v.mentioned.IDs = v.mentioned.IDs[:0]132for id := range unreads {133v.mentioned.IDs = append(v.mentioned.IDs, id)134}135136// Sort the IDs slice. We'll sort it according to the time that the last137// message was sent: the most recent message will be at the top.138sort.Slice(v.mentioned.IDs, func(i, j int) bool {139mi := unreads[v.mentioned.IDs[i]]140mj := unreads[v.mentioned.IDs[j]]141return mi.LastMessageID > mj.LastMessageID142})143144// Append the buttons back to the widget.145v.Append(v.DM)146for _, id := range v.mentioned.IDs {147v.Append(v.mentioned.Buttons[id])148}149}150151func (v *View) Unselect() {152v.SetSelected(false)153}154155func (v *View) SetSelected(selected bool) {156if selected {157v.DM.Pill.State = sidebutton.PillActive158} else {159v.DM.Pill.State = sidebutton.PillDisabled160}161v.DM.Pill.Invalidate()162}163164165