/// <summary> /// Adds the argument message as a local echo. When this local echo is resolved <see cref="PendingMessageResolved"/> will get called. /// </summary> /// <param name="message"></param> public void AddLocalEcho(LocalEchoMessage message) { pendingMessages.Add(message); Messages.Add(message); NewMessagesArrived?.Invoke(new[] { message }); }
/// <summary> /// Replace or remove a message from the channel. /// </summary> /// <param name="echo">The local echo message (client-side).</param> /// <param name="final">The response message, or null if the message became invalid.</param> public void ReplaceMessage(LocalEchoMessage echo, Message final) { if (!pendingMessages.Remove(echo)) { throw new InvalidOperationException("Attempted to remove echo that wasn't present"); } Messages.Remove(echo); if (final == null) { MessageRemoved?.Invoke(echo); return; } if (Messages.Contains(final)) { // message already inserted, so let's throw away this update. // we may want to handle this better in the future, but for the time being api requests are single-threaded so order is assumed. MessageRemoved?.Invoke(echo); return; } Messages.Add(final); PendingMessageResolved?.Invoke(echo, final); }
/// <summary> /// Replace or remove a message from the channel. /// </summary> /// <param name="echo">The local echo message (client-side).</param> /// <param name="final">The response message, or null if the message became invalid.</param> public void ReplaceMessage(LocalEchoMessage echo, Message final) { if (!pendingMessages.Remove(echo)) { throw new InvalidOperationException("Attempted to remove echo that wasn't present"); } Messages.Remove(echo); if (final == null) { MessageRemoved?.Invoke(echo); return; } if (Messages.Contains(final)) { throw new InvalidOperationException("Attempted to add the same message again"); } Messages.Add(final); PendingMessageResolved?.Invoke(echo, final); }
/// <summary> /// Posts a message to the currently opened channel. /// </summary> /// <param name="text">The message text that is going to be posted</param> /// <param name="isAction">Is true if the message is an action, e.g.: user is currently eating </param> /// <param name="target">An optional target channel. If null, <see cref="CurrentChannel"/> will be used.</param> public void PostMessage(string text, bool isAction = false, Channel target = null) { if (target == null) { target = CurrentChannel.Value; } if (target == null) { return; } void dequeueAndRun() { if (postQueue.Count > 0) { postQueue.Dequeue().Invoke(); } } postQueue.Enqueue(() => { if (!api.IsLoggedIn) { target.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); return; } var message = new LocalEchoMessage { Sender = api.LocalUser.Value, Timestamp = DateTimeOffset.Now, ChannelId = target.Id, IsAction = isAction, Content = text }; target.AddLocalEcho(message); // if this is a PM and the first message, we need to do a special request to create the PM channel if (target.Type == ChannelType.PM && !target.Joined) { var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(target.Users.First(), message); createNewPrivateMessageRequest.Success += createRes => { target.Id = createRes.ChannelID; target.ReplaceMessage(message, createRes.Message); dequeueAndRun(); }; createNewPrivateMessageRequest.Failure += exception => { Logger.Error(exception, "Posting message failed."); target.ReplaceMessage(message, null); dequeueAndRun(); }; api.Queue(createNewPrivateMessageRequest); return; } var req = new PostMessageRequest(message); req.Success += m => { target.ReplaceMessage(message, m); dequeueAndRun(); }; req.Failure += exception => { Logger.Error(exception, "Posting message failed."); target.ReplaceMessage(message, null); dequeueAndRun(); }; api.Queue(req); }); // always run if the queue is empty if (postQueue.Count == 1) { dequeueAndRun(); } }