Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/modules/chat/services/ChatService.server.ts
T

149 lines
5.5 KiB
TypeScript

import { CHAT_PERMISSIONS, type ChatNotificationPreference } from "@hivegrid/chat";
import { listDiscoverablePublicGroupChats } from "@hivegrid/chat/discovery.server";
import { joinPublicGroupChat } from "@hivegrid/chat/lifecycle.server";
import { setChatMemberPreferences } from "@hivegrid/chat/member-preferences.server";
import { getMemberChannelDetail, listMemberChannels, sendTopLevelChatMessage } from "@hivegrid/chat/repository.server";
import type { ChatChannelDetailVM, ChatChannelListFilterState, ChatChannelListVM, ChatResolvedAuthorVM } from "@hivegrid/view-models";
import { tenantHasModule } from "~/utils/connector/tenantConnector.server";
import { requireChatPermission, type ChatRouteContext } from "../routes/chatAccess.server";
import { buildMemberChatChannelDetailVM, buildMemberChatChannelListVM, buildChatResolvedAuthors, type ChatViewModelLabels } from "./chatViewModel.server";
export async function loadMemberChatChannels(input: {
readonly context: ChatRouteContext;
readonly tenantSlug: string;
readonly filter: ChatChannelListFilterState;
readonly afterCursor?: string;
readonly limit?: number;
readonly labels: ChatViewModelLabels;
}): Promise<ChatChannelListVM> {
const context = requireChatPermission(input.context, CHAT_PERMISSIONS.readOwnChannels);
const [result, discover] = await Promise.all([
listMemberChannels({
tenantId: context.tenantId,
userId: context.userId,
permissions: context.permissions,
query: input.filter.query,
view: input.filter.view,
includeArchived: input.filter.showArchived,
afterCursor: input.afterCursor,
limit: input.limit,
}),
input.afterCursor
? Promise.resolve(undefined)
: listDiscoverablePublicGroupChats({
tenantId: context.tenantId,
userId: context.userId,
permissions: context.permissions,
query: input.filter.query,
}),
]);
return buildMemberChatChannelListVM({
channels: result.channels,
tenantSlug: input.tenantSlug,
currentUserId: context.userId,
permissions: context.permissions,
filter: input.filter,
nextCursor: result.nextCursor,
discoverChannels: discover?.channels,
labels: input.labels,
});
}
export async function joinPublicChatChannel(input: {
readonly context: ChatRouteContext;
readonly chatId: string;
readonly requestId: string;
}) {
const context = requireChatPermission(input.context, CHAT_PERMISSIONS.readOwnChannels);
return joinPublicGroupChat({
tenantId: context.tenantId,
userId: context.userId,
permissions: context.permissions,
chatId: input.chatId,
requestId: input.requestId,
});
}
export async function updateOwnChatChannelPreferences(input: {
readonly context: ChatRouteContext;
readonly chatId: string;
readonly requestId: string;
readonly isPinned?: boolean;
readonly notificationPreference?: ChatNotificationPreference;
}) {
const context = requireChatPermission(input.context, CHAT_PERMISSIONS.readOwnChannels);
return setChatMemberPreferences({
tenantId: context.tenantId,
userId: context.userId,
permissions: context.permissions,
chatId: input.chatId,
requestId: input.requestId,
...(input.isPinned === undefined ? {} : { isPinned: input.isPinned }),
...(input.notificationPreference === undefined ? {} : { notificationPreference: input.notificationPreference }),
});
}
export async function loadMemberChatChannel(input: {
readonly context: ChatRouteContext;
readonly tenantSlug: string;
readonly chatId: string;
readonly beforeMessageCursor?: string;
readonly focusMessageId?: string;
readonly labels: ChatViewModelLabels;
}): Promise<{
readonly vm: ChatChannelDetailVM;
readonly eventCursor: string;
readonly authors: readonly ChatResolvedAuthorVM[];
}> {
const context = requireChatPermission(input.context, CHAT_PERMISSIONS.readOwnChannels);
const [detail, filesEnabled, tasksEnabled] = await Promise.all([
getMemberChannelDetail({
tenantId: context.tenantId,
userId: context.userId,
permissions: context.permissions,
chatId: input.chatId,
...(input.beforeMessageCursor ? { beforeMessageCursor: input.beforeMessageCursor } : {}),
...(input.focusMessageId ? { focusMessageId: input.focusMessageId } : {}),
}),
tenantHasModule({ tenantId: context.tenantId, userId: context.userId, moduleKey: "files" }),
tenantHasModule({ tenantId: context.tenantId, userId: context.userId, moduleKey: "tasks" }),
]);
return {
vm: buildMemberChatChannelDetailVM({
detail,
tenantSlug: input.tenantSlug,
currentUserId: context.userId,
permissions: context.permissions,
nowIso: new Date().toISOString(),
featureAvailability: { files: filesEnabled, tasks: tasksEnabled },
labels: input.labels,
}),
eventCursor: detail.eventCursor,
authors: buildChatResolvedAuthors(detail.channel.participants, context.userId),
};
}
export async function sendMemberChatMessage(input: {
readonly context: ChatRouteContext;
readonly chatId: string;
readonly clientMessageId: string;
readonly requestId: string;
readonly body: string;
readonly attachmentFileIds: readonly string[];
}) {
const context = requireChatPermission(input.context, CHAT_PERMISSIONS.write);
return sendTopLevelChatMessage({
tenantId: context.tenantId,
userId: context.userId,
permissions: context.permissions,
chatId: input.chatId,
clientMessageId: input.clientMessageId,
requestId: input.requestId,
body: input.body,
attachmentFileIds: input.attachmentFileIds,
});
}