feat(messaging): implement optimistic updates with retry for failed messages
- Add pending/failed message status to track send state - Implement optimistic UI: show message immediately, update on server response - Add retry functionality for failed messages via tap-to-retry - Change send button from icon to text "发送" for clarity - Add MessageSendService with temp ID generation to prevent collisions feat(notification): add JPush notification deduplication and clear on tap - Deduplicate notificationArrived events from dual JPush/vendor channels - Clear all notifications on tap (QQ-style behavior) feat(post): add client-side idempotency key for post creation - Generate client_request_id per publish intent to prevent duplicates on retry - Pass idempotency key through to postService and voteService
This commit is contained in:
@@ -228,7 +228,8 @@ export function useMessages(conversationId: string | null): UseMessagesReturn {
|
||||
// ==================== useSendMessage - 发送消息 ====================
|
||||
|
||||
interface UseSendMessageReturn {
|
||||
sendMessage: (segments: MessageSegment[], options?: { replyToId?: string }) => Promise<MessageResponse | null>;
|
||||
sendMessage: (segments: MessageSegment[], options?: { replyToId?: string; detailType?: 'private' | 'group' }) => Promise<MessageResponse | null>;
|
||||
retrySendMessage: (failedMessage: MessageResponse) => Promise<MessageResponse | null>;
|
||||
isSending: boolean;
|
||||
}
|
||||
|
||||
@@ -239,7 +240,7 @@ export function useSendMessage(conversationId: string | null): UseSendMessageRet
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
|
||||
const sendMessage = useCallback(
|
||||
async (segments: MessageSegment[], options?: { replyToId?: string }): Promise<MessageResponse | null> => {
|
||||
async (segments: MessageSegment[], options?: { replyToId?: string; detailType?: 'private' | 'group' }): Promise<MessageResponse | null> => {
|
||||
if (!conversationId) return null;
|
||||
|
||||
setIsSending(true);
|
||||
@@ -253,8 +254,24 @@ export function useSendMessage(conversationId: string | null): UseSendMessageRet
|
||||
[conversationId]
|
||||
);
|
||||
|
||||
const retrySendMessage = useCallback(
|
||||
async (failedMessage: MessageResponse): Promise<MessageResponse | null> => {
|
||||
if (!conversationId) return null;
|
||||
|
||||
setIsSending(true);
|
||||
try {
|
||||
const message = await messageManager.retrySendMessage(conversationId, failedMessage);
|
||||
return message;
|
||||
} finally {
|
||||
setIsSending(false);
|
||||
}
|
||||
},
|
||||
[conversationId]
|
||||
);
|
||||
|
||||
return {
|
||||
sendMessage,
|
||||
retrySendMessage,
|
||||
isSending,
|
||||
};
|
||||
}
|
||||
@@ -537,7 +554,8 @@ interface UseChatReturn {
|
||||
refreshMessages: () => Promise<void>;
|
||||
|
||||
// 发送消息
|
||||
sendMessage: (segments: MessageSegment[], options?: { replyToId?: string }) => Promise<MessageResponse | null>;
|
||||
sendMessage: (segments: MessageSegment[], options?: { replyToId?: string; detailType?: 'private' | 'group' }) => Promise<MessageResponse | null>;
|
||||
retrySendMessage: (failedMessage: MessageResponse) => Promise<MessageResponse | null>;
|
||||
isSending: boolean;
|
||||
|
||||
// 已读相关
|
||||
@@ -549,7 +567,7 @@ interface UseChatReturn {
|
||||
|
||||
export function useChat(conversationId: string | null): UseChatReturn {
|
||||
const { messages, isLoading: isLoadingMessages, hasMore: hasMoreMessages, loadMore, refresh } = useMessages(conversationId);
|
||||
const { sendMessage, isSending } = useSendMessage(conversationId);
|
||||
const { sendMessage, retrySendMessage, isSending } = useSendMessage(conversationId);
|
||||
const { markAsRead } = useMarkAsRead(conversationId);
|
||||
const { conversation } = useConversation(conversationId);
|
||||
|
||||
@@ -560,6 +578,7 @@ export function useChat(conversationId: string | null): UseChatReturn {
|
||||
loadMoreMessages: loadMore,
|
||||
refreshMessages: refresh,
|
||||
sendMessage,
|
||||
retrySendMessage,
|
||||
isSending,
|
||||
markAsRead,
|
||||
conversation,
|
||||
|
||||
Reference in New Issue
Block a user