github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/pager/pager.go (about) 1 package pager 2 3 import ( 4 "github.com/keybase/client/go/protocol/chat1" 5 "github.com/keybase/client/go/protocol/gregor1" 6 "github.com/keybase/go-codec/codec" 7 ) 8 9 // inboxPagerFields is the info that gives a total ordering on the inbox 10 type InboxPagerFields struct { 11 Mtime gregor1.Time `codec:"M"` 12 ConvID chat1.ConversationID `codec:"C"` 13 } 14 15 func (i InboxPagerFields) GetMtime() gregor1.Time { 16 return i.Mtime 17 } 18 19 func (i InboxPagerFields) GetConvID() chat1.ConversationID { 20 return i.ConvID 21 } 22 23 // pager provides the getPage and makePage functions for implementing 24 // paging in the chat1 protocol 25 type Pager struct { 26 codec codec.Handle 27 } 28 29 func NewPager() Pager { 30 mh := codec.MsgpackHandle{WriteExt: true} 31 return Pager{ 32 codec: &mh, 33 } 34 } 35 36 func (p Pager) encode(input interface{}) ([]byte, error) { 37 var data []byte 38 enc := codec.NewEncoderBytes(&data, p.codec) 39 if err := enc.Encode(input); err != nil { 40 return nil, err 41 } 42 return data, nil 43 } 44 45 func (p Pager) decode(data []byte, res interface{}) error { 46 dec := codec.NewDecoderBytes(data, p.codec) 47 err := dec.Decode(res) 48 return err 49 } 50 51 func (p Pager) GetPage(getcond func(bool) string, page *chat1.Pagination, 52 pivot interface{}) (string, bool, error) { 53 54 var dat []byte 55 var cond string 56 var prev bool 57 58 // Set the query condition depending on what direction we are looking 59 if len(page.Next) > 0 { 60 cond = getcond(false) 61 dat = page.Next 62 } else if len(page.Previous) > 0 { 63 cond = getcond(true) 64 dat = page.Previous 65 prev = true 66 } else { 67 return "", false, nil 68 } 69 70 if err := p.decode(dat, pivot); err != nil { 71 return "", false, err 72 } 73 74 return cond, prev, nil 75 } 76 77 func (p Pager) MakePage(length, reqed int, next interface{}, prev interface{}) (*chat1.Pagination, error) { 78 prevEncoded, err := p.encode(prev) 79 if err != nil { 80 return nil, err 81 } 82 nextEncoded, err := p.encode(next) 83 if err != nil { 84 return nil, err 85 } 86 87 return &chat1.Pagination{ 88 Num: length, 89 Next: nextEncoded, 90 Previous: prevEncoded, 91 Last: (length < reqed) || length == 0, 92 }, nil 93 } 94 95 // inboxPager provides a convenient interface to pager for the inbox 96 // use case 97 type InboxPager struct { 98 Pager 99 } 100 101 type InboxEntry interface { 102 GetMtime() gregor1.Time 103 GetConvID() chat1.ConversationID 104 } 105 106 func NewInboxPager() InboxPager { 107 return InboxPager{Pager: NewPager()} 108 } 109 110 func (p InboxPager) MakePage(res []InboxEntry, reqed int) (*chat1.Pagination, error) { 111 if len(res) == 0 { 112 return &chat1.Pagination{Num: 0, Last: true}, nil 113 } 114 115 // Get first and last message IDs to encode in the result 116 prevPF := InboxPagerFields{Mtime: res[0].GetMtime(), ConvID: res[0].GetConvID()} 117 nextPF := InboxPagerFields{ 118 Mtime: res[len(res)-1].GetMtime(), 119 ConvID: res[len(res)-1].GetConvID(), 120 } 121 122 return p.Pager.MakePage(len(res), reqed, nextPF, prevPF) 123 } 124 125 // threadPager provides a convenient interface to pager for the thread use case 126 type ThreadPager struct { 127 Pager 128 } 129 130 type Message interface { 131 GetMessageID() chat1.MessageID 132 } 133 134 func NewThreadPager() ThreadPager { 135 return ThreadPager{Pager: NewPager()} 136 } 137 138 func (p ThreadPager) MakePage(res []Message, reqed int, maxDeletedUpto chat1.MessageID) (*chat1.Pagination, error) { 139 if len(res) == 0 { 140 return &chat1.Pagination{Num: 0, Last: true}, nil 141 } 142 143 // Get first and last message IDs to encode in the result 144 prevMsgID := res[0].GetMessageID() 145 nextMsgID := res[len(res)-1].GetMessageID() 146 147 page, err := p.Pager.MakePage(len(res), reqed, nextMsgID, prevMsgID) 148 if err != nil { 149 return page, err 150 } 151 if prevMsgID.Min(nextMsgID) <= maxDeletedUpto { 152 // If any message is prior to the nukepoint, say this is the last page. 153 page.Last = true 154 } 155 return page, nil 156 } 157 158 func (p ThreadPager) MakeIndex(msg Message) ([]byte, error) { 159 return p.encode(msg.GetMessageID()) 160 } 161 162 func (p ThreadPager) MakeIndexByID(msgID chat1.MessageID) ([]byte, error) { 163 return p.encode(msgID) 164 }