feat(messaging): implement optimistic updates with retry for failed messages
All checks were successful
Frontend CI / ota (android) (push) Successful in 3m24s
Frontend CI / ota (ios) (push) Successful in 3m22s
Frontend CI / build-and-push-web (push) Successful in 21m12s
Frontend CI / build-android-apk (push) Successful in 41m40s

- 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:
lafay
2026-06-26 17:01:09 +08:00
parent 2bad59afbb
commit be77a9d04c
20 changed files with 451 additions and 134 deletions

View File

@@ -795,7 +795,7 @@ export const HomeScreen: React.FC = () => {
const authorId = item.author?.id || '';
const isPostAuthor = currentUser?.id === authorId;
return (
<View style={{ marginBottom: 2, paddingHorizontal: 1 }}>
<View style={{ marginBottom: 2, paddingHorizontal: responsiveGap / 2 }}>
<PostCard
post={item}
variant="grid"
@@ -804,7 +804,7 @@ export const HomeScreen: React.FC = () => {
/>
</View>
);
}, [currentUser?.id, stableOnPostAction]);
}, [currentUser?.id, stableOnPostAction, responsiveGap]);
// 渲染响应式网格布局(所有端统一使用 FlashList masonry 模式)
const renderResponsiveGrid = () => {
@@ -820,7 +820,7 @@ export const HomeScreen: React.FC = () => {
masonry
optimizeItemArrangement
contentContainerStyle={{
paddingHorizontal: responsivePadding,
paddingHorizontal: responsiveGap / 2,
paddingBottom: 80 + responsivePadding,
}}
showsVerticalScrollIndicator={false}