* LCIMClient.cs:
* LCIMMessage.cs: * LCIMTypedMessage.cs: * LCIMRecalledMessage.cs: * LCIMConversation.cs: * LCIMRcpController.cs: * LCIMUnreadController.cs: * LCIMMessageController.cs: * LCIMSessionController.cs: * LCIMConversationController.cs: chore: 完善功能模块
parent
f852fe2264
commit
48b0de3301
|
@ -237,8 +237,11 @@ namespace LeanCloud.Realtime {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Join() {
|
public async Task Join() {
|
||||||
LCIMPartiallySuccessResult result = await AddMembers(new string[] { Client.Id });
|
LCIMPartiallySuccessResult result = await Client.ConversationController.AddMembers(Id,
|
||||||
if (!result.IsSuccess) {
|
new string[] { Client.Id });
|
||||||
|
if (result.IsSuccess) {
|
||||||
|
ids.UnionWith(result.SuccessfulClientIdList);
|
||||||
|
} else {
|
||||||
LCIMOperationFailure error = result.FailureList[0];
|
LCIMOperationFailure error = result.FailureList[0];
|
||||||
throw new LCException(error.Code, error.Reason);
|
throw new LCException(error.Code, error.Reason);
|
||||||
}
|
}
|
||||||
|
|
|
@ -563,6 +563,48 @@ namespace LeanCloud.Realtime.Internal.Controller {
|
||||||
#region 消息处理
|
#region 消息处理
|
||||||
|
|
||||||
internal override async Task OnNotification(GenericCommand notification) {
|
internal override async Task OnNotification(GenericCommand notification) {
|
||||||
|
if (notification.Cmd == CommandType.Conv) {
|
||||||
|
await OnConversation(notification);
|
||||||
|
} else if (notification.Cmd == CommandType.Unread) {
|
||||||
|
await OnUnread(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnUnread(GenericCommand notification) {
|
||||||
|
UnreadCommand unread = notification.UnreadMessage;
|
||||||
|
|
||||||
|
IEnumerable<string> convIds = unread.Convs
|
||||||
|
.Select(conv => conv.Cid);
|
||||||
|
Dictionary<string, LCIMConversation> conversationDict = (await Client.GetConversationList(convIds))
|
||||||
|
.ToDictionary(item => item.Id);
|
||||||
|
ReadOnlyCollection<LCIMConversation> conversations = unread.Convs.Select(conv => {
|
||||||
|
// 设置对话中的未读数据
|
||||||
|
LCIMConversation conversation = conversationDict[conv.Cid];
|
||||||
|
conversation.Unread = conv.Unread;
|
||||||
|
if (conv.HasData || conv.HasBinaryMsg) {
|
||||||
|
// 如果有消息,则反序列化
|
||||||
|
LCIMMessage message = null;
|
||||||
|
if (conv.HasBinaryMsg) {
|
||||||
|
// 二进制消息
|
||||||
|
byte[] bytes = conv.BinaryMsg.ToByteArray();
|
||||||
|
message = LCIMBinaryMessage.Deserialize(bytes);
|
||||||
|
} else {
|
||||||
|
// 类型消息
|
||||||
|
message = LCIMTypedMessage.Deserialize(conv.Data);
|
||||||
|
}
|
||||||
|
// 填充消息数据
|
||||||
|
message.ConversationId = conv.Cid;
|
||||||
|
message.Id = conv.Mid;
|
||||||
|
message.FromClientId = conv.From;
|
||||||
|
message.SentTimestamp = conv.Timestamp;
|
||||||
|
conversation.LastMessage = message;
|
||||||
|
}
|
||||||
|
return conversation;
|
||||||
|
}).ToList().AsReadOnly();
|
||||||
|
Client.OnUnreadMessagesCountUpdated?.Invoke(conversations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnConversation(GenericCommand notification) {
|
||||||
ConvCommand convMessage = notification.ConvMessage;
|
ConvCommand convMessage = notification.ConvMessage;
|
||||||
switch (notification.Op) {
|
switch (notification.Op) {
|
||||||
case OpType.Joined:
|
case OpType.Joined:
|
||||||
|
|
|
@ -223,40 +223,20 @@ namespace LeanCloud.Realtime.Internal.Controller {
|
||||||
#region 消息处理
|
#region 消息处理
|
||||||
|
|
||||||
internal override async Task OnNotification(GenericCommand notification) {
|
internal override async Task OnNotification(GenericCommand notification) {
|
||||||
if (notification.Cmd == CommandType.Patch) {
|
if (notification.Cmd == CommandType.Direct) {
|
||||||
await OnMessagePatched(notification);
|
|
||||||
} else if (notification.Cmd == CommandType.Direct) {
|
|
||||||
await OnMessaage(notification);
|
await OnMessaage(notification);
|
||||||
|
} else if (notification.Cmd == CommandType.Patch) {
|
||||||
|
await OnMessagePatched(notification);
|
||||||
|
} else if (notification.Cmd == CommandType.Rcp) {
|
||||||
|
await OnMessageReceipt(notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnMessagePatched(GenericCommand notification) {
|
/// <summary>
|
||||||
PatchCommand patchMessage = notification.PatchMessage;
|
/// 接收消息事件
|
||||||
foreach (PatchItem patch in patchMessage.Patches) {
|
/// </summary>
|
||||||
// 获取对话
|
/// <param name="notification"></param>
|
||||||
LCIMConversation conversation = await Client.GetOrQueryConversation(patch.Cid);
|
/// <returns></returns>
|
||||||
LCIMMessage message;
|
|
||||||
if (patch.HasBinaryMsg) {
|
|
||||||
byte[] bytes = patch.BinaryMsg.ToByteArray();
|
|
||||||
message = LCIMBinaryMessage.Deserialize(bytes);
|
|
||||||
} else {
|
|
||||||
message = LCIMTypedMessage.Deserialize(patch.Data);
|
|
||||||
}
|
|
||||||
message.ConversationId = patch.Cid;
|
|
||||||
message.Id = patch.Mid;
|
|
||||||
message.FromClientId = patch.From;
|
|
||||||
message.SentTimestamp = patch.Timestamp;
|
|
||||||
message.PatchedTimestamp = patch.PatchTimestamp;
|
|
||||||
if (message is LCIMRecalledMessage recalledMessage) {
|
|
||||||
// 消息撤回
|
|
||||||
Client.OnMessageRecalled?.Invoke(conversation, recalledMessage);
|
|
||||||
} else {
|
|
||||||
// 消息修改
|
|
||||||
Client.OnMessageUpdated?.Invoke(conversation, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnMessaage(GenericCommand notification) {
|
private async Task OnMessaage(GenericCommand notification) {
|
||||||
DirectCommand direct = notification.DirectMessage;
|
DirectCommand direct = notification.DirectMessage;
|
||||||
// 反序列化消息
|
// 反序列化消息
|
||||||
|
@ -289,6 +269,58 @@ namespace LeanCloud.Realtime.Internal.Controller {
|
||||||
Client.OnMessage?.Invoke(conversation, message);
|
Client.OnMessage?.Invoke(conversation, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息被修改事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="notification"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task OnMessagePatched(GenericCommand notification) {
|
||||||
|
PatchCommand patchMessage = notification.PatchMessage;
|
||||||
|
foreach (PatchItem patch in patchMessage.Patches) {
|
||||||
|
// 获取对话
|
||||||
|
LCIMConversation conversation = await Client.GetOrQueryConversation(patch.Cid);
|
||||||
|
LCIMMessage message;
|
||||||
|
if (patch.HasBinaryMsg) {
|
||||||
|
byte[] bytes = patch.BinaryMsg.ToByteArray();
|
||||||
|
message = LCIMBinaryMessage.Deserialize(bytes);
|
||||||
|
} else {
|
||||||
|
message = LCIMTypedMessage.Deserialize(patch.Data);
|
||||||
|
}
|
||||||
|
message.ConversationId = patch.Cid;
|
||||||
|
message.Id = patch.Mid;
|
||||||
|
message.FromClientId = patch.From;
|
||||||
|
message.SentTimestamp = patch.Timestamp;
|
||||||
|
message.PatchedTimestamp = patch.PatchTimestamp;
|
||||||
|
if (message is LCIMRecalledMessage recalledMessage) {
|
||||||
|
// 消息撤回
|
||||||
|
Client.OnMessageRecalled?.Invoke(conversation, recalledMessage);
|
||||||
|
} else {
|
||||||
|
// 消息修改
|
||||||
|
Client.OnMessageUpdated?.Invoke(conversation, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息回执事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="notification"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task OnMessageReceipt(GenericCommand notification) {
|
||||||
|
RcpCommand rcp = notification.RcpMessage;
|
||||||
|
string convId = rcp.Cid;
|
||||||
|
string msgId = rcp.Id;
|
||||||
|
long timestamp = rcp.T;
|
||||||
|
bool isRead = rcp.Read;
|
||||||
|
string fromId = rcp.From;
|
||||||
|
LCIMConversation conversation = await Client.GetOrQueryConversation(convId);
|
||||||
|
if (isRead) {
|
||||||
|
Client.OnMessageRead?.Invoke(conversation, msgId);
|
||||||
|
} else {
|
||||||
|
Client.OnMessageDelivered?.Invoke(conversation, msgId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LeanCloud.Realtime.Protocol;
|
|
||||||
|
|
||||||
namespace LeanCloud.Realtime.Internal.Controller {
|
|
||||||
internal class LCIMRcpController : LCIMController {
|
|
||||||
internal LCIMRcpController(LCIMClient client) : base(client) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 消息处理
|
|
||||||
|
|
||||||
internal override async Task OnNotification(GenericCommand notification) {
|
|
||||||
RcpCommand rcp = notification.RcpMessage;
|
|
||||||
string convId = rcp.Cid;
|
|
||||||
string msgId = rcp.Id;
|
|
||||||
long timestamp = rcp.T;
|
|
||||||
bool isRead = rcp.Read;
|
|
||||||
string fromId = rcp.From;
|
|
||||||
LCIMConversation conversation = await Client.GetOrQueryConversation(convId);
|
|
||||||
if (isRead) {
|
|
||||||
Client.OnMessageRead?.Invoke(conversation, msgId);
|
|
||||||
} else {
|
|
||||||
Client.OnMessageDelivered?.Invoke(conversation, msgId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using LeanCloud.Realtime.Protocol;
|
|
||||||
|
|
||||||
namespace LeanCloud.Realtime.Internal.Controller {
|
|
||||||
internal class LCIMUnreadController : LCIMController {
|
|
||||||
internal LCIMUnreadController(LCIMClient client) : base(client) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 消息处理
|
|
||||||
|
|
||||||
internal override async Task OnNotification(GenericCommand notification) {
|
|
||||||
UnreadCommand unread = notification.UnreadMessage;
|
|
||||||
|
|
||||||
IEnumerable<string> convIds = unread.Convs
|
|
||||||
.Select(conv => conv.Cid);
|
|
||||||
Dictionary<string, LCIMConversation> conversationDict = (await Client.GetConversationList(convIds))
|
|
||||||
.ToDictionary(item => item.Id);
|
|
||||||
ReadOnlyCollection<LCIMConversation> conversations = unread.Convs.Select(conv => {
|
|
||||||
// 设置对话中的未读数据
|
|
||||||
LCIMConversation conversation = conversationDict[conv.Cid];
|
|
||||||
conversation.Unread = conv.Unread;
|
|
||||||
if (conv.HasData || conv.HasBinaryMsg) {
|
|
||||||
// 如果有消息,则反序列化
|
|
||||||
LCIMMessage message = null;
|
|
||||||
if (conv.HasBinaryMsg) {
|
|
||||||
// 二进制消息
|
|
||||||
byte[] bytes = conv.BinaryMsg.ToByteArray();
|
|
||||||
message = LCIMBinaryMessage.Deserialize(bytes);
|
|
||||||
} else {
|
|
||||||
// 类型消息
|
|
||||||
message = LCIMTypedMessage.Deserialize(conv.Data);
|
|
||||||
}
|
|
||||||
// 填充消息数据
|
|
||||||
message.ConversationId = conv.Cid;
|
|
||||||
message.Id = conv.Mid;
|
|
||||||
message.FromClientId = conv.From;
|
|
||||||
message.SentTimestamp = conv.Timestamp;
|
|
||||||
conversation.LastMessage = message;
|
|
||||||
}
|
|
||||||
return conversation;
|
|
||||||
}).ToList().AsReadOnly();
|
|
||||||
Client.OnUnreadMessagesCountUpdated?.Invoke(conversations);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -231,10 +231,6 @@ namespace LeanCloud.Realtime {
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal LCIMUnreadController UnreadController {
|
|
||||||
get; private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal LCIMGoAwayController GoAwayController {
|
internal LCIMGoAwayController GoAwayController {
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
@ -243,10 +239,6 @@ namespace LeanCloud.Realtime {
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal LCIMRcpController RcpController {
|
|
||||||
get; private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 接口
|
#region 接口
|
||||||
|
|
||||||
public LCIMClient(string clientId,
|
public LCIMClient(string clientId,
|
||||||
|
@ -289,9 +281,7 @@ namespace LeanCloud.Realtime {
|
||||||
SessionController = new LCIMSessionController(this);
|
SessionController = new LCIMSessionController(this);
|
||||||
ConversationController = new LCIMConversationController(this);
|
ConversationController = new LCIMConversationController(this);
|
||||||
MessageController = new LCIMMessageController(this);
|
MessageController = new LCIMMessageController(this);
|
||||||
UnreadController = new LCIMUnreadController(this);
|
|
||||||
GoAwayController = new LCIMGoAwayController(this);
|
GoAwayController = new LCIMGoAwayController(this);
|
||||||
RcpController = new LCIMRcpController(this);
|
|
||||||
|
|
||||||
Connection = new LCConnection(Id) {
|
Connection = new LCConnection(Id) {
|
||||||
OnNotification = OnConnectionNotification,
|
OnNotification = OnConnectionNotification,
|
||||||
|
@ -446,21 +436,17 @@ namespace LeanCloud.Realtime {
|
||||||
_ = SessionController.OnNotification(notification);
|
_ = SessionController.OnNotification(notification);
|
||||||
break;
|
break;
|
||||||
case CommandType.Conv:
|
case CommandType.Conv:
|
||||||
|
case CommandType.Unread:
|
||||||
_ = ConversationController.OnNotification(notification);
|
_ = ConversationController.OnNotification(notification);
|
||||||
break;
|
break;
|
||||||
case CommandType.Direct:
|
case CommandType.Direct:
|
||||||
case CommandType.Patch:
|
case CommandType.Patch:
|
||||||
|
case CommandType.Rcp:
|
||||||
_ = MessageController.OnNotification(notification);
|
_ = MessageController.OnNotification(notification);
|
||||||
break;
|
break;
|
||||||
case CommandType.Unread:
|
|
||||||
_ = UnreadController.OnNotification(notification);
|
|
||||||
break;
|
|
||||||
case CommandType.Goaway:
|
case CommandType.Goaway:
|
||||||
_ = GoAwayController.OnNotification(notification);
|
_ = GoAwayController.OnNotification(notification);
|
||||||
break;
|
break;
|
||||||
case CommandType.Rcp:
|
|
||||||
_ = RcpController.OnNotification(notification);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using System.Collections.ObjectModel;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
using LeanCloud;
|
using LeanCloud;
|
||||||
using LeanCloud.Common;
|
using LeanCloud.Common;
|
||||||
using LeanCloud.Storage;
|
using LeanCloud.Storage;
|
||||||
using LeanCloud.Realtime;
|
using LeanCloud.Realtime;
|
||||||
|
|
||||||
using static NUnit.Framework.TestContext;
|
using static System.Console;
|
||||||
|
|
||||||
namespace Realtime.Test {
|
namespace Realtime.Test {
|
||||||
public class Message {
|
public class Message {
|
||||||
|
@ -34,25 +36,16 @@ namespace Realtime.Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Order(0)]
|
|
||||||
public async Task Send() {
|
public async Task Send() {
|
||||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
AutoResetEvent are = new AutoResetEvent(false);
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
m2.OnMessage = (conv, msg) => {
|
m2.OnMessage = (conv, msg) => {
|
||||||
WriteLine(msg.Id);
|
WriteLine(msg.Id);
|
||||||
if (msg is LCIMImageMessage imageMsg) {
|
if (msg is LCIMImageMessage imageMsg) {
|
||||||
WriteLine($"-------- url: {imageMsg.Url}");
|
WriteLine($"-------- url: {imageMsg.Url}");
|
||||||
count++;
|
|
||||||
} else if (msg is LCIMFileMessage fileMsg) {
|
} else if (msg is LCIMFileMessage fileMsg) {
|
||||||
WriteLine($"-------- name: {fileMsg.Format}");
|
WriteLine($"-------- name: {fileMsg.Format}");
|
||||||
count++;
|
|
||||||
} else if (msg is LCIMTextMessage textMsg) {
|
} else if (msg is LCIMTextMessage textMsg) {
|
||||||
WriteLine($"-------- text: {textMsg.Text}");
|
WriteLine($"-------- text: {textMsg.Text}");
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (count >= 3) {
|
|
||||||
tcs.SetResult(null);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -71,79 +64,6 @@ namespace Realtime.Test {
|
||||||
LCIMFileMessage fileMessage = new LCIMFileMessage(file);
|
LCIMFileMessage fileMessage = new LCIMFileMessage(file);
|
||||||
await conversation.Send(fileMessage);
|
await conversation.Send(fileMessage);
|
||||||
Assert.NotNull(fileMessage.Id);
|
Assert.NotNull(fileMessage.Id);
|
||||||
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
[Order(1)]
|
|
||||||
public async Task AckAndRead() {
|
|
||||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
|
||||||
m2.OnMessage = async (conv, msg) => {
|
|
||||||
await conv.Read();
|
|
||||||
};
|
|
||||||
m1.OnMessageDelivered = (conv, msgId) => {
|
|
||||||
WriteLine($"{msgId} is delivered.");
|
|
||||||
};
|
|
||||||
m1.OnMessageRead = (conv, msgId) => {
|
|
||||||
WriteLine($"{msgId} is read.");
|
|
||||||
tcs.SetResult(null);
|
|
||||||
};
|
|
||||||
LCIMTextMessage textMessage = new LCIMTextMessage("hello");
|
|
||||||
LCIMMessageSendOptions options = new LCIMMessageSendOptions {
|
|
||||||
Receipt = true
|
|
||||||
};
|
|
||||||
await conversation.Send(textMessage, options);
|
|
||||||
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
[Order(2)]
|
|
||||||
public async Task Recall() {
|
|
||||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
|
||||||
m2.OnMessageRecalled = (conv, msgId) => {
|
|
||||||
WriteLine($"{msgId} is recalled.");
|
|
||||||
tcs.SetResult(null);
|
|
||||||
};
|
|
||||||
LCIMTextMessage textMessage = new LCIMTextMessage("I will be recalled.");
|
|
||||||
await conversation.Send(textMessage);
|
|
||||||
await Task.Delay(1000);
|
|
||||||
await conversation.RecallMessage(textMessage);
|
|
||||||
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
[Order(3)]
|
|
||||||
public async Task Update() {
|
|
||||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
|
||||||
m2.OnMessageUpdated = (conv, msg) => {
|
|
||||||
Assert.True(msg is LCIMTextMessage);
|
|
||||||
LCIMTextMessage textMessage = msg as LCIMTextMessage;
|
|
||||||
Assert.AreEqual(textMessage.Text, "world");
|
|
||||||
WriteLine($"{msg.Id} is updated");
|
|
||||||
tcs.SetResult(null);
|
|
||||||
};
|
|
||||||
LCIMTextMessage oldMessage = new LCIMTextMessage("hello");
|
|
||||||
await conversation.Send(oldMessage);
|
|
||||||
await Task.Delay(1000);
|
|
||||||
LCIMTextMessage newMessage = new LCIMTextMessage("world");
|
|
||||||
await conversation.UpdateMessage(oldMessage, newMessage);
|
|
||||||
|
|
||||||
await tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
[Order(4)]
|
|
||||||
public async Task Query() {
|
|
||||||
ReadOnlyCollection<LCIMMessage> messages = await conversation.QueryMessages();
|
|
||||||
Assert.Greater(messages.Count, 0);
|
|
||||||
foreach (LCIMMessage message in messages) {
|
|
||||||
Assert.AreEqual(message.ConversationId, conversation.Id);
|
|
||||||
Assert.NotNull(message.Id);
|
|
||||||
WriteLine(message.Id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue