-
+
diff --git a/src/lib/chat/splash_latest_message.ts b/src/lib/chat/splash_latest_message.ts
new file mode 100644
index 00000000..db1d2e98
--- /dev/null
+++ b/src/lib/chat/splash_latest_message.ts
@@ -0,0 +1,16 @@
+"use client";
+
+import { getChatRepository } from "@/data/repositories/chat_repository";
+import { Result, type Result as ResultT } from "@/utils";
+
+import { getSplashLatestMessagePreview } from "./splash_latest_message_preview";
+
+export async function fetchSplashLatestMessagePreview(): Promise<
+ ResultT
+> {
+ const result = await getChatRepository().getHistory(1, 0);
+ if (Result.isErr(result)) return Result.err(result.error);
+
+ const latestMessage = result.data.messages[0] ?? null;
+ return Result.ok(getSplashLatestMessagePreview(latestMessage));
+}
diff --git a/src/lib/chat/splash_latest_message_preview.ts b/src/lib/chat/splash_latest_message_preview.ts
new file mode 100644
index 00000000..ca145bec
--- /dev/null
+++ b/src/lib/chat/splash_latest_message_preview.ts
@@ -0,0 +1,56 @@
+export interface SplashLatestMessageSource {
+ type?: string | null;
+ content?: string | null;
+ audioUrl?: string | null;
+ image?: {
+ type?: string | null;
+ url?: string | null;
+ } | null;
+ lockDetail?: {
+ reason?: string | null;
+ } | null;
+}
+
+const IMAGE_PREVIEW_TEXT = "[图片]";
+const VOICE_PREVIEW_TEXT = "[语音]";
+const FALLBACK_PREVIEW_TEXT = "[消息]";
+
+export function getSplashLatestMessagePreview(
+ message: SplashLatestMessageSource | null | undefined,
+): string | null {
+ if (!message) return null;
+
+ if (isVoiceMessage(message)) return VOICE_PREVIEW_TEXT;
+ if (isImageMessage(message)) return IMAGE_PREVIEW_TEXT;
+
+ const content = message.content?.trim() ?? "";
+ return content.length > 0 ? content : FALLBACK_PREVIEW_TEXT;
+}
+
+function isVoiceMessage(message: SplashLatestMessageSource): boolean {
+ const type = normalizeMessageType(message.type);
+ return (
+ type === "voice" ||
+ type === "audio" ||
+ hasValue(message.audioUrl) ||
+ message.lockDetail?.reason === "voice_message"
+ );
+}
+
+function isImageMessage(message: SplashLatestMessageSource): boolean {
+ const type = normalizeMessageType(message.type);
+ return (
+ type === "image" ||
+ hasValue(message.image?.url) ||
+ hasValue(message.image?.type) ||
+ message.lockDetail?.reason === "image"
+ );
+}
+
+function normalizeMessageType(value: string | null | undefined): string {
+ return value?.trim().toLowerCase() ?? "";
+}
+
+function hasValue(value: string | null | undefined): boolean {
+ return value != null && value.trim().length > 0;
+}