github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/badges/badger.go (about) 1 // Copyright 2016 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package badges 5 6 import ( 7 "golang.org/x/net/context" 8 9 "github.com/keybase/client/go/gregor" 10 "github.com/keybase/client/go/libkb" 11 "github.com/keybase/client/go/protocol/chat1" 12 "github.com/keybase/client/go/protocol/gregor1" 13 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/keybase/client/go/protocol/stellar1" 15 ) 16 17 type InboxVersionSource interface { 18 GetInboxVersion(context.Context, gregor1.UID) (chat1.InboxVers, error) 19 } 20 21 type nullInboxVersionSource struct { 22 } 23 24 func (n nullInboxVersionSource) GetInboxVersion(ctx context.Context, uid gregor1.UID) (chat1.InboxVers, error) { 25 return chat1.InboxVers(0), nil 26 } 27 28 // Badger keeps a BadgeState up to date and broadcasts it to electron. 29 // This is the client-specific glue. 30 // The state is kept up to date by subscribing to: 31 // - All gregor state updates 32 // - All chat.activity gregor OOBMs 33 // - Logout 34 type Badger struct { 35 libkb.Contextified 36 badgeState *BadgeState 37 iboxVersSource InboxVersionSource 38 notifyCh chan keybase1.BadgeState 39 shutdownCh chan struct{} 40 } 41 42 func NewBadger(g *libkb.GlobalContext) *Badger { 43 b := &Badger{ 44 Contextified: libkb.NewContextified(g), 45 badgeState: NewBadgeState(g.Log, g.Env), 46 iboxVersSource: nullInboxVersionSource{}, 47 notifyCh: make(chan keybase1.BadgeState, 1000), 48 shutdownCh: make(chan struct{}), 49 } 50 go b.notifyLoop() 51 g.PushShutdownHook(func(mctx libkb.MetaContext) error { 52 close(b.shutdownCh) 53 return nil 54 }) 55 return b 56 } 57 58 func (b *Badger) notifyLoop() { 59 for { 60 select { 61 case state := <-b.notifyCh: 62 b.G().NotifyRouter.HandleBadgeState(state) 63 case <-b.shutdownCh: 64 return 65 } 66 } 67 } 68 69 func (b *Badger) SetLocalChatState(s LocalChatState) { 70 b.badgeState.SetLocalChatState(s) 71 } 72 73 func (b *Badger) SetInboxVersionSource(s InboxVersionSource) { 74 b.iboxVersSource = s 75 } 76 77 func (b *Badger) PushState(ctx context.Context, state gregor.State) { 78 b.G().Log.CDebugf(ctx, "Badger update with gregor state") 79 if err := b.badgeState.UpdateWithGregor(ctx, state); err != nil { 80 b.G().Log.Warning("Badger (PushState) UpdateWithGregor failed: %v", err) 81 } 82 if err := b.Send(ctx); err != nil { 83 b.G().Log.Warning("Badger send (PushState) failed: %v", err) 84 } 85 } 86 87 func (b *Badger) PushChatUpdate(ctx context.Context, update chat1.UnreadUpdate, inboxVers chat1.InboxVers) { 88 b.G().Log.CDebugf(ctx, "Badger update with chat update") 89 b.badgeState.UpdateWithChat(ctx, update, inboxVers, b.G().IsMobileAppType()) 90 if err := b.Send(ctx); err != nil { 91 b.G().Log.CDebugf(ctx, "Badger send (PushChatUpdate) failed: %v", err) 92 } 93 } 94 95 func (b *Badger) PushChatFullUpdate(ctx context.Context, update chat1.UnreadUpdateFull) { 96 b.G().Log.CDebugf(ctx, "Badger update with chat full update") 97 b.badgeState.UpdateWithChatFull(ctx, update, b.G().IsMobileAppType()) 98 if err := b.Send(ctx); err != nil { 99 b.G().Log.CDebugf(ctx, "Badger send (PushChatFullUpdate) failed: %v", err) 100 } 101 } 102 103 func (b *Badger) GetInboxVersionForTest(ctx context.Context) (chat1.InboxVers, error) { 104 uid := b.G().Env.GetUID() 105 return b.iboxVersSource.GetInboxVersion(ctx, uid.ToBytes()) 106 } 107 108 func (b *Badger) SetWalletAccountUnreadCount(ctx context.Context, accountID stellar1.AccountID, unreadCount int) { 109 changed := b.badgeState.SetWalletAccountUnreadCount(accountID, unreadCount) 110 if !changed { 111 return 112 } 113 b.G().Log.CDebugf(ctx, "Badger sending for SetWalletAccountUnreadCount: %s/%d", accountID, unreadCount) 114 if err := b.Send(ctx); err != nil { 115 b.G().Log.CDebugf(ctx, "Badger send (SetWalletAccountUnreadCount) failed: %s", err) 116 } 117 } 118 119 func (b *Badger) Clear(ctx context.Context) { 120 b.badgeState.Clear() 121 if err := b.Send(ctx); err != nil { 122 b.G().Log.CDebugf(ctx, "Badger send (clear) failed: %v", err) 123 } 124 } 125 126 // Send the badgestate to electron 127 func (b *Badger) Send(ctx context.Context) error { 128 state, err := b.badgeState.Export(ctx) 129 if err != nil { 130 return err 131 } 132 b.log(ctx, state) 133 b.notifyCh <- state 134 return nil 135 } 136 137 func (b *Badger) State() *BadgeState { 138 return b.badgeState 139 } 140 141 // Log a copy of the badgestate with some zeros stripped off for brevity. 142 func (b *Badger) log(ctx context.Context, state1 keybase1.BadgeState) { 143 state2 := state1 144 state2.Conversations = nil 145 for index, c1 := range state1.Conversations { 146 if c1.IsEmpty() { 147 continue 148 } 149 c2id := c1.ConvID 150 if len(c1.ConvID) >= chat1.DbShortFormLen { 151 // This is the db short form for logging brevity only. 152 // Don't let this leave this method. 153 c2id = chat1.ConversationID([]byte(c1.ConvID)).DbShortForm() 154 } 155 156 c2 := keybase1.BadgeConversationInfo{ 157 ConvID: c2id, 158 UnreadMessages: c1.UnreadMessages, 159 BadgeCount: c1.BadgeCount, 160 } 161 state2.Conversations = append(state2.Conversations, c2) 162 if index >= 50 { 163 b.G().Log.CDebugf(ctx, "Badger send: cutting off debug, too many convs") 164 break 165 } 166 } 167 b.G().Log.CDebugf(ctx, "Badger send: %+v", state2) 168 }