feat(db): add getLastMessageByConversation and update conversation list preview
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:
@@ -18,6 +18,7 @@ export interface IMessageRepository {
|
|||||||
clearConversation(conversationId: string): Promise<void>;
|
clearConversation(conversationId: string): Promise<void>;
|
||||||
search(keyword: string): Promise<CachedMessage[]>;
|
search(keyword: string): Promise<CachedMessage[]>;
|
||||||
getStats(): Promise<{ totalMessages: number; totalConversations: number }>;
|
getStats(): Promise<{ totalMessages: number; totalConversations: number }>;
|
||||||
|
getLastMessageByConversation(conversationId: string): Promise<CachedMessage | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MessageRepository implements IMessageRepository {
|
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`);
|
const convCount = await this.dataSource.getFirst<{ count: number }>(`SELECT COUNT(*) as count FROM conversations`);
|
||||||
return { totalMessages: msgCount?.count || 0, totalConversations: convCount?.count || 0 };
|
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();
|
export const messageRepository = new MessageRepository();
|
||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
MessageSegment,
|
MessageSegment,
|
||||||
} from '../../../types/dto';
|
} from '../../../types/dto';
|
||||||
import { authService } from '../../../services';
|
import { authService } from '../../../services';
|
||||||
import { userCacheRepository } from '@/database';
|
import { userCacheRepository, messageRepository } from '@/database';
|
||||||
import { Avatar, Text } from '../../../components/common';
|
import { Avatar, Text } from '../../../components/common';
|
||||||
|
|
||||||
const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
|
const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
|
||||||
@@ -34,7 +34,8 @@ const AsyncMessagePreview: React.FC<{
|
|||||||
status?: string;
|
status?: string;
|
||||||
isGroupChat?: boolean;
|
isGroupChat?: boolean;
|
||||||
senderName?: string;
|
senderName?: string;
|
||||||
}> = ({ segments, status, isGroupChat, senderName }) => {
|
conversationId?: string;
|
||||||
|
}> = ({ segments, status, isGroupChat, senderName, conversationId }) => {
|
||||||
const [displayText, setDisplayText] = useState<string>('');
|
const [displayText, setDisplayText] = useState<string>('');
|
||||||
const isMountedRef = useRef(true);
|
const isMountedRef = useRef(true);
|
||||||
|
|
||||||
@@ -49,19 +50,33 @@ const AsyncMessagePreview: React.FC<{
|
|||||||
return;
|
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) {
|
if (isMountedRef.current) {
|
||||||
setDisplayText(initialText);
|
setDisplayText(initialText);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasAtWithoutNickname = segments?.some(
|
const hasAtWithoutNickname = effectiveSegments?.some(
|
||||||
(s) => s.type === 'at' && s.data.user_id !== 'all' && !s.data.nickname
|
(s) => s.type === 'at' && s.data.user_id !== 'all' && !s.data.nickname
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasAtWithoutNickname) {
|
if (hasAtWithoutNickname) {
|
||||||
try {
|
try {
|
||||||
const asyncText = await extractTextFromSegmentsAsync(
|
const asyncText = await extractTextFromSegmentsAsync(
|
||||||
segments,
|
effectiveSegments,
|
||||||
userCacheRepository.get.bind(userCacheRepository),
|
userCacheRepository.get.bind(userCacheRepository),
|
||||||
authService.getUserById.bind(authService)
|
authService.getUserById.bind(authService)
|
||||||
);
|
);
|
||||||
@@ -79,7 +94,7 @@ const AsyncMessagePreview: React.FC<{
|
|||||||
return () => {
|
return () => {
|
||||||
isMountedRef.current = false;
|
isMountedRef.current = false;
|
||||||
};
|
};
|
||||||
}, [segments, status]);
|
}, [segments, status, conversationId]);
|
||||||
|
|
||||||
if (!displayText) return null;
|
if (!displayText) return null;
|
||||||
|
|
||||||
@@ -363,6 +378,7 @@ const ConversationListRowInner: React.FC<ConversationListRowProps> = ({
|
|||||||
status={item.last_message?.status}
|
status={item.last_message?.status}
|
||||||
isGroupChat={isGroupChat}
|
isGroupChat={isGroupChat}
|
||||||
senderName={getSenderName()}
|
senderName={getSenderName()}
|
||||||
|
conversationId={item.id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user