Merge pull request #92 from onerain88/chore

完善开发指南中的接口
oneRain 2020-12-24 11:14:34 +08:00 committed by GitHub
commit d263de14b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 252 additions and 70 deletions

View File

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<ReleaseVersion>0.4.2</ReleaseVersion> <ReleaseVersion>0.4.2</ReleaseVersion>
<AssemblyName>Common</AssemblyName>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -4,6 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<ReleaseVersion>0.4.2</ReleaseVersion> <ReleaseVersion>0.4.2</ReleaseVersion>
<AssemblyName>LiveQuery</AssemblyName>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -4,6 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<ReleaseVersion>0.4.2</ReleaseVersion> <ReleaseVersion>0.4.2</ReleaseVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AssemblyName>Realtime</AssemblyName>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -10,15 +10,22 @@ using static NUnit.Framework.TestContext;
namespace Realtime.Test { namespace Realtime.Test {
public class Client { public class Client {
private const string USERNAME1 = "username1";
private const string PASSWORD1 = "password1";
private const string USERNAME2 = "username2";
private const string PASSWORD2 = "password2";
[SetUp] [SetUp]
public void SetUp() { public async Task SetUp() {
LCLogger.LogDelegate += Utils.Print; Utils.SetUp();
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com"); await NewUser(USERNAME1, PASSWORD1);
await NewUser(USERNAME2, PASSWORD2);
} }
[TearDown] [TearDown]
public void TearDown() { public void TearDown() {
LCLogger.LogDelegate -= Utils.Print; Utils.TearDown();
} }
[Test] [Test]
@ -34,12 +41,12 @@ namespace Realtime.Test {
[Test] [Test]
public async Task OpenAndCloseByLCUser() { public async Task OpenAndCloseByLCUser() {
LCUser user = await LCUser.Login("hello", "world"); LCUser user = await LCUser.Login(USERNAME1, PASSWORD1);
LCIMClient client = new LCIMClient(user); LCIMClient client = new LCIMClient(user);
await client.Open(); await client.Open();
LCUser game = await LCUser.Login("game", "play"); LCUser game = await LCUser.Login(USERNAME2, PASSWORD2);
LCIMClient client2 = new LCIMClient(game); LCIMClient client2 = new LCIMClient(game);
await client2.Open(); await client2.Open();
@ -134,5 +141,21 @@ namespace Realtime.Test {
await tcs.Task; await tcs.Task;
} }
private async Task NewUser(string username, string password) {
try {
await LCUser.Login(username, password);
} catch (LCException e) {
if (e.Code == 211) {
LCUser user1 = new LCUser {
Username = username,
Password = password
};
await user1.SignUp();
} else {
throw e;
}
}
}
} }
} }

View File

@ -16,8 +16,7 @@ namespace Realtime.Test {
[SetUp] [SetUp]
public async Task SetUp() { public async Task SetUp() {
LCLogger.LogDelegate += Utils.Print; Utils.SetUp();
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
c1 = new LCIMClient(Guid.NewGuid().ToString()); c1 = new LCIMClient(Guid.NewGuid().ToString());
await c1.Open(); await c1.Open();
c2 = new LCIMClient(Guid.NewGuid().ToString()); c2 = new LCIMClient(Guid.NewGuid().ToString());
@ -32,7 +31,7 @@ namespace Realtime.Test {
await c1.Close(); await c1.Close();
await c2.Close(); await c2.Close();
await lean.Close(); await lean.Close();
LCLogger.LogDelegate -= Utils.Print; Utils.TearDown();
} }
[Test] [Test]

View File

@ -1,27 +1,24 @@
using NUnit.Framework; using NUnit.Framework;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using LeanCloud;
using LeanCloud.Common;
using LeanCloud.Realtime; using LeanCloud.Realtime;
namespace Realtime.Test { namespace Realtime.Test {
public class ConversationQuery { public class ConversationQuery {
private string clientId = "hello123"; private readonly string clientId = "m1";
private LCIMClient client; private LCIMClient client;
[SetUp] [SetUp]
public async Task SetUp() { public async Task SetUp() {
LCLogger.LogDelegate += Utils.Print; Utils.SetUp();
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
client = new LCIMClient(clientId); client = new LCIMClient(clientId);
await client.Open(); await client.Open();
} }
[TearDown] [TearDown]
public async Task TearDown() { public async Task TearDown() {
LCLogger.LogDelegate -= Utils.Print;
await client.Close(); await client.Close();
Utils.TearDown();
} }
[Test] [Test]
@ -36,7 +33,7 @@ namespace Realtime.Test {
[Test] [Test]
public async Task QueryMemberConversation() { public async Task QueryMemberConversation() {
string memberId = "cc1"; string memberId = "m1";
LCIMConversationQuery query = new LCIMConversationQuery(client); LCIMConversationQuery query = new LCIMConversationQuery(client);
query.WhereEqualTo("m", memberId); query.WhereEqualTo("m", memberId);
ReadOnlyCollection<LCIMConversation> conversations = await query.Find(); ReadOnlyCollection<LCIMConversation> conversations = await query.Find();
@ -45,5 +42,34 @@ namespace Realtime.Test {
Assert.True(conversation.MemberIds.Contains(memberId)); Assert.True(conversation.MemberIds.Contains(memberId));
} }
} }
[Test]
public async Task QueryCompact() {
string memberId = "m1";
LCIMConversationQuery query = new LCIMConversationQuery(client)
.WhereEqualTo("m", memberId);
query.Compact = true;
ReadOnlyCollection<LCIMConversation> conversations = await query.Find();
foreach (LCIMConversation conversation in conversations) {
Assert.True(conversation.MemberIds.Count == 0);
await conversation.Fetch();
Assert.True(conversation.MemberIds.Count > 0);
}
}
[Test]
public async Task QueryWithLastMessage() {
string memberId = "m1";
LCIMConversationQuery query = new LCIMConversationQuery(client)
.WhereEqualTo("m", memberId);
query.WithLastMessageRefreshed = true;
ReadOnlyCollection<LCIMConversation> conversations = await query.Find();
foreach (LCIMConversation conversation in conversations) {
Assert.True(!string.IsNullOrEmpty(conversation.LastMessage.Id));
if (conversation.LastMessage is LCIMBinaryMessage binaryMessage) {
TestContext.WriteLine(System.Text.Encoding.UTF8.GetString(binaryMessage.Data));
}
}
}
} }
} }

View File

@ -32,8 +32,7 @@ namespace Realtime.Test {
[SetUp] [SetUp]
public async Task SetUp() { public async Task SetUp() {
LCLogger.LogDelegate += Utils.Print; Utils.SetUp();
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
m1 = new LCIMClient("m1"); m1 = new LCIMClient("m1");
m2 = new LCIMClient("m2"); m2 = new LCIMClient("m2");
await m1.Open(); await m1.Open();
@ -45,7 +44,7 @@ namespace Realtime.Test {
public async Task TearDown() { public async Task TearDown() {
await m1.Close(); await m1.Close();
await m2.Close(); await m2.Close();
LCLogger.LogDelegate -= Utils.Print; Utils.TearDown();
} }
[Test] [Test]
@ -76,17 +75,19 @@ namespace Realtime.Test {
Assert.NotNull(textMessage.Id); Assert.NotNull(textMessage.Id);
LCFile image = new LCFile("hello", "../../../../../assets/hello.png"); LCFile image = new LCFile("hello", "../../../../../assets/hello.png");
await image.Save();
LCIMImageMessage imageMessage = new LCIMImageMessage(image); LCIMImageMessage imageMessage = new LCIMImageMessage(image);
await conversation.Send(imageMessage); await conversation.Send(imageMessage);
Assert.NotNull(imageMessage.Id); Assert.NotNull(imageMessage.Id);
LCFile file = new LCFile("apk", "../../../../../assets/test.apk"); LCFile file = new LCFile("apk", "../../../../../assets/test.apk");
await file.Save();
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);
LCIMBinaryMessage binaryMessage = new LCIMBinaryMessage(System.Text.Encoding.UTF8.GetBytes("LeanCloud"));
await conversation.Send(binaryMessage);
Assert.NotNull(binaryMessage.Id);
await tcs.Task; await tcs.Task;
} }
@ -152,7 +153,7 @@ namespace Realtime.Test {
[Test] [Test]
[Order(4)] [Order(4)]
public async Task Query() { public async Task Query() {
ReadOnlyCollection<LCIMMessage> messages = await conversation.QueryMessages(); ReadOnlyCollection<LCIMMessage> messages = await conversation.QueryMessages(messageType: -6);
Assert.Greater(messages.Count, 0); Assert.Greater(messages.Count, 0);
foreach (LCIMMessage message in messages) { foreach (LCIMMessage message in messages) {
Assert.AreEqual(message.ConversationId, conversation.Id); Assert.AreEqual(message.ConversationId, conversation.Id);

View File

@ -1,7 +1,6 @@
using NUnit.Framework; using NUnit.Framework;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using LeanCloud;
using LeanCloud.Realtime; using LeanCloud.Realtime;
using LeanCloud.Realtime.Internal.Protocol; using LeanCloud.Realtime.Internal.Protocol;
@ -14,8 +13,7 @@ namespace Realtime.Test {
[SetUp] [SetUp]
public async Task SetUp() { public async Task SetUp() {
LCLogger.LogDelegate += Utils.Print; Utils.SetUp();
LCApplication.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
c1 = new LCIMClient(Guid.NewGuid().ToString()); c1 = new LCIMClient(Guid.NewGuid().ToString());
c2 = new LCIMClient(Guid.NewGuid().ToString()); c2 = new LCIMClient(Guid.NewGuid().ToString());
await c1.Open(); await c1.Open();
@ -28,7 +26,7 @@ namespace Realtime.Test {
public async Task TearDown() { public async Task TearDown() {
await c1.Close(); await c1.Close();
await c2.Close(); await c2.Close();
LCLogger.LogDelegate -= Utils.Print; Utils.TearDown();
} }
[Test] [Test]

View File

@ -1,10 +1,18 @@
using System; using System;
using LeanCloud; using LeanCloud;
using LeanCloud.Common;
using NUnit.Framework; using NUnit.Framework;
namespace Realtime.Test { namespace Realtime.Test {
public static class Utils { public static class Utils {
internal static void SetUp() {
LCLogger.LogDelegate += Print;
LCApplication.Initialize("3zWMOXuO9iSdnjXM942i6DdI-gzGzoHsz", "bkwiNq4Tj417eUaHlTWS5sPm", "https://3zwmoxuo.lc-cn-n1-shared.com");
}
internal static void TearDown() {
LCLogger.LogDelegate -= Print;
}
internal static void Print(LCLogLevel level, string info) { internal static void Print(LCLogLevel level, string info) {
switch (level) { switch (level) {
case LCLogLevel.Debug: case LCLogLevel.Debug:

View File

@ -28,5 +28,9 @@ namespace LeanCloud.Realtime {
public override Task<LCIMPartiallySuccessResult> AddMembers(IEnumerable<string> clientIds) { public override Task<LCIMPartiallySuccessResult> AddMembers(IEnumerable<string> clientIds) {
throw new Exception("Add members is not allowed in chat room."); throw new Exception("Add members is not allowed in chat room.");
} }
public override Task Read() {
return Task.CompletedTask;
}
} }
} }

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using System.Linq;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using LeanCloud.Storage;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
/// <summary> /// <summary>
@ -180,9 +179,8 @@ namespace LeanCloud.Realtime {
/// <summary> /// <summary>
/// Mark the last message of this conversation as read. /// Mark the last message of this conversation as read.
/// </summary> /// </summary>
/// <param name="message"></param>
/// <returns></returns> /// <returns></returns>
public async Task Read() { public virtual async Task Read() {
if (LastMessage == null) { if (LastMessage == null) {
return; return;
} }
@ -260,10 +258,13 @@ namespace LeanCloud.Realtime {
} }
} }
/// <summary> /// <summary>
/// Sends a message in this conversation. /// Sends a message in this conversation.
/// </summary> /// </summary>
/// <param name="message">The message to send.</param> /// <param name="message">The message to send.</param>
/// <param name="options">The options of sending message.</param>
/// <returns></returns> /// <returns></returns>
public async Task<LCIMMessage> Send(LCIMMessage message, public async Task<LCIMMessage> Send(LCIMMessage message,
LCIMMessageSendOptions options = null) { LCIMMessageSendOptions options = null) {
@ -273,6 +274,7 @@ namespace LeanCloud.Realtime {
if (options == null) { if (options == null) {
options = LCIMMessageSendOptions.Default; options = LCIMMessageSendOptions.Default;
} }
await message.PrepareSend();
await Client.MessageController.Send(Id, message, options); await Client.MessageController.Send(Id, message, options);
LastMessage = message; LastMessage = message;
return message; return message;
@ -450,7 +452,7 @@ namespace LeanCloud.Realtime {
/// <param name="start">Start message ID.</param> /// <param name="start">Start message ID.</param>
/// <param name="end">End message ID.</param> /// <param name="end">End message ID.</param>
/// <param name="direction">Query direction (defaults to NewToOld).</param> /// <param name="direction">Query direction (defaults to NewToOld).</param>
/// <param name="limit">Limits the number of returned results. Its default value is 20.</param> /// <param name="limit">Limits the number of returned results. Its default value is 100.</param>
/// <param name="messageType">The message type to query. The default value is 0 (text message).</param> /// <param name="messageType">The message type to query. The default value is 0 (text message).</param>
/// <returns></returns> /// <returns></returns>
public async Task<ReadOnlyCollection<LCIMMessage>> QueryMessages(LCIMMessageQueryEndpoint start = null, public async Task<ReadOnlyCollection<LCIMMessage>> QueryMessages(LCIMMessageQueryEndpoint start = null,
@ -469,6 +471,17 @@ namespace LeanCloud.Realtime {
await Client.ConversationController.FetchReciptTimestamp(Id); await Client.ConversationController.FetchReciptTimestamp(Id);
} }
/// <summary>
/// Fetch conversation from server.
/// </summary>
/// <returns></returns>
public async Task<LCIMConversation> Fetch() {
LCIMConversationQuery query = new LCIMConversationQuery(Client);
query.WhereEqualTo("objectId", Id);
await query.Find();
return this;
}
internal static bool IsTemporayConversation(string convId) { internal static bool IsTemporayConversation(string convId) {
return convId.StartsWith("_tmp:"); return convId.StartsWith("_tmp:");
} }
@ -503,9 +516,28 @@ namespace LeanCloud.Realtime {
IEnumerable<string> ids = (muo as IList<object>).Cast<string>(); IEnumerable<string> ids = (muo as IList<object>).Cast<string>();
mutedIds = new HashSet<string>(ids); mutedIds = new HashSet<string>(ids);
} }
//if (conv.TryGetValue("lm", out object lmo)) { if (conv.TryGetValue("msg", out object msgo)) {
// LastMessageAt = (DateTime)LCDecoder.Decode(lmo); if (conv.TryGetValue("bin", out object bino)) {
//} string msg = msgo as string;
bool bin = (bool)bino;
if (bin) {
byte[] bytes = Convert.FromBase64String(msg);
LastMessage = LCIMBinaryMessage.Deserialize(bytes);
} else {
LastMessage = LCIMTypedMessage.Deserialize(msg);
}
}
LastMessage.ConversationId = Id;
if (conv.TryGetValue("msg_mid", out object msgId)) {
LastMessage.Id = msgId as string;
}
if (conv.TryGetValue("msg_from", out object msgFrom)) {
LastMessage.FromClientId = msgFrom as string;
}
if (conv.TryGetValue("msg_timestamp", out object timestamp)) {
LastMessage.SentTimestamp = (long)timestamp;
}
}
} }
internal void MergeInfo(Dictionary<string, object> attr) { internal void MergeInfo(Dictionary<string, object> attr) {

View File

@ -2,15 +2,35 @@
using System.Collections; using System.Collections;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using LeanCloud.Storage.Internal.Query; using LeanCloud.Storage.Internal.Query;
using System.Linq;
using System.Collections.Generic;
using System;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMConversationQuery { public class LCIMConversationQuery {
internal const int CompactFlag = 0x1;
internal const int WithLastMessageFlag = 0x2;
internal LCCompositionalCondition Condition { internal LCCompositionalCondition Condition {
get; private set; get; private set;
} }
private readonly LCIMClient client; private readonly LCIMClient client;
/// <summary>
/// Ignore the members of conversation.
/// </summary>
public bool Compact {
get; set;
} = false;
/// <summary>
/// With the last message.
/// </summary>
public bool WithLastMessageRefreshed {
get; set;
} = false;
public LCIMConversationQuery(LCIMClient client) { public LCIMConversationQuery(LCIMClient client) {
Condition = new LCCompositionalCondition(); Condition = new LCCompositionalCondition();
this.client = client; this.client = client;
@ -250,10 +270,6 @@ namespace LeanCloud.Realtime {
return this; return this;
} }
public bool WithLastMessageRefreshed {
get; set;
}
/// <summary> /// <summary>
/// Retrieves a list of LCObjects matching this query. /// Retrieves a list of LCObjects matching this query.
/// </summary> /// </summary>
@ -261,5 +277,28 @@ namespace LeanCloud.Realtime {
public async Task<ReadOnlyCollection<LCIMConversation>> Find() { public async Task<ReadOnlyCollection<LCIMConversation>> Find() {
return await client.ConversationController.Find(this); return await client.ConversationController.Find(this);
} }
/// <summary>
/// Retrieves the first conversation from the query.
/// </summary>
/// <returns></returns>
public async Task<LCIMConversation> First() {
Limit(1);
ReadOnlyCollection<LCIMConversation> conversations = await Find();
return conversations?.First();
}
/// <summary>
/// Retrieves the conversation.
/// </summary>
/// <param name="convId"></param>
/// <returns></returns>
public Task<LCIMConversation> Get(string convId) {
if (string.IsNullOrEmpty(convId)) {
throw new ArgumentNullException(nameof(convId));
}
WhereEqualTo("objectId", convId);
return First();
}
} }
} }

View File

@ -355,6 +355,22 @@ namespace LeanCloud.Realtime.Internal.Controller {
LCLogger.Error(e); LCLogger.Error(e);
} }
} }
int flag = 0;
if (query.Compact) {
flag += LCIMConversationQuery.CompactFlag;
}
if (query.WithLastMessageRefreshed) {
flag += LCIMConversationQuery.WithLastMessageFlag;
}
if (flag > 0) {
convMessage.Flag = flag;
}
convMessage.Skip = query.Condition.Skip;
convMessage.Limit = query.Condition.Limit;
string orders = query.Condition.BuildOrders();
if (!string.IsNullOrEmpty(orders)) {
convMessage.Sort = orders;
}
command.ConvMessage = convMessage; command.ConvMessage = convMessage;
GenericCommand response = await Connection.SendRequest(command); GenericCommand response = await Connection.SendRequest(command);
JsonObjectMessage results = response.ConvMessage.Results; JsonObjectMessage results = response.ConvMessage.Results;

View File

@ -234,15 +234,15 @@ namespace LeanCloud.Realtime.Internal.Controller {
message.MentionIdList.Contains(Client.Id); message.MentionIdList.Contains(Client.Id);
message.PatchedTimestamp = direct.PatchTimestamp; message.PatchedTimestamp = direct.PatchTimestamp;
message.IsTransient = direct.Transient; message.IsTransient = direct.Transient;
// 通知服务端已接收
if (!message.IsTransient) {
// 只有非暂态消息才需要发送 ack
_ = Ack(message.ConversationId, message.Id);
}
// 获取对话 // 获取对话
LCIMConversation conversation = await Client.GetOrQueryConversation(direct.Cid); LCIMConversation conversation = await Client.GetOrQueryConversation(direct.Cid);
conversation.Unread++; conversation.Unread++;
conversation.LastMessage = message; conversation.LastMessage = message;
// 通知服务端已接收
if (!(conversation is LCIMChatRoom) && !message.IsTransient) {
// 只有非暂态消息才需要发送 ack
_ = Ack(message.ConversationId, message.Id);
}
Client.OnMessage?.Invoke(conversation, message); Client.OnMessage?.Invoke(conversation, message);
} }
@ -285,8 +285,16 @@ namespace LeanCloud.Realtime.Internal.Controller {
LCIMConversation conversation = await Client.GetOrQueryConversation(convId); LCIMConversation conversation = await Client.GetOrQueryConversation(convId);
if (isRead) { if (isRead) {
Client.OnMessageRead?.Invoke(conversation, msgId); Client.OnMessageRead?.Invoke(conversation, msgId);
if (timestamp > conversation.LastReadTimestamp) {
conversation.LastReadTimestamp = timestamp;
Client.OnLastReadAtUpdated?.Invoke(conversation);
}
} else { } else {
Client.OnMessageDelivered?.Invoke(conversation, msgId); Client.OnMessageDelivered?.Invoke(conversation, msgId);
if (timestamp > conversation.LastDeliveredTimestamp) {
conversation.LastDeliveredTimestamp = timestamp;
Client.OnLastDeliveredAtUpdated?.Invoke(conversation);
}
} }
} }

View File

@ -37,8 +37,6 @@ namespace LeanCloud.Realtime {
get; private set; get; private set;
} }
#region 事件
#region 连接状态事件 #region 连接状态事件
/// <summary> /// <summary>
@ -206,21 +204,19 @@ namespace LeanCloud.Realtime {
/// <summary> /// <summary>
/// Occurs when the last delivered message is updated. /// Occurs when the last delivered message is updated.
/// </summary> /// </summary>
public Action OnLastDeliveredAtUpdated { public Action<LCIMConversation> OnLastDeliveredAtUpdated {
get; set; get; set;
} }
/// <summary> /// <summary>
/// Occurs when the last delivered message is updated. /// Occurs when the last read message is updated.
/// </summary> /// </summary>
public Action OnLastReadAtUpdated { public Action<LCIMConversation> OnLastReadAtUpdated {
get; set; get; set;
} }
#endregion #endregion
#endregion
internal ILCIMSignatureFactory SignatureFactory { internal ILCIMSignatureFactory SignatureFactory {
get; private set; get; private set;
} }

View File

@ -2,6 +2,7 @@
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using LeanCloud.Storage; using LeanCloud.Storage;
using System.Threading.Tasks;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
public class LCIMFileMessage : LCIMTextMessage { public class LCIMFileMessage : LCIMTextMessage {
@ -125,5 +126,11 @@ namespace LeanCloud.Realtime {
} }
} }
} }
internal override async Task PrepareSend() {
if (File != null && string.IsNullOrEmpty(File.ObjectId)) {
await File.Save();
}
}
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
namespace LeanCloud.Realtime { namespace LeanCloud.Realtime {
/// <summary> /// <summary>
@ -125,5 +126,9 @@ namespace LeanCloud.Realtime {
internal LCIMMessage() { internal LCIMMessage() {
} }
internal virtual Task PrepareSend() {
return Task.CompletedTask;
}
} }
} }

View File

@ -5,7 +5,7 @@ namespace LeanCloud.Realtime {
/// The priority for sending messages in chatroom. /// The priority for sending messages in chatroom.
/// </summary> /// </summary>
public enum LCIMMessagePriority { public enum LCIMMessagePriority {
Hight = 1, High = 1,
Normal = 2, Normal = 2,
Low = 3 Low = 3
} }

View File

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<ReleaseVersion>0.4.2</ReleaseVersion> <ReleaseVersion>0.4.2</ReleaseVersion>
<AssemblyName>Storage</AssemblyName>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -206,7 +206,7 @@ namespace LeanCloud.Storage.Internal.Http {
} }
// 当前用户 Session Token // 当前用户 Session Token
LCUser currentUser = await LCUser.GetCurrent(); LCUser currentUser = await LCUser.GetCurrent();
if (currentUser != null) { if (!headers.Contains("X-LC-Session") && currentUser != null) {
headers.Add("X-LC-Session", currentUser.SessionToken); headers.Add("X-LC-Session", currentUser.SessionToken);
} }
} }

View File

@ -212,14 +212,17 @@ namespace LeanCloud.Storage.Internal.Query {
if (conditionList != null && conditionList.Count > 0) { if (conditionList != null && conditionList.Count > 0) {
dict["where"] = JsonConvert.SerializeObject(Encode()); dict["where"] = JsonConvert.SerializeObject(Encode());
} }
if (orderByList != null && orderByList.Count > 0) { string order = BuildOrders();
dict["order"] = string.Join(",", orderByList); if (!string.IsNullOrEmpty(order)) {
dict["order"] = order;
} }
if (includes != null && includes.Count > 0) { string includes = BuildIncludes();
dict["include"] = string.Join(",", includes); if (!string.IsNullOrEmpty(includes)) {
dict["include"] = includes;
} }
if (selectedKeys != null && selectedKeys.Count > 0) { string keys = BuildKeys();
dict["keys"] = string.Join(",", selectedKeys); if (!string.IsNullOrEmpty(keys)) {
dict["keys"] = keys;
} }
if (IncludeACL) { if (IncludeACL) {
dict["returnACL"] = "true"; dict["returnACL"] = "true";
@ -233,5 +236,26 @@ namespace LeanCloud.Storage.Internal.Query {
} }
return JsonConvert.SerializeObject(Encode()); return JsonConvert.SerializeObject(Encode());
} }
public string BuildOrders() {
if (orderByList != null && orderByList.Count > 0) {
return string.Join(",", orderByList);
}
return null;
}
public string BuildIncludes() {
if (includes != null && includes.Count > 0) {
return string.Join(",", includes);
}
return null;
}
public string BuildKeys() {
if (selectedKeys != null && selectedKeys.Count > 0) {
return string.Join(",", selectedKeys);
}
return null;
}
} }
} }

View File

@ -9,7 +9,7 @@ namespace LeanCloud {
/// </summary> /// </summary>
public class LCApplication { public class LCApplication {
// SDK 版本号,用于 User-Agent 统计 // SDK 版本号,用于 User-Agent 统计
internal const string SDKVersion = "0.4.6"; internal const string SDKVersion = "0.5.0";
// 接口版本号,用于接口版本管理 // 接口版本号,用于接口版本管理
internal const string APIVersion = "1.1"; internal const string APIVersion = "1.1";

View File

@ -346,20 +346,12 @@ namespace LeanCloud.Storage {
return (int)ret["count"]; return (int)ret["count"];
} }
public async Task<T> Get(string objectId) { public Task<T> Get(string objectId) {
if (string.IsNullOrEmpty(objectId)) { if (string.IsNullOrEmpty(objectId)) {
throw new ArgumentNullException(nameof(objectId)); throw new ArgumentNullException(nameof(objectId));
} }
WhereEqualTo("objectId", objectId); WhereEqualTo("objectId", objectId);
Limit(1); return First();
ReadOnlyCollection<T> results = await Find();
if (results != null) {
if (results.Count == 0) {
return null;
}
return results[0];
}
return null;
} }
public async Task<ReadOnlyCollection<T>> Find() { public async Task<ReadOnlyCollection<T>> Find() {

View File

@ -430,7 +430,7 @@ namespace LeanCloud.Storage {
/// </summary> /// </summary>
/// <param name="email"></param> /// <param name="email"></param>
/// <returns></returns> /// <returns></returns>
public static async Task RequestPasswordRestBySmsCode(string mobile) { public static async Task RequestPasswordResetBySmsCode(string mobile) {
if (string.IsNullOrEmpty(mobile)) { if (string.IsNullOrEmpty(mobile)) {
throw new ArgumentNullException(nameof(mobile)); throw new ArgumentNullException(nameof(mobile));
} }