fix(chat): make cached media load on safari
This commit is contained in:
@@ -109,10 +109,13 @@ export class ChatMediaCacheCoordinator {
|
||||
);
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
if (blob.size <= 0) {
|
||||
throw new Error("Media download returned an empty blob.");
|
||||
const bytes = await response.arrayBuffer();
|
||||
if (bytes.byteLength <= 0) {
|
||||
throw new Error("Media download returned empty bytes.");
|
||||
}
|
||||
const mimeType =
|
||||
response.headers.get("content-type") ||
|
||||
fallbackChatMediaMimeType(input.kind);
|
||||
|
||||
const saveResult = await this.mediaStorage.saveMedia({
|
||||
cacheKey,
|
||||
@@ -120,11 +123,8 @@ export class ChatMediaCacheCoordinator {
|
||||
messageId: input.messageId,
|
||||
kind: input.kind,
|
||||
remoteUrl: input.remoteUrl,
|
||||
blob,
|
||||
mimeType:
|
||||
blob.type ||
|
||||
response.headers.get("content-type") ||
|
||||
fallbackChatMediaMimeType(input.kind),
|
||||
bytes,
|
||||
mimeType,
|
||||
});
|
||||
if (Result.isErr(saveResult)) throw saveResult.error;
|
||||
void requestBrowserPersistentStorageOnce();
|
||||
|
||||
@@ -69,12 +69,12 @@ export interface IChatRepository {
|
||||
/** 获取本地消息数量。 */
|
||||
getLocalMessageCount(): Promise<Result<number>>;
|
||||
|
||||
/** 获取本地缓存的图片 / 音频 Blob。 */
|
||||
/** 获取本地缓存的图片 / 音频。 */
|
||||
getCachedMedia(
|
||||
input: ChatMediaLookupInput,
|
||||
): Promise<Result<LocalChatMediaRow | null>>;
|
||||
|
||||
/** 下载并缓存远程图片 / 音频 Blob。 */
|
||||
/** 下载并缓存远程图片 / 音频。 */
|
||||
cacheRemoteMedia(
|
||||
input: CacheRemoteChatMediaInput,
|
||||
): Promise<Result<LocalChatMediaRow>>;
|
||||
|
||||
@@ -39,7 +39,10 @@ export interface LocalChatMediaRow {
|
||||
messageId: string;
|
||||
kind: ChatMediaKind;
|
||||
remoteUrl: string;
|
||||
blob: Blob;
|
||||
/** v3+ stores raw bytes instead of Blob to avoid iOS WebKit Blob URL issues. */
|
||||
bytes?: ArrayBuffer;
|
||||
/** Legacy v2 field. Read-only fallback for users who already have cached media. */
|
||||
blob?: Blob;
|
||||
mimeType: string;
|
||||
byteSize: number;
|
||||
createdAt: number;
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface SaveLocalChatMediaInput {
|
||||
messageId: string;
|
||||
kind: ChatMediaKind;
|
||||
remoteUrl: string;
|
||||
blob: Blob;
|
||||
bytes: ArrayBuffer;
|
||||
mimeType: string;
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ export class LocalChatMediaStorage {
|
||||
try {
|
||||
const row = (await this.db.media.get(cacheKey)) ?? null;
|
||||
if (row) {
|
||||
await this.db.media.update(cacheKey, {
|
||||
lastAccessedAt: Date.now(),
|
||||
});
|
||||
const normalizedRow = await this.normalizeLegacyMediaRow(row);
|
||||
await this.db.media.update(cacheKey, { lastAccessedAt: Date.now() });
|
||||
return Result.ok(normalizedRow);
|
||||
}
|
||||
return Result.ok(row);
|
||||
} catch (e) {
|
||||
@@ -64,9 +64,9 @@ export class LocalChatMediaStorage {
|
||||
messageId: input.messageId,
|
||||
kind: input.kind,
|
||||
remoteUrl: input.remoteUrl,
|
||||
blob: input.blob,
|
||||
bytes: input.bytes,
|
||||
mimeType: input.mimeType,
|
||||
byteSize: input.blob.size,
|
||||
byteSize: input.bytes.byteLength,
|
||||
createdAt: existing?.createdAt ?? now,
|
||||
updatedAt: now,
|
||||
lastAccessedAt: now,
|
||||
@@ -85,4 +85,35 @@ export class LocalChatMediaStorage {
|
||||
return Result.err(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async normalizeLegacyMediaRow(
|
||||
row: LocalChatMediaRow,
|
||||
): Promise<LocalChatMediaRow> {
|
||||
if (row.bytes && row.bytes.byteLength > 0) return row;
|
||||
if (!row.blob || row.blob.size <= 0) return row;
|
||||
|
||||
const bytes = await row.blob.arrayBuffer();
|
||||
const now = Date.now();
|
||||
const normalizedRow: LocalChatMediaRow = {
|
||||
cacheKey: row.cacheKey,
|
||||
ownerKey: row.ownerKey,
|
||||
messageId: row.messageId,
|
||||
kind: row.kind,
|
||||
remoteUrl: row.remoteUrl,
|
||||
bytes,
|
||||
mimeType:
|
||||
row.mimeType || row.blob.type || fallbackMediaMimeType(row.kind),
|
||||
byteSize: bytes.byteLength,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: now,
|
||||
lastAccessedAt: now,
|
||||
};
|
||||
|
||||
await this.db.media.put(normalizedRow);
|
||||
return normalizedRow;
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackMediaMimeType(kind: ChatMediaKind): string {
|
||||
return kind === "image" ? "image/*" : "audio/mpeg";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user