Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/modules/chat/client/chatAttachmentOptimistic.ts
T

94 lines
3.1 KiB
TypeScript

import { chatMessageIdentityKey, groupChatMessagesByDay, type ChatChannelDetailVM, type ChatMessageVM, type ChatResolvedAuthorVM } from "@hivegrid/view-models";
import type { ChatPendingAttachment } from "./useChatAttachmentUploads";
export type ChatAttachmentSendAttempt = {
readonly payloadKey: string;
readonly requestId: string;
readonly clientMessageId: string;
readonly fileIds: readonly string[];
readonly body: string;
readonly attachments: readonly ChatPendingAttachment[];
readonly createdAtIso: string;
readonly deliveryState: "sending" | "failed";
readonly errorKey: string | null;
};
export function overlayChatAttachmentSendAttempt(
vm: ChatChannelDetailVM,
attempt: ChatAttachmentSendAttempt | null,
currentUser: ChatResolvedAuthorVM
): ChatChannelDetailVM {
if (vm.status !== "ready" || attempt === null) return vm;
const recipientCount = Math.max(0, vm.data.header.memberCount - 1);
const localMessage: ChatMessageVM = {
id: `local:${attempt.clientMessageId}`,
clientMessageId: attempt.clientMessageId,
source: "local_echo",
deliveryState: attempt.deliveryState,
deliveryStateKey: attempt.deliveryState === "sending" ? "chat.message.delivery.sending" : "chat.message.delivery.failed",
failureReasonKey: attempt.errorKey,
retryable: attempt.deliveryState === "failed",
author: currentUser,
bodyMarkdown: attempt.body || null,
bodyFormat: "markdown",
systemEvent: null,
createdAtIso: attempt.createdAtIso,
updatedAtIso: attempt.createdAtIso,
editedAtIso: null,
deletedAtIso: null,
tombstoneKey: null,
parentMessageId: null,
mentions: [],
reactions: [],
thread: null,
attachments: attempt.attachments.flatMap((attachment) =>
attachment.fileId
? [
{
id: `${attempt.clientMessageId}:${attachment.fileId}`,
fileId: attachment.fileId,
kind: attachment.kind,
fileName: attachment.fileName,
mimeType: attachment.mimeType,
sizeBytes: attachment.sizeBytes,
downloadHref: "#pending-chat-attachment",
previewHref: null,
altText: attachment.kind === "image" ? attachment.fileName : null,
durationSeconds: attachment.durationSeconds,
scanStatus: "clean" as const,
},
]
: []
),
linkPreviews: [],
readAggregate: {
readCount: 0,
recipientCount,
readerUserIds: [],
readerLabels: [],
labelKey: "chat.message.read.aggregate",
},
pin: null,
actions: {
canReact: false,
canReply: false,
canEdit: false,
canDelete: false,
canPin: false,
canMarkAsTask: false,
},
};
const byKey = new Map(vm.data.messages.map((message) => [chatMessageIdentityKey(message), message]));
byKey.set(chatMessageIdentityKey(localMessage), localMessage);
const messages = [...byKey.values()].sort((left, right) => left.createdAtIso.localeCompare(right.createdAtIso));
return {
...vm,
data: {
...vm.data,
messages,
messageGroups: groupChatMessagesByDay(messages),
},
};
}