send message to new recipient

This commit is contained in:
m5r 2021-08-01 15:40:18 +08:00
parent 7d7c4cb495
commit 56e8880715
15 changed files with 456 additions and 39 deletions

View File

@ -1,16 +1,37 @@
import { BlitzApiRequest, BlitzApiResponse } from "blitz";
import db from "db";
import twilio from "twilio";
export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) {
await Promise.all([
/*await Promise.all([
db.message.deleteMany(),
db.phoneCall.deleteMany(),
db.phoneNumber.deleteMany(),
db.customer.deleteMany(),
]);
await db.user.deleteMany();
await db.user.deleteMany();*/
const accountSid = "ACa886d066be0832990d1cf43fb1d53362";
const authToken = "8696a59a64b94bb4eba3548ed815953b";
/*const ddd = await twilio(accountSid, authToken)
.lookups
.v1
// .phoneNumbers("+33613370787")
.phoneNumbers("+33476982071")
.fetch();*/
try {
await twilio(accountSid, authToken).messages.create({
body: "content",
to: "+213744123789",
from: "+33757592025",
});
} catch (error) {
console.log(error.code);
console.log(error.moreInfo);
console.log(error.details);
// console.log(JSON.stringify(Object.keys(error)));
}
res.status(200).end();
res.status(200).send(ddd);
}

View File

@ -16,15 +16,24 @@ const sendMessageQueue = Queue<Payload>(
const customer = await db.customer.findFirst({ where: { id: customerId } });
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId } });
const message = await twilio(customer!.accountSid!, customer!.authToken!).messages.create({
body: content,
to,
from: phoneNumber!.phoneNumber,
});
await db.message.update({
where: { id },
data: { twilioSid: message.sid },
});
try {
const message = await twilio(
customer!.accountSid!,
customer!.authToken!
).messages.create({
body: content,
to,
from: phoneNumber!.phoneNumber,
});
await db.message.update({
where: { id },
data: { twilioSid: message.sid },
});
} catch (error) {
// TODO: handle twilio error
console.log(error.code); // 21211
console.log(error.moreInfo); // https://www.twilio.com/docs/errors/21211
}
},
{
retry: ["1min"],

View File

@ -8,7 +8,8 @@ import NewMessageArea from "./new-message-area";
export default function Conversation() {
const router = useRouter();
const conversation = useConversation(router.params.recipient)[0];
const recipient = decodeURIComponent(router.params.recipient);
const conversation = useConversation(recipient)[0];
const messagesListRef = useRef<HTMLUListElement>(null);
useEffect(() => {
@ -75,7 +76,7 @@ export default function Conversation() {
</ul>
</div>
<Suspense fallback={null}>
<NewMessageArea />
<NewMessageArea recipient={recipient} />
</Suspense>
</>
);

View File

@ -8,14 +8,18 @@ import { Direction, Message, MessageStatus } from "../../../db";
import getConversationsQuery from "../queries/get-conversations";
import useCurrentCustomer from "../../core/hooks/use-current-customer";
import useCustomerPhoneNumber from "../../core/hooks/use-customer-phone-number";
import { FunctionComponent } from "react";
type Form = {
content: string;
};
export default function NewMessageArea() {
const router = useRouter();
const recipient = router.params.recipient;
type Props = {
recipient: string;
onSend?: () => void;
};
const NewMessageArea: FunctionComponent<Props> = ({ recipient, onSend }) => {
const { customer } = useCurrentCustomer();
const phoneNumber = useCustomerPhoneNumber();
const sendMessageMutation = useMutation(sendMessage)[0];
@ -30,6 +34,10 @@ export default function NewMessageArea() {
formState: { isSubmitting },
} = useForm<Form>();
const onSubmit = handleSubmit(async ({ content }) => {
if (!recipient) {
return;
}
if (isSubmitting) {
return;
}
@ -60,6 +68,7 @@ export default function NewMessageArea() {
{ refetch: false }
);
setValue("content", "");
onSend?.();
await sendMessageMutation({ to: recipient, content });
await refetchConversations({ cancelRefetch: true });
});
@ -83,7 +92,9 @@ export default function NewMessageArea() {
</button>
</form>
);
}
};
export default NewMessageArea;
function uuidv4() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {

View File

@ -0,0 +1,53 @@
import { Suspense, useState } from "react";
import { BottomSheet } from "react-spring-bottom-sheet";
import { atom, useAtom } from "jotai";
import { useRouter, Routes } from "blitz";
import "react-spring-bottom-sheet/dist/style.css";
import NewMessageArea from "./new-message-area";
export const bottomSheetOpenAtom = atom(false);
export default function NewMessageBottomSheet() {
const router = useRouter();
const [isOpen, setIsOpen] = useAtom(bottomSheetOpenAtom);
const [recipient, setRecipient] = useState("");
return (
<BottomSheet
open={isOpen}
onDismiss={() => setIsOpen(false)}
snapPoints={({ maxHeight }) => maxHeight / 2}
header={
<div className="w-full flex items-center justify-center p-4 text-black relative">
<span className="font-semibold text-base">New Message</span>
<button onClick={() => setIsOpen(false)} className="absolute right-4">
Cancel
</button>
</div>
}
>
<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>
<input
onChange={(event) => setRecipient(event.target.value)}
className="bg-none border-none outline-none flex-1 text-black"
/>
</div>
<Suspense fallback={null}>
<NewMessageArea
recipient={recipient}
onSend={() => {
router
.push(Routes.ConversationPage({ recipient }))
.then(() => setIsOpen(false));
}}
/>
</Suspense>
</main>
</BottomSheet>
);
}

View File

@ -0,0 +1,21 @@
import type { FunctionComponent, MouseEventHandler } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEdit } from "@fortawesome/pro-regular-svg-icons";
type Props = {
onClick: MouseEventHandler<HTMLButtonElement>;
};
const NewMessageButton: FunctionComponent<Props> = ({ onClick }) => {
return (
<button
onClick={onClick}
className="absolute bottom-20 right-6
w-14 h-14 bg-gray-800 rounded-full hover:bg-gray-900 active:shadow-lg shadow transition ease-in duration-200 focus:outline-none"
>
<FontAwesomeIcon size="lg" className="m-auto pl-1.5 text-white w-8 h-8" icon={faEdit} />
</button>
);
};
export default NewMessageButton;

View File

@ -14,6 +14,7 @@ export default function useConversation(recipient: string) {
return conversations[recipient]!;
},
keepPreviousData: true,
}
);
}

View File

@ -0,0 +1,14 @@
import { useQuery } from "blitz";
import getConversationsQuery from "../queries/get-conversations";
export default function useKnownRecipients() {
return useQuery(
getConversationsQuery,
{},
{
select(conversations) {
return Object.keys(conversations);
},
}
);
}

View File

@ -1,11 +1,15 @@
import { resolver } from "blitz";
import { z } from "zod";
import twilio from "twilio";
import db, { Direction, MessageStatus } from "../../../db";
import getCurrentCustomer from "../../customers/queries/get-current-customer";
import getCustomerPhoneNumber from "../../phone-numbers/queries/get-customer-phone-number";
import { encrypt } from "../../../db/_encryption";
import sendMessageQueue from "../../api/queue/send-message";
import appLogger from "../../../integrations/logger";
const logger = appLogger.child({ mutation: "send-message" });
const Body = z.object({
content: z.string(),
@ -17,6 +21,15 @@ export default resolver.pipe(
resolver.authorize(),
async ({ content, to }, context) => {
const customer = await getCurrentCustomer(null, context);
try {
await twilio(customer!.accountSid!, customer!.authToken!)
.lookups.v1.phoneNumbers(to)
.fetch();
} catch (error) {
logger.error(error);
return;
}
const customerId = customer!.id;
const customerPhoneNumber = await getCustomerPhoneNumber({ customerId }, context);

View File

@ -1,22 +1,28 @@
import { Suspense } from "react";
import { Suspense, useState } from "react";
import type { BlitzPage } from "blitz";
import { Routes } from "blitz";
import { useAtom } from "jotai";
import Layout from "../../core/layouts/layout";
import ConversationsList from "../components/conversations-list";
import NewMessageButton from "../components/new-message-button";
import NewMessageBottomSheet, { bottomSheetOpenAtom } from "../components/new-message-bottom-sheet";
import useRequireOnboarding from "../../core/hooks/use-require-onboarding";
const Messages: BlitzPage = () => {
useRequireOnboarding();
const setIsOpen = useAtom(bottomSheetOpenAtom)[1];
return (
<>
<div className="flex flex-col space-y-6 p-6">
<p>Messages page</p>
<h2 className="text-3xl font-bold">Messages</h2>
</div>
<Suspense fallback="Loading...">
<ConversationsList />
</Suspense>
<NewMessageButton onClick={() => setIsOpen(true)} />
<NewMessageBottomSheet />
</>
);
};

View File

@ -16,7 +16,7 @@ const ConversationPage: BlitzPage = () => {
useRequireOnboarding();
const router = useRouter();
const recipient = router.params.recipient;
const recipient = decodeURIComponent(router.params.recipient);
return (
<>
@ -39,7 +39,7 @@ const ConversationPage: BlitzPage = () => {
ConversationPage.getLayout = function ConversationLayout(page) {
const router = useRouter();
const recipient = router.params.recipient;
const recipient = decodeURIComponent(router.params.recipient);
const pageTitle = `Messages with ${recipient}`;
return (

View File

@ -12,7 +12,7 @@ const PhoneCalls: BlitzPage = () => {
return (
<>
<div className="flex flex-col space-y-6 p-6">
<p>Calls page</p>
<h2 className="text-3xl font-bold">Calls</h2>
</div>
<Suspense fallback="Loading...">
<PhoneCallsList />

View File

@ -33,22 +33,27 @@ const Settings: BlitzPage = () => {
useRequireOnboarding();
return (
<div className="flex flex-col space-y-6 p-6">
<aside className="py-6 lg:col-span-3">
<nav className="space-y-1">
{navigation.map((item) => (
<a
key={item.name}
href={item.href}
className="border-transparent text-gray-900 hover:bg-gray-50 hover:text-gray-900 group border-l-4 px-3 py-2 flex items-center text-sm font-medium"
>
<item.icon className="text-gray-400 group-hover:text-gray-500 flex-shrink-0 -ml-1 mr-3 h-6 w-6" />
<span className="truncate">{item.name}</span>
</a>
))}
</nav>
</aside>
</div>
<>
<div className="flex flex-col space-y-6 p-6">
<h2 className="text-3xl font-bold">Settings</h2>
</div>
<div className="flex flex-col space-y-6 p-6">
<aside className="py-6 lg:col-span-3">
<nav className="space-y-1">
{navigation.map((item) => (
<a
key={item.name}
href={item.href}
className="border-transparent text-gray-900 hover:bg-gray-50 hover:text-gray-900 group border-l-4 px-3 py-2 flex items-center text-sm font-medium"
>
<item.icon className="text-gray-400 group-hover:text-gray-500 flex-shrink-0 -ml-1 mr-3 h-6 w-6" />
<span className="truncate">{item.name}</span>
</a>
))}
</nav>
</aside>
</div>
</>
);
};

259
package-lock.json generated
View File

@ -1329,6 +1329,11 @@
"chalk": "^4.0.0"
}
},
"@juggle/resize-observer": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz",
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
},
"@mrleebo/prisma-ast": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.2.5.tgz",
@ -2212,6 +2217,161 @@
"pino": "^6.11.3"
}
},
"@reach/portal": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/@reach/portal/-/portal-0.13.2.tgz",
"integrity": "sha512-g74BnCdtuTGthzzHn2cWW+bcyIYb0iIE/yRsm89i8oNzNgpopbkh9UY8TPbhNlys52h7U60s4kpRTmcq+JqsTA==",
"requires": {
"@reach/utils": "0.13.2",
"tslib": "^2.1.0"
}
},
"@reach/utils": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.13.2.tgz",
"integrity": "sha512-3ir6cN60zvUrwjOJu7C6jec/samqAeyAB12ZADK+qjnmQPdzSYldrFWwDVV5H0WkhbYXR3uh+eImu13hCetNPQ==",
"requires": {
"@types/warning": "^3.0.0",
"tslib": "^2.1.0",
"warning": "^4.0.3"
}
},
"@react-aria/interactions": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.5.0.tgz",
"integrity": "sha512-EL5GWpzM9UHU17LztwgL/tF3H2tLG375CD64kieCgSfsRcCSlC3pavnPy9jbS8levdBQ2qo9e2xfoX5VtfJisw==",
"requires": {
"@babel/runtime": "^7.6.2",
"@react-aria/utils": "^3.8.1",
"@react-types/shared": "^3.7.0"
}
},
"@react-aria/ssr": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.0.2.tgz",
"integrity": "sha512-+M0wrUlc2eTuMiwTfd0iFZJGu2hvMeYBLE8gRdbPJCDjLhrNWOQLKR/y6ntxQ9u8zjrNl/YPOdRtcqkA2EBnAQ==",
"requires": {
"@babel/runtime": "^7.6.2"
}
},
"@react-aria/utils": {
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.8.1.tgz",
"integrity": "sha512-SvFf1T2HHAId6LS4+gbJNLQU9wr5GHuR5wA+HOtfVkZ82v3xhOnzfjR5qgjSLYGsPfqNgci5cpKYlHf4YqMf5w==",
"requires": {
"@babel/runtime": "^7.6.2",
"@react-aria/ssr": "^3.0.2",
"@react-stately/utils": "^3.2.1",
"@react-types/shared": "^3.7.0",
"clsx": "^1.1.1"
}
},
"@react-spring/animated": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.2.4.tgz",
"integrity": "sha512-AfV6ZM8pCCAT29GY5C8/1bOPjZrv/7kD0vedjiE/tEYvNDwg9GlscrvsTViWR2XykJoYrDfdkYArrldWpsCJ5g==",
"requires": {
"@react-spring/shared": "~9.2.0",
"@react-spring/types": "~9.2.0"
}
},
"@react-spring/core": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.2.4.tgz",
"integrity": "sha512-R+PwyfsjiuYCWqaTTfCpYpRmsP0h87RNm7uxC1Uxy7QAHUfHEm2sAHn+AdHPwq/MbVwDssVT8C5yf2WGcqiXGg==",
"requires": {
"@react-spring/animated": "~9.2.0",
"@react-spring/shared": "~9.2.0",
"@react-spring/types": "~9.2.0"
}
},
"@react-spring/konva": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/konva/-/konva-9.2.4.tgz",
"integrity": "sha512-19anDOIkfjcydDTfGgVIuZ3lruZxKubYGs9oHCswaP8SRLj7c1kkopJHUr/S4LXGxiIdqdF0XucWm0iTEPEq4w==",
"requires": {
"@react-spring/animated": "~9.2.0",
"@react-spring/core": "~9.2.0",
"@react-spring/shared": "~9.2.0",
"@react-spring/types": "~9.2.0"
}
},
"@react-spring/native": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/native/-/native-9.2.4.tgz",
"integrity": "sha512-xKJWKh5qOhSclpL3iuGwJRLoZzTNvlBEnIrMs8yh8xvX6z9Lmnu4uGu5DpfrnM1GzBvRoktoCoLEx/VcEYFSng==",
"requires": {
"@react-spring/animated": "~9.2.0",
"@react-spring/core": "~9.2.0",
"@react-spring/shared": "~9.2.0",
"@react-spring/types": "~9.2.0"
}
},
"@react-spring/rafz": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.2.4.tgz",
"integrity": "sha512-SOKf9eue+vAX+DGo7kWYNl9i9J3gPUlQjifIcV9Bzw9h3i30wPOOP0TjS7iMG/kLp2cdHQYDNFte6nt23VAZkQ=="
},
"@react-spring/shared": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.2.4.tgz",
"integrity": "sha512-ZEr4l2BxmyFRUvRA2VCkPfCJii4E7cGkwbjmTBx1EmcGrOnde/V2eF5dxqCTY3k35QuCegkrWe0coRJVkh8q2Q==",
"requires": {
"@react-spring/rafz": "~9.2.0",
"@react-spring/types": "~9.2.0"
}
},
"@react-spring/three": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.2.4.tgz",
"integrity": "sha512-ljFig7XW099VWwRPKPUf+4yYLivp/sSWXN3oO5SJOF/9BSoV1quS/9chZ5Myl5J14od3CsHf89Tv4FdlX5kHlA==",
"requires": {
"@react-spring/animated": "~9.2.0",
"@react-spring/core": "~9.2.0",
"@react-spring/shared": "~9.2.0",
"@react-spring/types": "~9.2.0"
}
},
"@react-spring/types": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.2.4.tgz",
"integrity": "sha512-zHUXrWO8nweUN/ISjrjqU7GgXXvoEbFca1CgiE0TY0H/dqJb3l+Rhx8ecPVNYimzFg3ZZ1/T0egpLop8SOv4aA=="
},
"@react-spring/web": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.2.4.tgz",
"integrity": "sha512-vtPvOalLFvuju/MDBtoSnCyt0xXSL6Amyv82fljOuWPl1yGd4M1WteijnYL9Zlriljl0a3oXcPunAVYTD9dbDQ==",
"requires": {
"@react-spring/animated": "~9.2.0",
"@react-spring/core": "~9.2.0",
"@react-spring/shared": "~9.2.0",
"@react-spring/types": "~9.2.0"
}
},
"@react-spring/zdog": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/@react-spring/zdog/-/zdog-9.2.4.tgz",
"integrity": "sha512-rv7ptedS37SHr6yuCbRkUErAzAhebdgt8f4KUtZWzseC+7qLNkaZWf+uujgsb881qAuX9b9yz8rre9UKeYepgw==",
"requires": {
"@react-spring/animated": "~9.2.0",
"@react-spring/core": "~9.2.0",
"@react-spring/shared": "~9.2.0",
"@react-spring/types": "~9.2.0"
}
},
"@react-stately/utils": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.2.1.tgz",
"integrity": "sha512-H79CYKPiQZrO1/dMSwjRJxsRlYg7y8PbTwnZOQ1h3DI5W6tD8CCLSlU1A5/Fp1GfcGNnK8gHqsJ9oJSRAwFS1g==",
"requires": {
"@babel/runtime": "^7.6.2"
}
},
"@react-types/shared": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.7.1.tgz",
"integrity": "sha512-VNKlqh37UjB3Hd7gb5Hgsum/2x5mhd7vuBBGPEFevhkOMBW8KlqrU75yaKUe3rEFbky7H6+A8Dzoj4r68OS14w=="
},
"@rushstack/eslint-patch": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.6.tgz",
@ -2786,6 +2946,11 @@
"@types/jest": "*"
}
},
"@types/warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI="
},
"@types/yargs": {
"version": "15.0.14",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz",
@ -2935,6 +3100,15 @@
"eslint-visitor-keys": "^2.0.0"
}
},
"@xstate/react": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@xstate/react/-/react-1.5.1.tgz",
"integrity": "sha512-DJHDqDlZHus08X98uMJw4KR17FRWBXLHMQ02YRxx0DMm5VLn75VwGyt4tXdlNZHQWjyk++C5c9Ichq3PdmM3og==",
"requires": {
"use-isomorphic-layout-effect": "^1.0.0",
"use-subscription": "^1.3.0"
}
},
"abab": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
@ -3800,6 +3974,11 @@
}
}
},
"body-scroll-lock": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz",
"integrity": "sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg=="
},
"boolean": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.2.tgz",
@ -6973,6 +7152,14 @@
"readable-stream": "^3.1.1"
}
},
"focus-trap": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.6.0.tgz",
"integrity": "sha512-2hWVR3XbBejn5v8wDW9DFzLWXcxMNaSJ/CtE3E+FJjjBCLwIYbZJwjUi2RDBfQPM58gHEt5hck0jrJgHR9/s+A==",
"requires": {
"tabbable": "^5.2.0"
}
},
"follow-redirects": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
@ -12382,6 +12569,50 @@
"react-is": "^16.12.0 || ^17.0.0"
}
},
"react-spring": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/react-spring/-/react-spring-9.2.4.tgz",
"integrity": "sha512-bMjbyTW0ZGd+/h9cjtohLqCwOGqX2OuaTvalOVfLCGmhzEg/u3GgopI3LAm4UD2Br3MNdVdGgNVoESg4MGqKFQ==",
"requires": {
"@react-spring/core": "~9.2.0",
"@react-spring/konva": "~9.2.0",
"@react-spring/native": "~9.2.0",
"@react-spring/three": "~9.2.0",
"@react-spring/web": "~9.2.0",
"@react-spring/zdog": "~9.2.0"
}
},
"react-spring-bottom-sheet": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/react-spring-bottom-sheet/-/react-spring-bottom-sheet-3.4.0.tgz",
"integrity": "sha512-zKwTymxrTRMHPjfBiMw8reQlWoVqlCGMTefmMYkAlBvR7n3hBe5sntuQJAEjmrAnA+cLSGp44mtmgBtT2ksL5Q==",
"requires": {
"@juggle/resize-observer": "^3.2.0",
"@reach/portal": "^0.13.0",
"@xstate/react": "^1.2.0",
"body-scroll-lock": "^3.1.5",
"focus-trap": "^6.2.2",
"react-spring": "^8.0.27",
"react-use-gesture": "^8.0.1",
"xstate": "^4.15.1"
},
"dependencies": {
"react-spring": {
"version": "8.0.27",
"resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz",
"integrity": "sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==",
"requires": {
"@babel/runtime": "^7.3.1",
"prop-types": "^15.5.8"
}
},
"react-use-gesture": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-8.0.1.tgz",
"integrity": "sha512-CXzUNkulUdgouaAlvAsC5ZVo0fi9KGSBSk81WrE4kOIcJccpANe9zZkAYr5YZZhqpicIFxitsrGVS4wmoMun9A=="
}
}
},
"react-test-renderer": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.1.tgz",
@ -12400,6 +12631,11 @@
}
}
},
"react-use-gesture": {
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-9.1.3.tgz",
"integrity": "sha512-CdqA2SmS/fj3kkS2W8ZU8wjTbVBAIwDWaRprX7OKaj7HlGwBasGEFggmk5qNklknqk9zK/h8D355bEJFTpqEMg=="
},
"read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@ -13962,6 +14198,11 @@
"rename-overwrite": "^3.0.0"
}
},
"tabbable": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.2.0.tgz",
"integrity": "sha512-0uyt8wbP0P3T4rrsfYg/5Rg3cIJ8Shl1RJ54QMqYxm1TLdWqJD1u6+RQjr2Lor3wmfT7JRHkirIwy99ydBsyPg=="
},
"table": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
@ -14821,6 +15062,11 @@
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
},
"use-isomorphic-layout-effect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz",
"integrity": "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ=="
},
"use-subscription": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz",
@ -15144,6 +15390,14 @@
"makeerror": "1.0.x"
}
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"watchpack": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz",
@ -15352,6 +15606,11 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
},
"xstate": {
"version": "4.23.1",
"resolved": "https://registry.npmjs.org/xstate/-/xstate-4.23.1.tgz",
"integrity": "sha512-8ZoCe8d6wDSPfkep+GBgi+fKAdMyXcaizoNf5FKceEhlso4+9n1TeK6oviaDsXZ3Z5O8xKkJOxXPNuD4cA9LCw=="
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

View File

@ -59,6 +59,9 @@
"react": "18.0.0-alpha-6f3fcbd6f-20210730",
"react-dom": "18.0.0-alpha-6f3fcbd6f-20210730",
"react-hook-form": "7.12.2",
"react-spring": "9.2.4",
"react-spring-bottom-sheet": "3.4.0",
"react-use-gesture": "9.1.3",
"tailwindcss": "2.2.7",
"twilio": "3.66.1",
"zod": "3.5.1"