feat(chat): 添加聊天界面视图模型和联系人管理功能

- 实现 ChatViewModel 处理消息收发、历史记录加载和状态管理
- 添加消息搜索、草稿保存、引用回复等功能
- 实现多媒体附件发送包括图片、视频、音频和文件
- 添加群组提及用户功能和消息撤回机制
- 实现联系人管理功能包括好友搜索、添加、删除和黑名单管理
- 添加好友请求处理和实时消息监听
- 实现会话列表管理包含未读消息统计和实时更新
- 集成 IM SDK 的连接状态管理和事件监听
- 添加消息状态跟踪和超时处理机制
- 实现数据缓存机制优化用户体验
这个提交包含在:
XuqmGroup 2026-04-28 22:32:21 +08:00
父节点 73060518f0
当前提交 30a9f71eac

查看文件

@ -321,18 +321,51 @@ public final class XuqmImServerSdk {
} }
public ImMessage parseMessageCallbackPayload(String body) { public ImMessage parseMessageCallbackPayload(String body) {
WebhookCallbackEnvelope envelope = parseCallbackEnvelope(body); return parseMessageCallbackPayload(parseCallbackEnvelope(body));
}
public ImMessage parseMessageCallbackPayload(WebhookCallbackEnvelope envelope) {
if (!envelope.isMessageEvent() if (!envelope.isMessageEvent()
|| !(envelope.isEvent("message.sent") || !(envelope.isMessageSentEvent()
|| envelope.isEvent("message.edited") || envelope.isMessageEditedEvent()
|| envelope.isEvent("message.revoked"))) { || envelope.isMessageRevokedEvent())) {
throw new ImSdkException("Callback event is not a message event"); throw new ImSdkException("Callback event is not a message event");
} }
return readPayload(envelope, ImMessage.class); return readPayload(envelope, ImMessage.class);
} }
public ImMessage parseMessageSentCallbackPayload(String body) {
return parseMessageSentCallbackPayload(parseCallbackEnvelope(body));
}
public ImMessage parseMessageSentCallbackPayload(WebhookCallbackEnvelope envelope) {
requireMessageEvent(envelope, "message.sent");
return readPayload(envelope, ImMessage.class);
}
public ImMessage parseMessageEditedCallbackPayload(String body) {
return parseMessageEditedCallbackPayload(parseCallbackEnvelope(body));
}
public ImMessage parseMessageEditedCallbackPayload(WebhookCallbackEnvelope envelope) {
requireMessageEvent(envelope, "message.edited");
return readPayload(envelope, ImMessage.class);
}
public ImMessage parseMessageRevokedCallbackPayload(String body) {
return parseMessageRevokedCallbackPayload(parseCallbackEnvelope(body));
}
public ImMessage parseMessageRevokedCallbackPayload(WebhookCallbackEnvelope envelope) {
requireMessageEvent(envelope, "message.revoked");
return readPayload(envelope, ImMessage.class);
}
public MessageReadCallbackPayload parseMessageReadCallbackPayload(String body) { public MessageReadCallbackPayload parseMessageReadCallbackPayload(String body) {
WebhookCallbackEnvelope envelope = parseCallbackEnvelope(body); return parseMessageReadCallbackPayload(parseCallbackEnvelope(body));
}
public MessageReadCallbackPayload parseMessageReadCallbackPayload(WebhookCallbackEnvelope envelope) {
if (!envelope.isReadReceiptEvent()) { if (!envelope.isReadReceiptEvent()) {
throw new ImSdkException("Callback event is not a message.read event"); throw new ImSdkException("Callback event is not a message.read event");
} }
@ -1240,6 +1273,12 @@ public final class XuqmImServerSdk {
} }
} }
private void requireMessageEvent(WebhookCallbackEnvelope envelope, String event) {
if (!envelope.isMessageEvent() || !envelope.isEvent(event)) {
throw new ImSdkException("Callback event is not a " + event + " event");
}
}
private static String text(JsonNode node, String field) { private static String text(JsonNode node, String field) {
JsonNode value = node == null ? null : node.get(field); JsonNode value = node == null ? null : node.get(field);
return value == null || value.isNull() ? null : value.asText(); return value == null || value.isNull() ? null : value.asText();
@ -1718,6 +1757,18 @@ public final class XuqmImServerSdk {
return isType("message"); return isType("message");
} }
public boolean isMessageSentEvent() {
return isEvent("message.sent");
}
public boolean isMessageEditedEvent() {
return isEvent("message.edited");
}
public boolean isMessageRevokedEvent() {
return isEvent("message.revoked");
}
public boolean isReadReceiptEvent() { public boolean isReadReceiptEvent() {
return isEvent("message.read"); return isEvent("message.read");
} }