diff --git a/app/features/messages/components/conversations-list.tsx b/app/features/messages/components/conversations-list.tsx index f7299ae..d877234 100644 --- a/app/features/messages/components/conversations-list.tsx +++ b/app/features/messages/components/conversations-list.tsx @@ -5,7 +5,7 @@ import { IoChevronForward } from "react-icons/io5"; import { formatRelativeDate } from "~/features/core/helpers/date-formatter"; import PhoneInitLoader from "~/features/core/components/phone-init-loader"; import EmptyMessages from "./empty-messages"; -import type { MessagesLoaderData } from "~/routes/__app/messages"; +import type { MessagesLoaderData } from "~/features/messages/loaders/messages"; export default function ConversationsList() { const { conversations } = useLoaderData(); @@ -24,7 +24,7 @@ export default function ConversationsList() { {Object.values(conversations).map(({ recipient, formattedPhoneNumber, lastMessage }) => { return (
  • - +
    {formattedPhoneNumber ?? recipient}
    diff --git a/app/features/messages/loaders/messages.ts b/app/features/messages/loaders/messages.ts index 1be01cf..5b3d7d5 100644 --- a/app/features/messages/loaders/messages.ts +++ b/app/features/messages/loaders/messages.ts @@ -1,21 +1,20 @@ import type { LoaderFunction } from "@remix-run/node"; +import { json } from "superjson-remix"; +import { parsePhoneNumber } from "awesome-phonenumber"; import { type Message, Prisma, Direction, SubscriptionStatus } from "@prisma/client"; import db from "~/utils/db.server"; import { requireLoggedIn } from "~/utils/auth.server"; export type MessagesLoaderData = { - user: { - hasFilledTwilioCredentials: boolean; - hasPhoneNumber: boolean; - }; + user: { hasPhoneNumber: boolean }; conversations: Record | undefined; }; type Conversation = { recipient: string; formattedPhoneNumber: string; - messages: Message[]; + lastMessage: Message; }; const loader: LoaderFunction = async ({ request }) => { @@ -49,8 +48,65 @@ const loader: LoaderFunction = async ({ request }) => { }, }, }); - const organization = user!.memberships[0]!.organization; - // const hasFilledTwilioCredentials = Boolean(organization?.twilioAccountSid && organization?.twilioAuthToken); + const organization = user!.memberships[0].organization; + const phoneNumber = await db.phoneNumber.findUnique({ + where: { organizationId_isCurrent: { organizationId: organization.id, isCurrent: true } }, + select: { + id: true, + organizationId: true, + number: true, + }, + }); + const conversations = await getConversations(); + + return json({ + user: { hasPhoneNumber: Boolean(phoneNumber) }, + conversations, + }); + + async function getConversations() { + const organizationId = organizations[0].id; + const phoneNumber = await db.phoneNumber.findUnique({ + where: { organizationId_isCurrent: { organizationId, isCurrent: true } }, + }); + if (!phoneNumber || phoneNumber.isFetchingMessages) { + return; + } + + const messages = await db.message.findMany({ + where: { phoneNumberId: phoneNumber.id }, + orderBy: { sentAt: Prisma.SortOrder.desc }, + }); + + let conversations: Record = {}; + for (const message of messages) { + let recipient: string; + if (message.direction === Direction.Outbound) { + recipient = message.to; + } else { + recipient = message.from; + } + const formattedPhoneNumber = parsePhoneNumber(recipient).getNumber("international"); + + if (!conversations[recipient]) { + conversations[recipient] = { + recipient, + formattedPhoneNumber, + lastMessage: message, + }; + } + + if (message.sentAt > conversations[recipient].lastMessage.sentAt) { + conversations[recipient].lastMessage = message; + } + /*conversations[recipient]!.messages.push({ + ...message, + content: decrypt(message.content, organization.encryptionKey), + });*/ + } + + return conversations; + } }; export default loader; diff --git a/app/routes/__app/messages.$recipient.tsx b/app/routes/__app/messages.$recipient.tsx index e8e01b6..bb7a35b 100644 --- a/app/routes/__app/messages.$recipient.tsx +++ b/app/routes/__app/messages.$recipient.tsx @@ -81,13 +81,11 @@ export default function ConversationPage() { {conversation?.formattedPhoneNumber ?? recipient} - + - {/*Loading messages with {recipient}
    }>*/} - {/**/} ); } diff --git a/app/routes/__app/messages.tsx b/app/routes/__app/messages.tsx index 7f90d54..df672a2 100644 --- a/app/routes/__app/messages.tsx +++ b/app/routes/__app/messages.tsx @@ -1,118 +1,12 @@ -import { type LoaderFunction } from "@remix-run/node"; -import { json, useLoaderData } from "superjson-remix"; -import { type Message, Prisma, Direction, SubscriptionStatus } from "@prisma/client"; -import { parsePhoneNumber } from "awesome-phonenumber"; +import { useLoaderData } from "superjson-remix"; +import messagesLoader, { type MessagesLoaderData } from "~/features/messages/loaders/messages"; import PageTitle from "~/features/core/components/page-title"; import MissingTwilioCredentials from "~/features/core/components/missing-twilio-credentials"; import ConversationsList from "~/features/messages/components/conversations-list"; -import db from "~/utils/db.server"; -import { requireLoggedIn } from "~/utils/auth.server"; +import NewMessageButton from "~/features/messages/components/new-message-button"; -export type MessagesLoaderData = { - user: { - hasPhoneNumber: boolean; - }; - conversations: Record | undefined; -}; - -type Conversation = { - recipient: string; - formattedPhoneNumber: string; - lastMessage: Message; -}; - -export const loader: LoaderFunction = async ({ request }) => { - const { id, organizations } = await requireLoggedIn(request); - const user = await db.user.findFirst({ - where: { id }, - select: { - id: true, - fullName: true, - email: true, - role: true, - memberships: { - include: { - organization: { - include: { - subscriptions: { - where: { - OR: [ - { status: { not: SubscriptionStatus.deleted } }, - { - status: SubscriptionStatus.deleted, - cancellationEffectiveDate: { gt: new Date() }, - }, - ], - }, - orderBy: { lastEventTime: Prisma.SortOrder.desc }, - }, - }, - }, - }, - }, - }, - }); - const organization = user!.memberships[0].organization; - const phoneNumber = await db.phoneNumber.findUnique({ - where: { organizationId_isCurrent: { organizationId: organization.id, isCurrent: true } }, - select: { - id: true, - organizationId: true, - number: true, - }, - }); - const conversations = await getConversations(); - - return json({ - user: { hasPhoneNumber: Boolean(phoneNumber) }, - conversations, - }); - - async function getConversations() { - const organizationId = organizations[0].id; - const phoneNumber = await db.phoneNumber.findUnique({ - where: { organizationId_isCurrent: { organizationId, isCurrent: true } }, - }); - if (!phoneNumber || phoneNumber.isFetchingMessages) { - return; - } - - const messages = await db.message.findMany({ - where: { phoneNumberId: phoneNumber.id }, - orderBy: { sentAt: Prisma.SortOrder.desc }, - }); - - let conversations: Record = {}; - for (const message of messages) { - let recipient: string; - if (message.direction === Direction.Outbound) { - recipient = message.to; - } else { - recipient = message.from; - } - const formattedPhoneNumber = parsePhoneNumber(recipient).getNumber("international"); - - if (!conversations[recipient]) { - conversations[recipient] = { - recipient, - formattedPhoneNumber, - lastMessage: message, - }; - } - - if (message.sentAt > conversations[recipient].lastMessage.sentAt) { - conversations[recipient].lastMessage = message; - } - /*conversations[recipient]!.messages.push({ - ...message, - content: decrypt(message.content, organization.encryptionKey), - });*/ - } - - return conversations; - } -}; +export const loader = messagesLoader; export default function MessagesPage() { const { user } = useLoaderData(); @@ -130,7 +24,6 @@ export default function MessagesPage() { <>
    - {/* TODO: skeleton conversations list */}