github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/storage/readoutbox.go (about) 1 package storage 2 3 import ( 4 "fmt" 5 6 "github.com/keybase/client/go/chat/globals" 7 "github.com/keybase/client/go/chat/utils" 8 "github.com/keybase/client/go/libkb" 9 "github.com/keybase/client/go/protocol/chat1" 10 "github.com/keybase/client/go/protocol/gregor1" 11 "golang.org/x/net/context" 12 ) 13 14 const readOutboxVersion = 1 15 16 type ReadOutboxRecord struct { 17 ID chat1.OutboxID 18 ConvID chat1.ConversationID 19 MsgID chat1.MessageID 20 ForceUnread bool 21 } 22 23 type diskReadOutbox struct { 24 Version int `codec:"V"` 25 Records []ReadOutboxRecord `codec:"O"` 26 } 27 28 type ReadOutbox struct { 29 globals.Contextified 30 utils.DebugLabeler 31 *baseBox 32 33 uid gregor1.UID 34 } 35 36 func NewReadOutbox(g *globals.Context, uid gregor1.UID) *ReadOutbox { 37 return &ReadOutbox{ 38 Contextified: globals.NewContextified(g), 39 DebugLabeler: utils.NewDebugLabeler(g.ExternalG(), "ReadOutbox", false), 40 baseBox: newBaseBox(g), 41 uid: uid, 42 } 43 } 44 45 func (o *ReadOutbox) dbKey() libkb.DbKey { 46 return libkb.DbKey{ 47 Typ: libkb.DBChatOutbox, 48 Key: fmt.Sprintf("rob:%s", o.uid), 49 } 50 } 51 52 func (o *ReadOutbox) clear(ctx context.Context) Error { 53 err := o.G().LocalChatDb.Delete(o.dbKey()) 54 if err != nil { 55 return NewInternalError(ctx, o.DebugLabeler, "error clearing read outbox: uid: %s err: %s", 56 o.uid, err) 57 } 58 return nil 59 } 60 61 func (o *ReadOutbox) readStorage(ctx context.Context) (res diskReadOutbox) { 62 if memobox := readOutboxMemCache.Get(o.uid); memobox != nil { 63 o.Debug(ctx, "hit in memory cache") 64 res = *memobox 65 } else { 66 found, ierr := o.readDiskBox(ctx, o.dbKey(), &res) 67 if ierr != nil { 68 if _, ok := ierr.(libkb.LoginRequiredError); !ok { 69 o.maybeNuke(NewInternalError(ctx, o.DebugLabeler, ierr.Error()), o.dbKey()) 70 } 71 return diskReadOutbox{Version: readOutboxVersion} 72 } 73 if !found { 74 return diskReadOutbox{Version: readOutboxVersion} 75 } 76 readOutboxMemCache.Put(o.uid, &res) 77 } 78 if res.Version != readOutboxVersion { 79 o.Debug(ctx, "on disk version not equal to program version, clearing: disk :%d program: %d", 80 res.Version, readOutboxVersion) 81 if cerr := o.clear(ctx); cerr != nil { 82 return diskReadOutbox{Version: readOutboxVersion} 83 } 84 return diskReadOutbox{Version: readOutboxVersion} 85 } 86 return res 87 } 88 89 func (o *ReadOutbox) writeStorage(ctx context.Context, obox diskReadOutbox) (err Error) { 90 if ierr := o.writeDiskBox(ctx, o.dbKey(), obox); ierr != nil { 91 return NewInternalError(ctx, o.DebugLabeler, "error writing outbox: err: %s", ierr) 92 } 93 readOutboxMemCache.Put(o.uid, &obox) 94 return nil 95 } 96 97 func (o *ReadOutbox) PushRead(ctx context.Context, convID chat1.ConversationID, msgID chat1.MessageID, forceUnread bool) (err Error) { 98 locks.ReadOutbox.Lock() 99 defer locks.ReadOutbox.Unlock() 100 obox := o.readStorage(ctx) 101 id, ierr := NewOutboxID() 102 if ierr != nil { 103 return NewInternalError(ctx, o.DebugLabeler, "failed to generate id: %s", ierr) 104 } 105 obox.Records = append(obox.Records, ReadOutboxRecord{ 106 ID: id, 107 ConvID: convID, 108 MsgID: msgID, 109 ForceUnread: forceUnread, 110 }) 111 return o.writeStorage(ctx, obox) 112 } 113 114 func (o *ReadOutbox) GetRecords(ctx context.Context) (res []ReadOutboxRecord, err Error) { 115 locks.ReadOutbox.Lock() 116 defer locks.ReadOutbox.Unlock() 117 obox := o.readStorage(ctx) 118 return obox.Records, nil 119 } 120 121 func (o *ReadOutbox) RemoveRecord(ctx context.Context, id chat1.OutboxID) Error { 122 locks.ReadOutbox.Lock() 123 defer locks.ReadOutbox.Unlock() 124 obox := o.readStorage(ctx) 125 var newrecs []ReadOutboxRecord 126 for _, rec := range obox.Records { 127 if !rec.ID.Eq(&id) { 128 newrecs = append(newrecs, rec) 129 } 130 } 131 obox.Records = newrecs 132 return o.writeStorage(ctx, obox) 133 }