From a262f6182377cc4e3b9ce76fea002909c9098fab Mon Sep 17 00:00:00 2001 From: m5r Date: Thu, 22 Jul 2021 00:48:49 +0800 Subject: [PATCH] list phone calls --- src/database/_types.ts | 16 ------ src/database/{sms.ts => message.ts} | 43 +++++++++------ src/database/phone-call.ts | 53 +++++++++++++++++++ src/hooks/use-conversation.ts | 22 ++++---- .../api/conversation/[recipient]/index.ts | 2 +- .../conversation/[recipient]/send-message.ts | 9 ++-- src/pages/api/ddd.ts | 11 ++-- src/pages/api/queue/fetch-calls.ts | 40 ++++++++++++++ src/pages/api/queue/fetch-messages.ts | 14 ++--- src/pages/api/queue/insert-calls.ts | 32 +++++++++++ src/pages/api/queue/insert-messages.ts | 29 +++++----- src/pages/api/queue/send-message.ts | 2 +- src/pages/api/queue/set-twilio-webhooks.ts | 4 +- src/pages/api/user/add-phone-number.ts | 2 + .../{incoming-sms.ts => incoming-message.ts} | 16 +++--- src/pages/calls.tsx | 24 +++++++-- src/pages/messages/[recipient].tsx | 33 ++++++++---- src/pages/messages/index.tsx | 21 ++++---- 18 files changed, 268 insertions(+), 105 deletions(-) delete mode 100644 src/database/_types.ts rename src/database/{sms.ts => message.ts} (50%) create mode 100644 src/database/phone-call.ts create mode 100644 src/pages/api/queue/fetch-calls.ts create mode 100644 src/pages/api/queue/insert-calls.ts rename src/pages/api/webhook/{incoming-sms.ts => incoming-message.ts} (78%) diff --git a/src/database/_types.ts b/src/database/_types.ts deleted file mode 100644 index 5a3ee66..0000000 --- a/src/database/_types.ts +++ /dev/null @@ -1,16 +0,0 @@ -export enum SmsType { - SENT = "sent", - RECEIVED = "received", -} - -export type Sms = { - id: string; - customerId: string; - content: string; - from: string; - to: string; - type: SmsType; - twilioSid?: string; - // status: sent/delivered/received - sentAt: string; // timestampz -}; diff --git a/src/database/sms.ts b/src/database/message.ts similarity index 50% rename from src/database/sms.ts rename to src/database/message.ts index 90da669..b3163e7 100644 --- a/src/database/sms.ts +++ b/src/database/message.ts @@ -1,31 +1,44 @@ +import { MessageStatus } from "twilio/lib/rest/api/v2010/account/message"; + import appLogger from "../../lib/logger"; import supabase from "../supabase/server"; -import type { Sms } from "./_types"; import { findCustomer } from "./customer"; import { decrypt } from "./_encryption"; -const logger = appLogger.child({ module: "sms" }); +const logger = appLogger.child({ module: "message" }); -export async function insertSms(messages: Omit): Promise { +export type Message = { + id: string; + customerId: string; + content: string; + from: string; + to: string; + direction: "inbound" | "outbound"; + status: MessageStatus; + twilioSid?: string; + sentAt: string; // timestampz +}; + +export async function insertMessage(message: Omit): Promise { const { error, data } = await supabase - .from("sms") - .insert(messages); + .from("message") + .insert(message); if (error) throw error; return data![0]; } -export async function insertManySms(messages: Omit[]) { +export async function insertManyMessage(messages: Omit[]) { await supabase - .from("sms") + .from("message") .insert(messages) .throwOnError(); } -export async function findCustomerMessages(customerId: Sms["customerId"]): Promise { +export async function findCustomerMessages(customerId: Message["customerId"]): Promise { const { error, data } = await supabase - .from("sms") + .from("message") .select("*") .eq("customerId", customerId); @@ -34,9 +47,9 @@ export async function findCustomerMessages(customerId: Sms["customerId"]): Promi return data!; } -export async function findCustomerMessageBySid({ customerId, twilioSid }: Pick): Promise { +export async function findCustomerMessageBySid({ customerId, twilioSid }: Pick): Promise { const { error, data } = await supabase - .from("sms") + .from("message") .select("*") .eq("customerId", customerId) .eq("twilioSid", twilioSid) @@ -47,17 +60,17 @@ export async function findCustomerMessageBySid({ customerId, twilioSid }: Pick) { - await supabase.from("sms") +export async function setTwilioSid({ id, twilioSid }: Pick) { + await supabase.from("message") .update({ twilioSid }) .eq("id", id) .throwOnError(); } -export async function findConversation(customerId: Sms["customerId"], recipient: Sms["to"]): Promise { +export async function findConversation(customerId: Message["customerId"], recipient: Message["to"]): Promise { const customer = await findCustomer(customerId); const { error, data } = await supabase - .from("sms") + .from("message") .select("*") .eq("customerId", customerId) .or(`to.eq.${recipient},from.eq.${recipient}`); diff --git a/src/database/phone-call.ts b/src/database/phone-call.ts new file mode 100644 index 0000000..f7aee63 --- /dev/null +++ b/src/database/phone-call.ts @@ -0,0 +1,53 @@ +import type { CallStatus } from "twilio/lib/rest/api/v2010/account/call"; + +import appLogger from "../../lib/logger"; +import supabase from "../supabase/server"; + +const logger = appLogger.child({ module: "phone-call" }); + +export type PhoneCall = { + id: string; + customerId: string; + twilioSid: string; + from: string; + to: string; + status: CallStatus; + direction: "inbound" | "outbound"; + duration: string; + createdAt: string; // timestampz +} + +export async function insertPhoneCall(phoneCall: Omit): Promise { + const { error, data } = await supabase + .from("phone-call") + .insert(phoneCall); + + if (error) throw error; + + return data![0]; +} + +export async function insertManyPhoneCalls(phoneCalls: Omit[]) { + await supabase + .from("phone-call") + .insert(phoneCalls) + .throwOnError(); +} + +export async function findCustomerPhoneCalls(customerId: PhoneCall["customerId"]): Promise { + const { error, data } = await supabase + .from("phone-call") + .select("*") + .eq("customerId", customerId); + + if (error) throw error; + + return data!; +} + +export async function setTwilioSid({ id, twilioSid }: Pick) { + await supabase.from("phone-call") + .update({ twilioSid }) + .eq("id", id) + .throwOnError(); +} diff --git a/src/hooks/use-conversation.ts b/src/hooks/use-conversation.ts index 851d53f..9d1994c 100644 --- a/src/hooks/use-conversation.ts +++ b/src/hooks/use-conversation.ts @@ -1,11 +1,10 @@ import { useMutation, useQuery, useQueryClient } from "react-query"; import axios from "axios"; -import type { Sms } from "../database/_types"; -import { SmsType } from "../database/_types"; +import type { Message } from "../database/message"; import useUser from "./use-user"; type UseConversationParams = { - initialData?: Sms[]; + initialData?: Message[]; recipient: string; } @@ -16,11 +15,11 @@ export default function useConversation({ const user = useUser(); const getConversationUrl = `/api/conversation/${encodeURIComponent(recipient)}`; const fetcher = async () => { - const { data } = await axios.get(getConversationUrl); + const { data } = await axios.get(getConversationUrl); return data; }; const queryClient = useQueryClient(); - const getConversationQuery = useQuery( + const getConversationQuery = useQuery( getConversationUrl, fetcher, { @@ -31,21 +30,22 @@ export default function useConversation({ ); const sendMessage = useMutation( - (sms: Pick) => axios.post(`/api/conversation/${sms.to}/send-message`, sms, { withCredentials: true }), + (sms: Pick) => axios.post(`/api/conversation/${sms.to}/send-message`, sms, { withCredentials: true }), { - onMutate: async (sms: Pick) => { + onMutate: async (sms: Pick) => { await queryClient.cancelQueries(getConversationUrl); - const previousMessages = queryClient.getQueryData(getConversationUrl); + const previousMessages = queryClient.getQueryData(getConversationUrl); if (previousMessages) { - queryClient.setQueryData(getConversationUrl, [ + queryClient.setQueryData(getConversationUrl, [ ...previousMessages, { id: "", // TODO: somehow generate an id from: "", // TODO: get user's phone number customerId: user.userProfile!.id, sentAt: new Date().toISOString(), - type: SmsType.SENT, + direction: "outbound", + status: "queued", content: sms.content, to: sms.to, }, @@ -56,7 +56,7 @@ export default function useConversation({ }, onError: (error, variables, context) => { if (context?.previousMessages) { - queryClient.setQueryData(getConversationUrl, context.previousMessages); + queryClient.setQueryData(getConversationUrl, context.previousMessages); } }, onSettled: () => queryClient.invalidateQueries(getConversationUrl), diff --git a/src/pages/api/conversation/[recipient]/index.ts b/src/pages/api/conversation/[recipient]/index.ts index 695347a..99f1a01 100644 --- a/src/pages/api/conversation/[recipient]/index.ts +++ b/src/pages/api/conversation/[recipient]/index.ts @@ -1,7 +1,7 @@ import Joi from "joi"; import { withApiAuthRequired } from "../../../../../lib/session-helpers"; -import { findConversation } from "../../../../database/sms"; +import { findConversation } from "../../../../database/message"; import type { ApiError } from "../../_types"; import appLogger from "../../../../../lib/logger"; diff --git a/src/pages/api/conversation/[recipient]/send-message.ts b/src/pages/api/conversation/[recipient]/send-message.ts index 459b323..64cfa06 100644 --- a/src/pages/api/conversation/[recipient]/send-message.ts +++ b/src/pages/api/conversation/[recipient]/send-message.ts @@ -1,14 +1,12 @@ import Joi from "joi"; -import { SmsType } from "../../../../database/_types"; import { withApiAuthRequired } from "../../../../../lib/session-helpers"; -import { findConversation, insertSms } from "../../../../database/sms"; +import { insertMessage } from "../../../../database/message"; import type { ApiError } from "../../_types"; import appLogger from "../../../../../lib/logger"; import { findCustomerPhoneNumber } from "../../../../database/phone-number"; import { encrypt } from "../../../../database/_encryption"; import { findCustomer } from "../../../../database/customer"; -import twilio from "twilio"; import sendMessageQueue from "../../queue/send-message"; const logger = appLogger.child({ route: "/api/conversation" }); @@ -60,11 +58,12 @@ export default withApiAuthRequired(async function sendMessageHandler( const { phoneNumber } = await findCustomerPhoneNumber(customerId); const body: Body = validationResult.value; - const sms = await insertSms({ + const sms = await insertMessage({ from: phoneNumber, customerId: customerId, sentAt: new Date().toISOString(), - type: SmsType.SENT, + direction: "outbound", + status: "queued", content: encrypt(body.content, customer.encryptionKey), to: body.to, }); diff --git a/src/pages/api/ddd.ts b/src/pages/api/ddd.ts index cde4e55..8b1d86e 100644 --- a/src/pages/api/ddd.ts +++ b/src/pages/api/ddd.ts @@ -1,19 +1,19 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { insertSms } from "../../database/sms"; -import { SmsType } from "../../database/_types"; +import { insertMessage } from "../../database/message"; import { encrypt } from "../../database/_encryption"; import twilio from "twilio"; +import fetchCallsQueue from "./queue/fetch-calls"; export default async function ddd(req: NextApiRequest, res: NextApiResponse) { const accountSid = "ACa886d066be0832990d1cf43fb1d53362"; const authToken = "8696a59a64b94bb4eba3548ed815953b"; // const ddd = await twilio(accountSid, authToken).incomingPhoneNumbers.list(); const phoneNumber = "+33757592025"; - const ddd = await twilio(accountSid, authToken) + /*const ddd = await twilio(accountSid, authToken) .messages .list({ to: phoneNumber, - }); + });*/ /*const ddd = await insertSms({ to: "+213", @@ -43,6 +43,9 @@ export default async function ddd(req: NextApiRequest, res: NextApiResponse) { voiceApplicationSid: appSid, });*/ + const customerId = "bcb723bc-9706-4811-a964-cc03018bd2ac"; + const ddd = fetchCallsQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }) + console.log("ddd", ddd); return res.status(200).send(ddd); diff --git a/src/pages/api/queue/fetch-calls.ts b/src/pages/api/queue/fetch-calls.ts new file mode 100644 index 0000000..8e838b2 --- /dev/null +++ b/src/pages/api/queue/fetch-calls.ts @@ -0,0 +1,40 @@ +import { Queue } from "quirrel/next"; +import twilio from "twilio"; + +import { findCustomerPhoneNumber } from "../../../database/phone-number"; +import { findCustomer } from "../../../database/customer"; +import insertCallsQueue from "./insert-calls"; + +type Payload = { + customerId: string; +} + +const fetchCallsQueue = Queue( + "api/queue/fetch-calls", + async ({ customerId }) => { + const customer = await findCustomer(customerId); + const phoneNumber = await findCustomerPhoneNumber(customerId); + + const [callsSent, callsReceived] = await Promise.all([ + twilio(customer.accountSid, customer.authToken) + .calls + .list({ from: phoneNumber.phoneNumber }), + twilio(customer.accountSid, customer.authToken) + .calls + .list({ to: phoneNumber.phoneNumber }) + ]); + const calls = [ + ...callsSent, + ...callsReceived, + ].sort((a, b) => a.dateCreated.getTime() - b.dateCreated.getTime()); + + await insertCallsQueue.enqueue({ + customerId, + calls, + }, { + id: `insert-calls-${customerId}`, + }); + }, +); + +export default fetchCallsQueue; \ No newline at end of file diff --git a/src/pages/api/queue/fetch-messages.ts b/src/pages/api/queue/fetch-messages.ts index 41cdeec..66c2706 100644 --- a/src/pages/api/queue/fetch-messages.ts +++ b/src/pages/api/queue/fetch-messages.ts @@ -15,12 +15,14 @@ const fetchMessagesQueue = Queue( const customer = await findCustomer(customerId); const phoneNumber = await findCustomerPhoneNumber(customerId); - const messagesSent = await twilio(customer.accountSid, customer.authToken) - .messages - .list({ from: phoneNumber.phoneNumber }); - const messagesReceived = await twilio(customer.accountSid, customer.authToken) - .messages - .list({ to: phoneNumber.phoneNumber }); + const [messagesSent, messagesReceived] = await Promise.all([ + twilio(customer.accountSid, customer.authToken) + .messages + .list({ from: phoneNumber.phoneNumber }), + twilio(customer.accountSid, customer.authToken) + .messages + .list({ to: phoneNumber.phoneNumber }), + ]); const messages = [ ...messagesSent, ...messagesReceived, diff --git a/src/pages/api/queue/insert-calls.ts b/src/pages/api/queue/insert-calls.ts new file mode 100644 index 0000000..cd1dbb6 --- /dev/null +++ b/src/pages/api/queue/insert-calls.ts @@ -0,0 +1,32 @@ +import { Queue } from "quirrel/next"; +import type { CallInstance } from "twilio/lib/rest/api/v2010/account/call"; + +import type { PhoneCall } from "../../../database/phone-call"; +import { insertManyPhoneCalls } from "../../../database/phone-call"; + +type Payload = { + customerId: string; + calls: CallInstance[]; +} + +const insertCallsQueue = Queue( + "api/queue/insert-calls", + async ({ calls, customerId }) => { + const phoneCalls = calls + .map>(call => ({ + customerId, + twilioSid: call.sid, + from: call.from, + to: call.to, + direction: call.direction === "inbound" ? "inbound" : "outbound", + status: call.status, + duration: call.duration, + createdAt: new Date(call.dateCreated).toISOString(), + })) + .sort((a, b) => a.createdAt.localeCompare(b.createdAt)); + + await insertManyPhoneCalls(phoneCalls); + }, +); + +export default insertCallsQueue; \ No newline at end of file diff --git a/src/pages/api/queue/insert-messages.ts b/src/pages/api/queue/insert-messages.ts index 824a0b1..0638140 100644 --- a/src/pages/api/queue/insert-messages.ts +++ b/src/pages/api/queue/insert-messages.ts @@ -2,9 +2,8 @@ import { Queue } from "quirrel/next"; import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message"; import { findCustomer } from "../../../database/customer"; -import type { Sms } from "../../../database/_types"; -import { SmsType } from "../../../database/_types"; -import { insertManySms } from "../../../database/sms"; +import type { Message } from "../../../database/message"; +import { insertManyMessage } from "../../../database/message"; import { encrypt } from "../../../database/_encryption"; type Payload = { @@ -18,16 +17,20 @@ const insertMessagesQueue = Queue( const customer = await findCustomer(customerId); const encryptionKey = customer.encryptionKey; - const sms = messages.map>(message => ({ - customerId, - content: encrypt(message.body, encryptionKey), - from: message.from, - to: message.to, - type: ["received", "receiving"].includes(message.status) ? SmsType.RECEIVED : SmsType.SENT, - messageSid: message.sid, - sentAt: message.dateSent.toISOString(), - })); - await insertManySms(sms); + const sms = messages + .map>(message => ({ + customerId, + content: encrypt(message.body, encryptionKey), + from: message.from, + to: message.to, + status: message.status, + direction: message.direction === "inbound" ? "inbound" : "outbound", + twilioSid: message.sid, + sentAt: new Date(message.dateSent).toISOString(), + })) + .sort((a, b) => a.sentAt.localeCompare(b.sentAt)); + + await insertManyMessage(sms); }, ); diff --git a/src/pages/api/queue/send-message.ts b/src/pages/api/queue/send-message.ts index 802ddda..646ef96 100644 --- a/src/pages/api/queue/send-message.ts +++ b/src/pages/api/queue/send-message.ts @@ -3,7 +3,7 @@ import twilio from "twilio"; import { findCustomer } from "../../../database/customer"; import { findCustomerPhoneNumber } from "../../../database/phone-number"; -import { setTwilioSid } from "../../../database/sms"; +import { setTwilioSid } from "../../../database/message"; type Payload = { id: string; diff --git a/src/pages/api/queue/set-twilio-webhooks.ts b/src/pages/api/queue/set-twilio-webhooks.ts index e8fb17f..16693ca 100644 --- a/src/pages/api/queue/set-twilio-webhooks.ts +++ b/src/pages/api/queue/set-twilio-webhooks.ts @@ -9,14 +9,14 @@ type Payload = { } const setTwilioWebhooks = Queue( - "api/queue/send-message", + "api/queue/set-twilio-webhooks", async ({ customerId }) => { const customer = await findCustomer(customerId); const twimlApp = await twilio(customer.accountSid, customer.authToken) .applications .create({ friendlyName: "Virtual Phone", - smsUrl: "https://phone.mokhtar.dev/api/webhook/incoming-sms", + smsUrl: "https://phone.mokhtar.dev/api/webhook/incoming-message", smsMethod: "POST", voiceUrl: "https://phone.mokhtar.dev/api/webhook/incoming-call", voiceMethod: "POST", diff --git a/src/pages/api/user/add-phone-number.ts b/src/pages/api/user/add-phone-number.ts index e70f57b..fb5ce33 100644 --- a/src/pages/api/user/add-phone-number.ts +++ b/src/pages/api/user/add-phone-number.ts @@ -7,6 +7,7 @@ import appLogger from "../../../../lib/logger"; import { createPhoneNumber } from "../../../database/phone-number"; import { findCustomer } from "../../../database/customer"; import fetchMessagesQueue from "../queue/fetch-messages"; +import fetchCallsQueue from "../queue/fetch-calls"; import setTwilioWebhooks from "../queue/set-twilio-webhooks"; const logger = appLogger.child({ route: "/api/user/add-phone-number" }); @@ -49,6 +50,7 @@ export default withApiAuthRequired(async function addPhoneNumberHandler(req, res await Promise.all([ fetchMessagesQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }), + fetchCallsQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }), setTwilioWebhooks.enqueue({ customerId }, { id: `set-twilio-webhooks-${customerId}` }), ]); diff --git a/src/pages/api/webhook/incoming-sms.ts b/src/pages/api/webhook/incoming-message.ts similarity index 78% rename from src/pages/api/webhook/incoming-sms.ts rename to src/pages/api/webhook/incoming-message.ts index 2e842f0..653b960 100644 --- a/src/pages/api/webhook/incoming-sms.ts +++ b/src/pages/api/webhook/incoming-message.ts @@ -3,14 +3,13 @@ import twilio from "twilio"; import type { ApiError } from "../_types"; import appLogger from "../../../../lib/logger"; -import { Customer, findCustomerByPhoneNumber } from "../../../database/customer"; -import { insertSms } from "../../../database/sms"; -import { SmsType } from "../../../database/_types"; +import { findCustomerByPhoneNumber } from "../../../database/customer"; +import { insertMessage } from "../../../database/message"; import { encrypt } from "../../../database/_encryption"; -const logger = appLogger.child({ route: "/api/webhook/incoming-sms" }); +const logger = appLogger.child({ route: "/api/webhook/incoming-message" }); -export default async function incomingSmsHandler(req: NextApiRequest, res: NextApiResponse) { +export default async function incomingMessageHandler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== "POST") { const statusCode = 405; const apiError: ApiError = { @@ -41,7 +40,7 @@ export default async function incomingSmsHandler(req: NextApiRequest, res: NextA try { const phoneNumber = req.body.To; const customer = await findCustomerByPhoneNumber(phoneNumber); - const url = "https://phone.mokhtar.dev/api/webhook/incoming-sms"; + const url = "https://phone.mokhtar.dev/api/webhook/incoming-message"; const isRequestValid = twilio.validateRequest(customer.authToken!, twilioSignature, url, req.body); if (!isRequestValid) { const statusCode = 400; @@ -55,11 +54,12 @@ export default async function incomingSmsHandler(req: NextApiRequest, res: NextA return; } - await insertSms({ + await insertMessage({ customerId: customer.id, to: req.body.To, from: req.body.From, - type: SmsType.RECEIVED, + status: "received", + direction: "inbound", sentAt: req.body.DateSent, content: encrypt(req.body.Body, customer.encryptionKey), }); diff --git a/src/pages/calls.tsx b/src/pages/calls.tsx index 5707622..1829db1 100644 --- a/src/pages/calls.tsx +++ b/src/pages/calls.tsx @@ -1,14 +1,15 @@ import type { InferGetServerSidePropsType, NextPage } from "next"; import { withPageOnboardingRequired } from "../../lib/session-helpers"; -import Layout from "../components/layout"; +import { findCustomerPhoneCalls } from "../database/phone-call"; import useUser from "../hooks/use-user"; +import Layout from "../components/layout"; type Props = InferGetServerSidePropsType; const pageTitle = "Calls"; -const Calls: NextPage = (props) => { +const Calls: NextPage = ({ phoneCalls }) => { const { userProfile } = useUser(); console.log("userProfile", userProfile); @@ -21,19 +22,34 @@ const Calls: NextPage = (props) => {

Calls page

+
    + {phoneCalls.map((phoneCall) => { + const recipient = phoneCall.direction === "outbound" ? phoneCall.to : phoneCall.from; + return ( +
  • +
    {recipient}
    +
    {new Date(phoneCall.createdAt).toLocaleString("fr-FR")}
    +
  • + ) + })} +
); }; export const getServerSideProps = withPageOnboardingRequired( - async ({ res }) => { + async ({ res }, user) => { res.setHeader( "Cache-Control", "private, s-maxage=15, stale-while-revalidate=59", ); - return { props: {} }; + const phoneCalls = await findCustomerPhoneCalls(user.id); + + return { + props: { phoneCalls }, + }; }, ); diff --git a/src/pages/messages/[recipient].tsx b/src/pages/messages/[recipient].tsx index 6390aeb..051f020 100644 --- a/src/pages/messages/[recipient].tsx +++ b/src/pages/messages/[recipient].tsx @@ -7,9 +7,8 @@ import clsx from "clsx"; import { useForm } from "react-hook-form"; import { withPageOnboardingRequired } from "../../../lib/session-helpers"; -import { findConversation } from "../../database/sms"; -import type { Sms } from "../../database/_types"; -import { SmsType } from "../../database/_types"; +import type { Message } from "../../database/message"; +import { findConversation } from "../../database/message"; import supabase from "../../supabase/client"; import useUser from "../../hooks/use-user"; import useConversation from "../../hooks/use-conversation"; @@ -17,7 +16,7 @@ import Layout from "../../components/layout"; type Props = { recipient: string; - conversation: Sms[]; + conversation: Message[]; } type Form = { @@ -60,7 +59,7 @@ const Messages: NextPage = (props) => { } const subscription = supabase - .from(`sms:customerId=eq.${userProfile.id}`) + .from(`sms:customerId=eq.${userProfile.id}`) .on("INSERT", (payload) => { const message = payload.new; if ([message.from, message.to].includes(recipient)) { @@ -98,12 +97,28 @@ const Messages: NextPage = (props) => {
    - {conversation!.map(message => { + {conversation!.map((message, index) => { + const isOutbound = message.direction === "outbound"; + const isSameSender = message.from === conversation![index + 1]?.from; + const isLast = index === conversation!.length; return ( -
  • - {message.content} +
  • + + {message.content} +
  • - ) + ); })}
diff --git a/src/pages/messages/index.tsx b/src/pages/messages/index.tsx index 634f562..ec5eb2a 100644 --- a/src/pages/messages/index.tsx +++ b/src/pages/messages/index.tsx @@ -2,9 +2,8 @@ import type { InferGetServerSidePropsType, NextPage } from "next"; import Link from "next/link"; import { withPageOnboardingRequired } from "../../../lib/session-helpers"; -import type { Sms } from "../../database/_types"; -import { SmsType } from "../../database/_types"; -import { findCustomerMessages } from "../../database/sms"; +import type { Message } from "../../database/message"; +import { findCustomerMessages } from "../../database/message"; import { findCustomer } from "../../database/customer"; import { decrypt } from "../../database/_encryption"; import useUser from "../../hooks/use-user"; @@ -25,15 +24,17 @@ const Messages: NextPage = ({ conversations }) => {

Messages page

-