store twilio stuff in TwilioAccount table and remodel session data

This commit is contained in:
m5r
2022-05-21 21:33:23 +02:00
parent 19a35bac92
commit 6684dcc0e5
23 changed files with 411 additions and 365 deletions

View File

@ -8,19 +8,14 @@ import getTwilioClient, { translateMessageDirection, translateMessageStatus } fr
export type NewMessageActionData = {};
const action: ActionFunction = async ({ params, request }) => {
const user = await requireLoggedIn(request);
const organization = user.organizations[0];
const phoneNumber = await db.phoneNumber.findUnique({
where: { organizationId_isCurrent: { organizationId: user.organizations[0].id, isCurrent: true } },
});
const { phoneNumber, twilioAccount } = await requireLoggedIn(request);
if (!twilioAccount) {
throw new Error("unreachable");
}
const recipient = decodeURIComponent(params.recipient ?? "");
const formData = Object.fromEntries(await request.formData());
const { twilioAccountSid, twilioSubAccountSid } = organization;
// const twilioClient = getTwilioClient({ twilioSubAccountSid, twilioAccountSid });
const twilioClient = getTwilioClient({ twilioSubAccountSid: twilioAccountSid, twilioAccountSid });
const twilioClient = getTwilioClient(twilioAccount);
try {
console.log({ twilioAccountSid, twilioSubAccountSid });
console.log({
body: formData.content.toString(),
to: recipient,

View File

@ -10,7 +10,7 @@ import { type ConversationLoaderData } from "~/routes/__app/messages.$recipient"
import useSession from "~/features/core/hooks/use-session";
export default function Conversation() {
const { currentPhoneNumber } = useSession();
const { phoneNumber } = useSession();
const params = useParams<{ recipient: string }>();
const recipient = decodeURIComponent(params.recipient ?? "");
const { conversation } = useLoaderData<ConversationLoaderData>();
@ -21,15 +21,15 @@ export default function Conversation() {
if (transition.submission) {
messages.push({
id: "temp",
phoneNumberId: currentPhoneNumber.id,
from: currentPhoneNumber.number,
phoneNumberId: phoneNumber!.id,
from: phoneNumber!.number,
to: recipient,
sentAt: new Date(),
direction: Direction.Outbound,
status: "Queued",
content: transition.submission.formData.get("content")!.toString()
})
content: transition.submission.formData.get("content")!.toString(),
});
}
useEffect(() => {
@ -91,7 +91,7 @@ export default function Conversation() {
})}
</ul>
</div>
<NewMessageArea />
<NewMessageArea recipient={recipient} />
</>
);
}

View File

@ -20,8 +20,8 @@ export default function NewMessageBottomSheet() {
onClose={() => setIsOpen(false)}
snapPoints={[0.5]}
>
<BottomSheet.Container>
<BottomSheet.Header>
<BottomSheet.Container onViewportBoxUpdate={null}>
<BottomSheet.Header onViewportBoxUpdate={null}>
<div className="w-full flex items-center justify-center p-4 text-black relative">
<span className="font-semibold text-base">New Message</span>
@ -30,7 +30,7 @@ export default function NewMessageBottomSheet() {
</button>
</div>
</BottomSheet.Header>
<BottomSheet.Content>
<BottomSheet.Content onViewportBoxUpdate={null}>
<main className="flex flex-col h-full overflow-hidden">
<div className="flex items-center p-4 border-t border-b">
<span className="mr-4 text-[#333]">To:</span>
@ -48,7 +48,7 @@ export default function NewMessageBottomSheet() {
</BottomSheet.Content>
</BottomSheet.Container>
<BottomSheet.Backdrop onTap={() => setIsOpen(false)} />
<BottomSheet.Backdrop onViewportBoxUpdate={null} onTap={() => setIsOpen(false)} />
</BottomSheet>
);
}

View File

@ -1,10 +1,10 @@
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 { type Message, Prisma, Direction } from "@prisma/client";
import db from "~/utils/db.server";
import { requireLoggedIn } from "~/utils/auth.server";
import { requireLoggedIn, type SessionData } from "~/utils/auth.server";
export type MessagesLoaderData = {
user: { hasPhoneNumber: boolean };
@ -18,95 +18,58 @@ type Conversation = {
};
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();
const sessionData = await requireLoggedIn(request);
return json<MessagesLoaderData>({
user: { hasPhoneNumber: Boolean(phoneNumber) },
conversations,
user: { hasPhoneNumber: Boolean(sessionData.phoneNumber) },
conversations: await getConversations(sessionData.phoneNumber),
});
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<string, Conversation> = {};
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;
async function getConversations(sessionPhoneNumber: SessionData["phoneNumber"]) {
if (!sessionPhoneNumber) {
return;
}
const phoneNumber = await db.phoneNumber.findUnique({
where: { id: sessionPhoneNumber.id },
});
if (!phoneNumber || phoneNumber.isFetchingMessages) {
return;
}
const messages = await db.message.findMany({
where: { phoneNumberId: phoneNumber.id },
orderBy: { sentAt: Prisma.SortOrder.desc },
});
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;
}
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;
}