github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }