package model import ( "sort" "sync" "time" ) // Timeline encapsulates a collection of ordered messages, and a mechanism to access them // in a threadsafe manner. type Timeline struct { Messages []Message SignedGroupID []byte lock sync.Mutex } // Message is a local representation of a given message sent over a group chat channel. type Message struct { Timestamp time.Time Received time.Time PeerID string Message string Signature []byte Verified bool PreviousMessageSig []byte } func compareSignatures(a []byte, b []byte) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true } // GetMessages returns a copy of the entire timeline func (t *Timeline) GetMessages() []Message { t.lock.Lock() messages := make([]Message, len(t.Messages)) copy(messages[:], t.Messages[:]) t.lock.Unlock() return messages } // Len gets the length of the timeline func (t *Timeline) Len() int { return len(t.Messages) } // Swap swaps 2 messages on the timeline. func (t *Timeline) Swap(i, j int) { t.Messages[i], t.Messages[j] = t.Messages[j], t.Messages[i] } // Less checks 2 messages (i and j) in the timeline and returns true if i occcured before j, else false func (t *Timeline) Less(i, j int) bool { if t.Messages[i].Timestamp.Before(t.Messages[j].Timestamp) { return true } if compareSignatures(t.Messages[i].PreviousMessageSig, t.SignedGroupID) { return true } if compareSignatures(t.Messages[i].Signature, t.Messages[j].PreviousMessageSig) { return true } return false } // Insert inserts a message into the timeline in a thread safe way. func (t *Timeline) Insert(mi *Message) { t.lock.Lock() defer t.lock.Unlock() for _, m := range t.Messages { // If the message already exists, then we don't add it if compareSignatures(m.Signature, mi.Signature) { return } } t.Messages = append(t.Messages, *mi) sort.Sort(t) }