github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/maps/track.go (about) 1 package maps 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/keybase/client/go/chat/types" 10 "github.com/keybase/client/go/protocol/chat1" 11 "github.com/keybase/client/go/protocol/gregor1" 12 ) 13 14 type locationTrack struct { 15 sync.Mutex 16 stopCh chan struct{} 17 updateCh chan chat1.Coordinate 18 19 convID chat1.ConversationID 20 msgID chat1.MessageID 21 endTime time.Time 22 allCoords []chat1.Coordinate 23 getCurrentPosition bool 24 maxCoords int 25 stopped bool 26 perm chat1.UIWatchPositionPerm 27 } 28 29 func (t *locationTrack) GetCoords() (res []chat1.Coordinate) { 30 t.Lock() 31 defer t.Unlock() 32 res = make([]chat1.Coordinate, len(t.allCoords)) 33 copy(res, t.allCoords) 34 return res 35 } 36 37 func (t *locationTrack) capLocked(maxCoords int) { 38 if len(t.allCoords) < maxCoords || len(t.allCoords) == 0 { 39 return 40 } 41 newCoords := make([]chat1.Coordinate, maxCoords) 42 copy(newCoords, t.allCoords[len(t.allCoords)-maxCoords:len(t.allCoords)]) 43 newCoords = append([]chat1.Coordinate{t.allCoords[0]}, newCoords...) 44 t.allCoords = newCoords 45 } 46 47 func (t *locationTrack) Drain(coord chat1.Coordinate) (res int) { 48 t.Lock() 49 defer t.Unlock() 50 defer t.capLocked(t.maxCoords) 51 if !coord.IsZero() { 52 t.allCoords = append(t.allCoords, coord) 53 res++ 54 } 55 for { 56 select { 57 case coord := <-t.updateCh: 58 t.allCoords = append(t.allCoords, coord) 59 res++ 60 default: 61 return res 62 } 63 } 64 } 65 66 func (t *locationTrack) SetCoords(coords []chat1.Coordinate) { 67 t.Lock() 68 defer t.Unlock() 69 t.allCoords = coords 70 t.capLocked(t.maxCoords) 71 } 72 73 func (t *locationTrack) Stop() { 74 t.Lock() 75 defer t.Unlock() 76 if t.stopped { 77 return 78 } 79 t.stopped = true 80 close(t.stopCh) 81 } 82 83 func (t *locationTrack) IsStopped() bool { 84 t.Lock() 85 defer t.Unlock() 86 return t.stopped 87 } 88 89 func (t *locationTrack) Key() types.LiveLocationKey { 90 key := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%d", t.convID, t.msgID))) 91 return types.LiveLocationKey(key) 92 } 93 94 func (t *locationTrack) ToDisk() diskLocationTrack { 95 return diskLocationTrack{ 96 ConvID: t.convID, 97 MsgID: t.msgID, 98 EndTime: gregor1.ToTime(t.endTime), 99 Coords: t.GetCoords(), 100 GetCurrentPosition: t.getCurrentPosition, 101 MaxCoords: t.maxCoords, 102 Stopped: t.stopped, 103 } 104 } 105 106 func newLocationTrack(convID chat1.ConversationID, msgID chat1.MessageID, 107 endTime time.Time, getCurrentPosition bool, maxCoords int, stopped bool) *locationTrack { 108 perm := chat1.UIWatchPositionPerm_BASE 109 if !getCurrentPosition { 110 perm = chat1.UIWatchPositionPerm_ALWAYS 111 } 112 return &locationTrack{ 113 stopCh: make(chan struct{}), 114 updateCh: make(chan chat1.Coordinate, 50), 115 convID: convID, 116 msgID: msgID, 117 endTime: endTime, 118 getCurrentPosition: getCurrentPosition, 119 maxCoords: maxCoords, 120 stopped: stopped, 121 perm: perm, 122 } 123 } 124 125 func newLocationTrackFromDisk(d diskLocationTrack) *locationTrack { 126 t := newLocationTrack(d.ConvID, d.MsgID, gregor1.FromTime(d.EndTime), d.GetCurrentPosition, d.MaxCoords, 127 d.Stopped) 128 t.allCoords = d.Coords 129 return t 130 }