feat(db): add getLastMessageByConversation and update conversation list preview
All checks were successful
Frontend CI / ota-android (push) Successful in 1m16s
Frontend CI / build-and-push-web (push) Successful in 2m53s
Frontend CI / build-android-apk (push) Successful in 34m6s

Add `getLastMessageByConversation` to `MessageRepository` to allow fetching the most recent message for a specific conversation.

Update `ConversationListRow` to use this new repository method as a fallback when message segments are missing, ensuring the conversation list preview displays the last message content correctly.
This commit is contained in:
2026-05-05 02:01:05 +08:00
parent 8a271b2376
commit c0ebebe3da
2 changed files with 32 additions and 6 deletions

View File

@@ -18,6 +18,7 @@ export interface IMessageRepository {
clearConversation(conversationId: string): Promise<void>;
search(keyword: string): Promise<CachedMessage[]>;
getStats(): Promise<{ totalMessages: number; totalConversations: number }>;
getLastMessageByConversation(conversationId: string): Promise<CachedMessage | null>;
}
export class MessageRepository implements IMessageRepository {
@@ -147,6 +148,15 @@ export class MessageRepository implements IMessageRepository {
const convCount = await this.dataSource.getFirst<{ count: number }>(`SELECT COUNT(*) as count FROM conversations`);
return { totalMessages: msgCount?.count || 0, totalConversations: convCount?.count || 0 };
}
async getLastMessageByConversation(conversationId: string): Promise<CachedMessage | null> {
const r = await this.dataSource.getFirst<any>(
`SELECT * FROM messages WHERE conversationId = ? AND status != 'deleted' ORDER BY seq DESC LIMIT 1`,
[conversationId]
);
if (!r) return null;
return { ...r, isRead: r.isRead === 1, segments: r.segments ? JSON.parse(r.segments) : undefined };
}
}
export const messageRepository = new MessageRepository();

View File

@@ -14,7 +14,7 @@ import {
MessageSegment,
} from '../../../types/dto';
import { authService } from '../../../services';
import { userCacheRepository } from '@/database';
import { userCacheRepository, messageRepository } from '@/database';
import { Avatar, Text } from '../../../components/common';
const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
@@ -34,7 +34,8 @@ const AsyncMessagePreview: React.FC<{
status?: string;
isGroupChat?: boolean;
senderName?: string;
}> = ({ segments, status, isGroupChat, senderName }) => {
conversationId?: string;
}> = ({ segments, status, isGroupChat, senderName, conversationId }) => {
const [displayText, setDisplayText] = useState<string>('');
const isMountedRef = useRef(true);
@@ -49,19 +50,33 @@ const AsyncMessagePreview: React.FC<{
return;
}
const initialText = extractTextFromSegments(segments);
let effectiveSegments = segments;
// 当 segments 为空时,从本地 SQLite 回退查询
if ((!effectiveSegments || effectiveSegments.length === 0) && conversationId) {
try {
const lastMsg = await messageRepository.getLastMessageByConversation(conversationId);
if (lastMsg?.segments) {
effectiveSegments = Array.isArray(lastMsg.segments)
? lastMsg.segments
: JSON.parse(lastMsg.segments);
}
} catch {}
}
const initialText = extractTextFromSegments(effectiveSegments);
if (isMountedRef.current) {
setDisplayText(initialText);
}
const hasAtWithoutNickname = segments?.some(
const hasAtWithoutNickname = effectiveSegments?.some(
(s) => s.type === 'at' && s.data.user_id !== 'all' && !s.data.nickname
);
if (hasAtWithoutNickname) {
try {
const asyncText = await extractTextFromSegmentsAsync(
segments,
effectiveSegments,
userCacheRepository.get.bind(userCacheRepository),
authService.getUserById.bind(authService)
);
@@ -79,7 +94,7 @@ const AsyncMessagePreview: React.FC<{
return () => {
isMountedRef.current = false;
};
}, [segments, status]);
}, [segments, status, conversationId]);
if (!displayText) return null;
@@ -363,6 +378,7 @@ const ConversationListRowInner: React.FC<ConversationListRowProps> = ({
status={item.last_message?.status}
isGroupChat={isGroupChat}
senderName={getSenderName()}
conversationId={item.id}
/>
)}
</Text>