internal void RemoveThread(YamsterThread thread, YamsterModelEventCollector eventCollector) { int index = this.threadsInternal.BinarySearch(thread, Comparer <YamsterThread> .Create((x, y) => Math.Sign(x.ThreadId - y.ThreadId))); int deletedIndex = this.deletedThreadsInternal.BinarySearch(thread, Comparer <YamsterThread> .Create((x, y) => Math.Sign(x.ThreadId - y.ThreadId))); if (index < 0 && deletedIndex < 0) { Debug.Assert(false, "RemoveThread() called on thread that doesn't belong to this group"); return; } if (index >= 0) { this.threadsInternal.RemoveAt(index); if (thread.Read) { this.NotifyThreadReadChanged(newReadValue: false, eventCollector: eventCollector); } } else { this.deletedThreadsInternal.RemoveAt(deletedIndex); } }
internal void AddThread(YamsterThread thread, YamsterModelEventCollector eventCollector) { int index = this.threadsInternal.BinarySearch(thread, Comparer <YamsterThread> .Create((x, y) => Math.Sign(x.ThreadId - y.ThreadId))); int deletedIndex = this.deletedThreadsInternal.BinarySearch(thread, Comparer <YamsterThread> .Create((x, y) => Math.Sign(x.ThreadId - y.ThreadId))); if (index >= 0 || deletedIndex >= 0) { throw new InvalidOperationException("Program Bug: The message was already added to this thread"); } if (!thread.AllMessagesDeleted) { this.threadsInternal.Insert(~index, thread); if (thread.Read) { this.NotifyThreadReadChanged(newReadValue: true, eventCollector: eventCollector); } } else { this.deletedThreadsInternal.Insert(~deletedIndex, thread); } }
internal void ProcessDbMessageState(DbMessageState record) { var eventCollector = new YamsterModelEventCollector(); ProcessDbMessageState(record, eventCollector); eventCollector.FireEvents(); }
internal void FixupUnresolvedObjectsForThread(YamsterThread thread, YamsterMessage latestMessage, YamsterModelEventCollector eventCollector) { Debug.Assert(latestMessage.Thread == thread); DbThreadState dbThreadState; if (unresolvedThreadStatesById.TryGetValue(thread.ThreadId, out dbThreadState)) { unresolvedThreadStatesById.Remove(thread.ThreadId); thread.SetDbThreadState(dbThreadState, eventCollector); } // Is this thread part of a conversation? long conversationId = latestMessage.DbMessage.ConversationId; if (conversationId != 0) { // Register the thread as the owner of this conversation this.threadsByConversationId[conversationId] = thread; // Is there an unresolved conversation object? DbConversation unresolvedConversation; if (this.unresolvedConversationsById.TryGetValue(conversationId, out unresolvedConversation)) { // Yes, so apply it now this.unresolvedConversationsById.Remove(conversationId); thread.UpdateConversation(unresolvedConversation, eventCollector); } } }
/// <summary> /// If the YamsterMessage with messageId already exists, this calls messageAction /// immediately. Otherwise, messageAction is queued and will be called later when /// the message appears. If the same listener calls ListenForMessageById() more /// than once, only the most recent request is kept. /// </summary> internal void ListenForMessageById(long messageId, object listener, YamsterModelEventCollector eventCollector, ListenedMessageAction messageAction) { YamsterMessage message = this.GetMessageById(messageId, nullIfMissing: true); if (message != null) { messageAction(message, eventCollector); return; } ListenedMessage listenedMessage; if (!listenedMessagesById.TryGetValue(messageId, out listenedMessage)) { listenedMessage = new ListenedMessage() { MessageId = messageId }; listenedMessagesById.Add(messageId, listenedMessage); } listenedMessage.ActionsByListener[listener] = messageAction; }
// TODO: This is a hack. Need a better story for referencing objects that haven't been // synced yet. public YamsterUser GetPossiblyUnknownUserById(long userId) { var eventCollector = new YamsterModelEventCollector(); var user = FetchUserById(userId, eventCollector); eventCollector.FireEvents(); return(user); }
void ProcessDbGroupState(DbGroupState record) { var eventCollector = new YamsterModelEventCollector(); YamsterGroup group = this.FetchGroupById(record.GroupId, eventCollector); group.SetDbGroupState(record, eventCollector); eventCollector.FireEvents(); }
void ProcessDbUser(DbUser record) { var eventCollector = new YamsterModelEventCollector(); YamsterUser user = this.FetchUserById(record.UserId, eventCollector); user.DbUser = record; eventCollector.NotifyAfterUpdate(user); eventCollector.FireEvents(); }
void ProcessDbMessage(DbMessage record) { // Does the message exist yet? var message = this.GetMessageById(record.MessageId, nullIfMissing: true); bool messageIsNew = message == null; var eventCollector = new YamsterModelEventCollector(); if (messageIsNew) { message = new YamsterMessage(record.MessageId, this); this.messagesById.Add(record.MessageId, message); eventCollector.NotifyAfterAdd(message); message.SetDbMessage(record, eventCollector); // For now we assume that messages cannot move between threads var threadId = message.ThreadId; YamsterThread thread = GetThreadById(threadId, nullIfMissing: true); if (thread == null) { thread = new YamsterThread(threadId, message.Group, this); threadsById.Add(threadId, thread); eventCollector.NotifyAfterAdd(thread); message.Group.AddThread(thread, eventCollector); thread.AddMessage(message, eventCollector); FixupUnresolvedObjectsForThread(thread, message, eventCollector); } else { thread.AddMessage(message, eventCollector); } } else { message.SetDbMessage(record, eventCollector); } // Was there an unresolved message that we can process now? DbMessageState unresolvedMessageState; if (this.unresolvedMessageStatesById.TryGetValue(record.MessageId, out unresolvedMessageState)) { this.unresolvedMessageStatesById.Remove(record.MessageId); ProcessDbMessageState(unresolvedMessageState, eventCollector); } if (messageIsNew) { CheckForListenedMessage(message, eventCollector); } eventCollector.FireEvents(); }
internal void NotifyTotalLikesChanged(int totalLikesCountDelta, YamsterModelEventCollector eventCollector) { if (totalLikesCountDelta == 0) { return; } this.totalLikesCount += totalLikesCountDelta; Debug.Assert(totalLikesCount == this.messagesInternal.Sum(x => x.LikesCount)); eventCollector.NotifyAfterUpdate(this); }
internal YamsterGroup FetchGroupById(long groupId, YamsterModelEventCollector eventCollector) { YamsterGroup group = this.GetGroupById(groupId, nullIfMissing: true); if (group == null) { group = new YamsterGroup(groupId, this); this.groupsById.Add(groupId, group); eventCollector.NotifyAfterAdd(group); } return(group); }
internal YamsterUser FetchUserById(long userId, YamsterModelEventCollector eventCollector) { YamsterUser user = this.GetUserById(userId, nullIfMissing: true); if (user == null) { user = new YamsterUser(userId, this); this.usersById.Add(userId, user); eventCollector.NotifyAfterAdd(user); } return(user); }
internal void NotifyMessageReadChanged(bool newReadValue, YamsterModelEventCollector eventCollector) { this.PerformActionAndCheckRead(() => { if (newReadValue) { ++readMessageCount; } else { --readMessageCount; } }, eventCollector); }
internal void UpdateConversation(DbConversation conversation, YamsterModelEventCollector eventCollector) { this.ConversationId = conversation.ConversationId; var users = new List <YamsterUser>(conversation.ParticipantUserIds.Count); foreach (long userId in conversation.ParticipantUserIds) { var user = YamsterCache.FetchUserById(userId, eventCollector); users.Add(user); } this.participants = new YamsterUserSet(users); eventCollector.NotifyAfterUpdate(this); }
internal void SetDbThreadState(DbThreadState newValue, YamsterModelEventCollector eventCollector) { if (newValue == null) { throw new ArgumentNullException("DbThreadState"); } if (newValue.ThreadId != this.ThreadId) { throw new ArgumentException("Cannot change ID"); } this.dbThreadState = newValue; UpdateLoadedStatus(); eventCollector.NotifyAfterUpdate(this); }
private void PerformActionAndCheckRead(Action action, YamsterModelEventCollector eventCollector) { bool oldThreadRead = this.Read; action(); Debug.Assert(readMessageCount == this.messagesInternal.Where(x => x.Read).Count()); if (this.Read != oldThreadRead && !this.AllMessagesDeleted) { this.Group.NotifyThreadReadChanged(this.Read, eventCollector); eventCollector.NotifyAfterUpdate(this); } }
void ProcessDbMessageState(DbMessageState record, YamsterModelEventCollector eventCollector) { // Does the message exist yet? var message = this.GetMessageById(record.MessageId, nullIfMissing: true); if (message != null) { message.SetDbMessageState(record, eventCollector); } else { // Stash the message state and deal with it later this.unresolvedMessageStatesById[record.MessageId] = record; } }
void CheckForListenedMessage(YamsterMessage message, YamsterModelEventCollector eventCollector) { ListenedMessage listenedMessage; if (!listenedMessagesById.TryGetValue(message.MessageId, out listenedMessage)) { return; } listenedMessagesById.Remove(message.MessageId); foreach (var messageAction in listenedMessage.ActionsByListener.Values) { messageAction(message, eventCollector); } }
internal void AddMessage(YamsterMessage message, YamsterModelEventCollector eventCollector) { // Insert sorted by MessageId int index = this.messagesInternal.BinarySearch(message, Comparer <YamsterMessage> .Create((x, y) => Math.Sign(x.MessageId - y.MessageId))); int deletedIndex = this.deletedMessagesInternal.BinarySearch(message, Comparer <YamsterMessage> .Create((x, y) => Math.Sign(x.MessageId - y.MessageId))); if (index >= 0 || deletedIndex >= 0) { throw new InvalidOperationException("Program Bug: The message was already added to this thread"); } bool oldAllMessagesDeleted = this.AllMessagesDeleted; if (!message.Deleted) { this.PerformActionAndCheckRead(() => { this.messagesInternal.Insert(~index, message); }, eventCollector); message.NotifyAddedToThread(this); if (message.Read) { // Increment the read message counter this.NotifyMessageReadChanged(newReadValue: true, eventCollector: eventCollector); } NotifyTotalLikesChanged(+message.LikesCount, eventCollector); // Adding a message changes LastUpdate, Messages.Count, and possibly other properties // like TotalLikesCount eventCollector.NotifyAfterUpdate(this); } else { this.deletedMessagesInternal.Insert(~deletedIndex, message); } if (oldAllMessagesDeleted != this.AllMessagesDeleted) { // Remove and re-add the thread so it moves to the appropriate // collection (YamsterGroup.Threads or DeletedThreads) this.Group.RemoveThread(this, eventCollector); this.Group.AddThread(this, eventCollector); } }
void ProcessDbThreadState(DbThreadState record) { // Does the thread exist yet? YamsterThread thread; if (threadsById.TryGetValue(record.ThreadId, out thread)) { var eventCollector = new YamsterModelEventCollector(); thread.SetDbThreadState(record, eventCollector); eventCollector.FireEvents(); } else { // Stash the record and deal with it later unresolvedThreadStatesById[record.ThreadId] = record; } }
void ProcessDbConversation(DbConversation record) { // Does the thread exist yet? YamsterThread thread; if (threadsByConversationId.TryGetValue(record.ConversationId, out thread)) { var eventCollector = new YamsterModelEventCollector(); thread.UpdateConversation(record, eventCollector); eventCollector.FireEvents(); } else { // Stash the conversation and deal with it later unresolvedConversationsById[record.ConversationId] = record; } }
internal void NotifyThreadReadChanged(bool newReadValue, YamsterModelEventCollector eventCollector) { bool oldGroupRead = this.Read; if (newReadValue) { ++readThreadCount; } else { --readThreadCount; } Debug.Assert(readThreadCount >= 0 && readThreadCount <= threadsInternal.Count); eventCollector.NotifyAfterUpdate(this); }
internal void RemoveMessage(YamsterMessage message, YamsterModelEventCollector eventCollector) { int index = this.messagesInternal.BinarySearch(message, Comparer <YamsterMessage> .Create((x, y) => Math.Sign(x.MessageId - y.MessageId))); int deletedIndex = this.deletedMessagesInternal.BinarySearch(message, Comparer <YamsterMessage> .Create((x, y) => Math.Sign(x.MessageId - y.MessageId))); if (index < 0 && deletedIndex < 0) { Debug.Assert(false, "RemoveMessage() called on message that doesn't belong to this thread"); return; } if (index >= 0) { bool oldAllMessagesDeleted = this.AllMessagesDeleted; this.PerformActionAndCheckRead(() => { this.messagesInternal.RemoveAt(index); }, eventCollector); if (oldAllMessagesDeleted != this.AllMessagesDeleted) { // Remove and re-add the thread so it moves to the appropriate // collection (YamsterGroup.Threads or DeletedThreads) // (Note that NotifyMessageReadChanged() assumes that the thread // is in the right collection.) this.Group.RemoveThread(this, eventCollector); this.Group.AddThread(this, eventCollector); } if (message.Read) { // Decrement the read message counter this.NotifyMessageReadChanged(newReadValue: false, eventCollector: eventCollector); } this.NotifyTotalLikesChanged(-message.LikesCount, eventCollector); } else { this.deletedMessagesInternal.RemoveAt(deletedIndex); } }
internal void SetDbMessageState(DbMessageState newValue, YamsterModelEventCollector eventCollector) { if (newValue == null) { throw new ArgumentNullException("DbMessageState"); } if (newValue.MessageId != messageId) { throw new ArgumentException("Cannot change ID"); } bool oldRead = this.Read; bool oldDeleted = this.Deleted; if (newValue.Deleted != oldDeleted) { // Remove and re-add the message so it moves to the appropriate // collection (YamsterThread.Messages or DeletedMessages). // (Note that NotifyMessageReadChanged() assumes that the message // is in the right collection.) this.Thread.RemoveMessage(this, eventCollector); this.dbMessageState = newValue; this.Thread.AddMessage(this, eventCollector); this.cachedPreviewText = null; } else { this.dbMessageState = newValue; if (this.Read != oldRead && !this.Deleted) { this.Thread.NotifyMessageReadChanged(this.Read, eventCollector); } } UpdateLoadedStatus(); eventCollector.NotifyAfterUpdate(this); }
internal void SetDbMessage(DbMessage newValue, YamsterModelEventCollector eventCollector) { if (newValue == null) { throw new ArgumentNullException("DbMessage"); } if (newValue.MessageId != messageId) { throw new ArgumentException("Cannot change ID"); } var oldValue = this.dbMessage; this.dbMessage = newValue; this.cachedPreviewText = null; this.group = this.YamsterCache.FetchGroupById(this.dbMessage.GroupId, eventCollector); this.senderUser = this.YamsterCache.FetchUserById(this.dbMessage.SenderUserId, eventCollector); this.messageRepliedTo = null; if (this.dbMessage.MessageIdRepliedTo != 0 // The Yammer UI doesn't show "in reply to" when messageRepliedTo is the threadstarter. // TODO: What does it do if the threadstarter's MessageId != ThreadId? && this.dbMessage.MessageIdRepliedTo != this.dbMessage.ThreadId) { this.YamsterCache.ListenForMessageById( this.dbMessage.MessageIdRepliedTo, this, eventCollector, delegate(YamsterMessage listenedMessage, YamsterModelEventCollector ec) { if (listenedMessage.MessageId == this.dbMessage.MessageIdRepliedTo) { this.messageRepliedTo = listenedMessage; ec.NotifyAfterUpdate(this); } } ); } int oldlikingUsersCount = likingUsers.Count; likingUsers.Clear(); foreach (long userId in this.dbMessage.LikingUserIds) { var user = this.YamsterCache.FetchUserById(userId, eventCollector); likingUsers.AddUser(user); } if (this.thread != null) { int totalLikesCountDelta = newValue.LikesCount - oldValue.LikesCount; if (totalLikesCountDelta != 0) { this.thread.NotifyTotalLikesChanged(totalLikesCountDelta, eventCollector); } } notifiedUsers.Clear(); foreach (long userId in this.dbMessage.NotifiedUserIds) { var user = this.YamsterCache.FetchUserById(userId, eventCollector); notifiedUsers.AddUser(user); } UpdateLoadedStatus(); eventCollector.NotifyAfterUpdate(this); }