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; }
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(); }
void UpdateViewWithMessage(YamsterMessage message) { bool shouldBeInView = false; if (CompiledFunc != null) { var executionContext = new YqlExecutionContext(this.appContext); executionContext.Message = message; shouldBeInView = CompiledFunc(executionContext); } ViewedMessage viewedMessage = null; bool isInView = viewedMessagesById.TryGetValue(message.MessageId, out viewedMessage); bool statisticsChanged = false; if (isInView) { if (!shouldBeInView) { this.RemoveViewedMessage(viewedMessage); NotifyViewChanged(YamsterViewChangeType.ModelLeaveView, message); // TotalItemCount changed statisticsChanged = true; } else { if (message.Read != viewedMessage.Read) { this.readMessageCount += message.Read ? +1 : -1; viewedMessage.Read = message.Read; statisticsChanged = true; } } } else { if (shouldBeInView) { AddViewedMessage(new ViewedMessage(message)); NotifyViewChanged(YamsterViewChangeType.ModelEnterView, message); // TotalItemCount changed statisticsChanged = true; } } if (statisticsChanged) { NotifyViewChanged(YamsterViewChangeType.StatisticsChanged, null); } }
public static YamsterNewMessage CreateReply(YamsterMessage messageBeingRepliedTo) { if (messageBeingRepliedTo == null) { throw new ArgumentNullException("messageRepliedTo"); } var newMessage = new YamsterNewMessage(); newMessage.MessageBeingRepliedTo = messageBeingRepliedTo; newMessage.Group = messageBeingRepliedTo.Group; return(newMessage); }
public YamsterMessage GetMessageById(long messageId, bool nullIfMissing = false) { YamsterMessage message = null; if (!this.messagesById.TryGetValue(messageId, out message)) { if (!nullIfMissing) { throw new KeyNotFoundException("The message ID " + messageId + " was not found"); } } return(message); }
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); } }
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); } }
public ViewedMessage(YamsterMessage message) { this.Message = message; this.Read = message.Read; }
public YamsterMessageChangedEventArgs(YamsterMessage message, YamsterModelChangeType changeType) : base(changeType) { this.Message = message; }
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); }