add recipient field to messages and phone calls

This commit is contained in:
m5r 2022-05-22 01:17:43 +02:00
parent 6684dcc0e5
commit 1f37eb45d5
6 changed files with 78 additions and 58 deletions

View File

@ -1,14 +1,14 @@
import type { LoaderFunction } from "@remix-run/node";
import { json } from "superjson-remix";
import { parsePhoneNumber } from "awesome-phonenumber";
import { type Message, Prisma, Direction } from "@prisma/client";
import { type Message, Prisma } from "@prisma/client";
import db from "~/utils/db.server";
import { requireLoggedIn, type SessionData } from "~/utils/auth.server";
export type MessagesLoaderData = {
user: { hasPhoneNumber: boolean };
conversations: Record<string, Conversation> | undefined;
conversations: Conversations | undefined;
};
type Conversation = {
@ -18,23 +18,23 @@ type Conversation = {
};
const loader: LoaderFunction = async ({ request }) => {
const sessionData = await requireLoggedIn(request);
const { phoneNumber } = await requireLoggedIn(request);
return json<MessagesLoaderData>({
user: { hasPhoneNumber: Boolean(sessionData.phoneNumber) },
conversations: await getConversations(sessionData.phoneNumber),
user: { hasPhoneNumber: Boolean(phoneNumber) },
conversations: await getConversations(phoneNumber),
});
};
export default loader;
type Conversations = Record<string, Conversation>;
async function getConversations(sessionPhoneNumber: SessionData["phoneNumber"]) {
if (!sessionPhoneNumber) {
return;
}
const phoneNumber = await db.phoneNumber.findUnique({
where: { id: sessionPhoneNumber.id },
});
const phoneNumber = await db.phoneNumber.findUnique({ where: { id: sessionPhoneNumber.id } });
if (!phoneNumber || phoneNumber.isFetchingMessages) {
return;
}
@ -42,34 +42,22 @@ async function getConversations(sessionPhoneNumber: SessionData["phoneNumber"])
const messages = await db.message.findMany({
where: { phoneNumberId: phoneNumber.id },
orderBy: { sentAt: Prisma.SortOrder.desc },
distinct: "recipient",
});
let conversations: Record<string, Conversation> = {};
for (const message of messages) {
let recipient: string;
if (message.direction === Direction.Outbound) {
recipient = message.to;
} else {
recipient = message.from;
}
return messages.reduce<Conversations>((conversations, message) => {
const recipient = message.recipient;
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;
}, {});
}

View File

@ -4,7 +4,8 @@ import { z } from "zod";
import db from "~/utils/db.server";
import { type FormError, validate } from "~/utils/validation.server";
import { requireLoggedIn } from "~/utils/auth.server";
import { refreshSessionData, requireLoggedIn } from "~/utils/auth.server";
import { commitSession } from "~/utils/session.server";
import setTwilioWebhooksQueue from "~/queues/set-twilio-webhooks.server";
type SetPhoneNumberFailureActionData = { errors: FormError<typeof bodySchema>; submitted?: never };
@ -39,9 +40,16 @@ const action: ActionFunction = async ({ request }) => {
phoneNumberId: validation.data.phoneNumberSid,
organizationId: organization.id,
});
console.log("queued");
const { session } = await refreshSessionData(request);
return json<SetPhoneNumberActionData>({ submitted: true });
return json<SetPhoneNumberActionData>(
{ submitted: true },
{
headers: {
"Set-Cookie": await commitSession(session),
},
},
);
};
export default action;

View File

@ -1,5 +1,5 @@
import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message";
import type { Message } from "@prisma/client";
import { type Message, Direction } from "@prisma/client";
import { Queue } from "~/utils/queue.server";
import db from "~/utils/db.server";
@ -22,20 +22,25 @@ export default Queue<Payload>("insert messages", async ({ data }) => {
}
const sms = messages
.map<Message>((message) => ({
.map<Message>((message) => {
const status = translateMessageStatus(message.status);
const direction = translateMessageDirection(message.direction);
return {
id: message.sid,
phoneNumberId: phoneNumber.id,
content: message.body,
recipient: direction === Direction.Outbound ? message.to : message.from,
from: message.from,
to: message.to,
status: translateMessageStatus(message.status),
direction: translateMessageDirection(message.direction),
status,
direction,
sentAt: new Date(message.dateCreated),
}))
};
})
.sort((a, b) => a.sentAt.getTime() - b.sentAt.getTime());
const { count } = await db.message.createMany({ data: sms, skipDuplicates: true });
logger.info(`inserted ${count} new messages for phoneNumberId=${phoneNumberId}`)
logger.info(`inserted ${count} new messages for phoneNumberId=${phoneNumberId}`);
if (!phoneNumber.isFetchingMessages) {
return;

View File

@ -1,5 +1,5 @@
import type { CallInstance } from "twilio/lib/rest/api/v2010/account/call";
import type { PhoneCall } from "@prisma/client";
import { type PhoneCall, Direction } from "@prisma/client";
import { Queue } from "~/utils/queue.server";
import db from "~/utils/db.server";
@ -22,16 +22,21 @@ export default Queue<Payload>("insert phone calls", async ({ data }) => {
}
const phoneCalls = calls
.map<PhoneCall>((call) => ({
.map<PhoneCall>((call) => {
const direction = translateCallDirection(call.direction);
const status = translateCallStatus(call.status);
return {
phoneNumberId,
id: call.sid,
recipient: direction === Direction.Outbound ? call.to : call.from,
from: call.from,
to: call.to,
direction: translateCallDirection(call.direction),
status: translateCallStatus(call.status),
direction,
status,
duration: call.duration,
createdAt: new Date(call.dateCreated),
}))
};
})
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
const ddd = await db.phoneCall.createMany({ data: phoneCalls, skipDuplicates: true });

View File

@ -118,6 +118,7 @@ CREATE TABLE "Message" (
"id" TEXT NOT NULL,
"sentAt" TIMESTAMPTZ NOT NULL,
"content" TEXT NOT NULL,
"recipient" TEXT NOT NULL,
"from" TEXT NOT NULL,
"to" TEXT NOT NULL,
"direction" "Direction" NOT NULL,
@ -131,6 +132,7 @@ CREATE TABLE "Message" (
CREATE TABLE "PhoneCall" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"recipient" TEXT NOT NULL,
"from" TEXT NOT NULL,
"to" TEXT NOT NULL,
"status" "CallStatus" NOT NULL,
@ -172,11 +174,17 @@ CREATE UNIQUE INDEX "Token_membershipId_key" ON "Token"("membershipId");
-- CreateIndex
CREATE UNIQUE INDEX "Token_hashedToken_type_key" ON "Token"("hashedToken", "type");
-- CreateIndex
CREATE INDEX "Message_phoneNumberId_recipient_idx" ON "Message"("phoneNumberId", "recipient");
-- CreateIndex
CREATE INDEX "PhoneCall_phoneNumberId_recipient_idx" ON "PhoneCall"("phoneNumberId", "recipient");
-- CreateIndex
CREATE UNIQUE INDEX "PhoneNumber_organizationId_isCurrent_key" ON "PhoneNumber"("organizationId", "isCurrent") WHERE ("isCurrent" = true);
-- AddForeignKey
ALTER TABLE "TwilioAccount" ADD CONSTRAINT "TwilioAccount_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "TwilioAccount" ADD CONSTRAINT "TwilioAccount_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey

View File

@ -17,7 +17,7 @@ model TwilioAccount {
twimlAppSid String?
organizationId String @unique
organization Organization @relation(fields: [organizationId], references: [id])
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
}
model Organization {
@ -105,17 +105,21 @@ model Message {
id String @id
sentAt DateTime @db.Timestamptz(6)
content String
recipient String
from String
to String
direction Direction
status MessageStatus
phoneNumberId String
phoneNumber PhoneNumber @relation(fields: [phoneNumberId], references: [id], onDelete: Cascade)
@@index([phoneNumberId, recipient])
}
model PhoneCall {
id String @id
createdAt DateTime @default(now()) @db.Timestamptz(6)
recipient String
from String
to String
status CallStatus
@ -123,6 +127,8 @@ model PhoneCall {
duration String
phoneNumberId String
phoneNumber PhoneNumber @relation(fields: [phoneNumberId], references: [id], onDelete: Cascade)
@@index([phoneNumberId, recipient])
}
model PhoneNumber {