diff --git a/app/features/keypad/components/blurred-keypad.tsx b/app/features/keypad/components/blurred-keypad.tsx
new file mode 100644
index 0000000..b3ef2f5
--- /dev/null
+++ b/app/features/keypad/components/blurred-keypad.tsx
@@ -0,0 +1,18 @@
+import { IoCall } from "react-icons/io5";
+
+import Keypad from "~/features/keypad/components/keypad";
+
+export default function BlurredKeypad() {
+ return (
+
+ );
+}
diff --git a/app/features/keypad/components/keypad.tsx b/app/features/keypad/components/keypad.tsx
index 6d97dc9..1c7473b 100644
--- a/app/features/keypad/components/keypad.tsx
+++ b/app/features/keypad/components/keypad.tsx
@@ -1,50 +1,45 @@
-import type { FunctionComponent, PropsWithChildren } from "react";
-import type { PressHookProps } from "@react-aria/interactions";
+import { type FunctionComponent, type PropsWithChildren, useRef } from "react";
import { usePress } from "@react-aria/interactions";
+import { useLongPressDigit, usePressDigit } from "~/features/keypad/hooks/atoms";
-type Props = {
- onDigitPressProps: (digit: string) => PressHookProps;
- onZeroPressProps: PressHookProps;
-};
-
-const Keypad: FunctionComponent> = ({ children, onDigitPressProps, onZeroPressProps }) => {
+const Keypad: FunctionComponent> = ({ children }) => {
return (
-
-
+
+
ABC
-
+
DEF
-
+
GHI
-
+
JKL
-
+
MNO
-
+
PQRS
-
+
TUV
-
+
WXYZ
-
-
-
+
+
+
{typeof children !== "undefined" ? {children}
: null}
@@ -57,15 +52,23 @@ const Row: FunctionComponent> = ({ children }) => (
{children}
);
-const DigitLetters: FunctionComponent> = ({ children }) => {children}
;
+const DigitLetters: FunctionComponent> = ({ children }) => (
+ {children}
+);
type DigitProps = {
digit: string;
- onPressProps: Props["onDigitPressProps"];
};
-const Digit: FunctionComponent> = ({ children, digit, onPressProps }) => {
- const { pressProps } = usePress(onPressProps(digit));
+const Digit: FunctionComponent> = ({ children, digit }) => {
+ const pressDigit = usePressDigit();
+ const onPressProps = {
+ onPress() {
+ // navigator.vibrate(1); // removed in webkit
+ pressDigit(digit);
+ },
+ };
+ const { pressProps } = usePress(onPressProps);
return (
@@ -75,11 +78,24 @@ const Digit: FunctionComponent
> = ({ children, dig
);
};
-type ZeroDigitProps = {
- onPressProps: Props["onZeroPressProps"];
-};
-
-const ZeroDigit: FunctionComponent> = ({ onPressProps }) => {
+const ZeroDigit: FunctionComponent = () => {
+ const timeoutRef = useRef | null>(null);
+ const pressDigit = usePressDigit();
+ const longPressDigit = useLongPressDigit();
+ const onPressProps = {
+ onPressStart() {
+ pressDigit("0");
+ timeoutRef.current = setTimeout(() => {
+ longPressDigit("+");
+ }, 750);
+ },
+ onPressEnd() {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ timeoutRef.current = null;
+ }
+ },
+ };
const { pressProps } = usePress(onPressProps);
return (
diff --git a/app/features/keypad/hooks/atoms.ts b/app/features/keypad/hooks/atoms.ts
new file mode 100644
index 0000000..f836122
--- /dev/null
+++ b/app/features/keypad/hooks/atoms.ts
@@ -0,0 +1,33 @@
+import { atom, useAtom } from "jotai";
+
+const phoneNumberAtom = atom("");
+const pressDigitAtom = atom(null, (get, set, digit: string) => {
+ if (get(phoneNumberAtom).length > 17) {
+ return;
+ }
+
+ if ("0123456789+#*".indexOf(digit) === -1) {
+ return;
+ }
+
+ set(phoneNumberAtom, (prevState) => prevState + digit);
+});
+const longPressDigitAtom = atom(null, (get, set, replaceWith: string) => {
+ if (get(phoneNumberAtom).length > 17) {
+ return;
+ }
+
+ set(phoneNumberAtom, (prevState) => prevState.slice(0, -1) + replaceWith);
+});
+const pressBackspaceAtom = atom(null, (get, set) => {
+ if (get(phoneNumberAtom).length === 0) {
+ return;
+ }
+
+ set(phoneNumberAtom, (prevState) => prevState.slice(0, -1));
+});
+
+export const usePhoneNumber = () => useAtom(phoneNumberAtom);
+export const useRemoveDigit = () => useAtom(pressBackspaceAtom)[1];
+export const usePressDigit = () => useAtom(pressDigitAtom)[1];
+export const useLongPressDigit = () => useAtom(longPressDigitAtom)[1];
diff --git a/app/features/keypad/hooks/use-on-backspace-press.ts b/app/features/keypad/hooks/use-on-backspace-press.ts
new file mode 100644
index 0000000..ee24e8c
--- /dev/null
+++ b/app/features/keypad/hooks/use-on-backspace-press.ts
@@ -0,0 +1,34 @@
+import { useRef } from "react";
+import { usePress } from "@react-aria/interactions";
+
+import { useRemoveDigit } from "./atoms";
+
+export default function useOnBackspacePress() {
+ const removeDigit = useRemoveDigit();
+ const timeoutRef = useRef | null>(null);
+ const intervalRef = useRef | null>(null);
+ const { pressProps: onBackspacePressProps } = usePress({
+ onPressStart() {
+ timeoutRef.current = setTimeout(() => {
+ removeDigit();
+ intervalRef.current = setInterval(removeDigit, 75);
+ }, 325);
+ },
+ onPressEnd() {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ timeoutRef.current = null;
+ }
+
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ intervalRef.current = null;
+ return;
+ }
+
+ removeDigit();
+ },
+ });
+
+ return onBackspacePressProps;
+}
diff --git a/app/routes/__app/keypad.tsx b/app/routes/__app/keypad.tsx
index 4da1f44..1d53b5a 100644
--- a/app/routes/__app/keypad.tsx
+++ b/app/routes/__app/keypad.tsx
@@ -1,32 +1,56 @@
-import { Fragment, useRef, useState } from "react";
+import { Fragment } from "react";
+import type { LoaderFunction, MetaFunction } from "@remix-run/node";
import { useNavigate } from "@remix-run/react";
-import { atom, useAtom } from "jotai";
-import { usePress } from "@react-aria/interactions";
+import { json, useLoaderData } from "superjson-remix";
import { Transition } from "@headlessui/react";
import { IoBackspace, IoCall } from "react-icons/io5";
-import { Direction } from "@prisma/client";
+import { Prisma } from "@prisma/client";
-import Keypad from "~/features/keypad/components/keypad";
-// import usePhoneCalls from "~/features/keypad/hooks/use-phone-calls";
import useKeyPress from "~/features/keypad/hooks/use-key-press";
-// import useCurrentUser from "~/features/core/hooks/use-current-user";
-import KeypadErrorModal from "~/features/keypad/components/keypad-error-modal";
+import useOnBackspacePress from "~/features/keypad/hooks/use-on-backspace-press";
+import Keypad from "~/features/keypad/components/keypad";
+import BlurredKeypad from "~/features/keypad/components/blurred-keypad";
+import MissingTwilioCredentials from "~/features/core/components/missing-twilio-credentials";
import InactiveSubscription from "~/features/core/components/inactive-subscription";
+import { getSeoMeta } from "~/utils/seo";
+import db from "~/utils/db.server";
+import { requireLoggedIn } from "~/utils/auth.server";
+import { usePhoneNumber, usePressDigit, useRemoveDigit } from "~/features/keypad/hooks/atoms";
+
+export const meta: MetaFunction = () => ({
+ ...getSeoMeta({ title: "Keypad" }),
+});
+
+type KeypadLoaderData = {
+ hasOngoingSubscription: boolean;
+ hasPhoneNumber: boolean;
+ lastRecipientCalled?: string;
+};
+
+export const loader: LoaderFunction = async ({ request }) => {
+ const { phoneNumber } = await requireLoggedIn(request);
+ const hasOngoingSubscription = true; // TODO
+ const hasPhoneNumber = Boolean(phoneNumber);
+ const lastCall =
+ phoneNumber &&
+ (await db.phoneCall.findFirst({
+ where: { phoneNumberId: phoneNumber.id },
+ orderBy: { createdAt: Prisma.SortOrder.desc },
+ }));
+ return json({
+ hasOngoingSubscription,
+ hasPhoneNumber,
+ lastRecipientCalled: lastCall?.recipient,
+ });
+};
export default function KeypadPage() {
- const { hasFilledTwilioCredentials, hasPhoneNumber, hasOngoingSubscription } = {
- hasFilledTwilioCredentials: false,
- hasPhoneNumber: false,
- hasOngoingSubscription: false,
- };
+ const { hasOngoingSubscription, hasPhoneNumber, lastRecipientCalled } = useLoaderData();
const navigate = useNavigate();
- const [isKeypadErrorModalOpen, setIsKeypadErrorModalOpen] = useState(false);
- const phoneCalls: any[] = []; //usePhoneCalls();
- const [phoneNumber, setPhoneNumber] = useAtom(phoneNumberAtom);
- const removeDigit = useAtom(pressBackspaceAtom)[1];
- const timeoutRef = useRef | null>(null);
- const intervalRef = useRef | null>(null);
- const pressDigit = useAtom(pressDigitAtom)[1];
+ const [phoneNumber, setPhoneNumber] = usePhoneNumber();
+ const removeDigit = useRemoveDigit();
+ const pressDigit = usePressDigit();
+ const onBackspacePress = useOnBackspacePress();
useKeyPress((key) => {
if (!hasOngoingSubscription) {
return;
@@ -38,74 +62,21 @@ export default function KeypadPage() {
pressDigit(key);
});
- const longPressDigit = useAtom(longPressDigitAtom)[1];
- const onZeroPressProps = {
- onPressStart() {
- if (!hasOngoingSubscription) {
- return;
- }
- pressDigit("0");
- timeoutRef.current = setTimeout(() => {
- longPressDigit("+");
- }, 750);
- },
- onPressEnd() {
- if (timeoutRef.current) {
- clearTimeout(timeoutRef.current);
- timeoutRef.current = null;
- }
- },
- };
- const onDigitPressProps = (digit: string) => ({
- onPress() {
- // navigator.vibrate(1); // removed in webkit
- if (!hasOngoingSubscription) {
- return;
- }
-
- pressDigit(digit);
- },
- });
- const { pressProps: onBackspacePress } = usePress({
- onPressStart() {
- timeoutRef.current = setTimeout(() => {
- removeDigit();
- intervalRef.current = setInterval(removeDigit, 75);
- }, 325);
- },
- onPressEnd() {
- if (timeoutRef.current) {
- clearTimeout(timeoutRef.current);
- timeoutRef.current = null;
- }
-
- if (intervalRef.current) {
- clearInterval(intervalRef.current);
- intervalRef.current = null;
- return;
- }
-
- removeDigit();
- },
- });
+ if (!hasPhoneNumber) {
+ return (
+ <>
+
+
+ >
+ );
+ }
if (!hasOngoingSubscription) {
return (
<>
-
-
-
- {phoneNumber}
-
-
-
-
-
-
+
>
);
}
@@ -117,24 +88,16 @@ export default function KeypadPage() {
{phoneNumber}
-
+
- setIsKeypadErrorModalOpen(false)} isOpen={isKeypadErrorModalOpen} />
>
);
}
-
-const phoneNumberAtom = atom("");
-const pressDigitAtom = atom(null, (get, set, digit: string) => {
- if (get(phoneNumberAtom).length > 17) {
- return;
- }
-
- if ("0123456789+#*".indexOf(digit) === -1) {
- return;
- }
-
- set(phoneNumberAtom, (prevState) => prevState + digit);
-});
-const longPressDigitAtom = atom(null, (get, set, replaceWith: string) => {
- if (get(phoneNumberAtom).length > 17) {
- return;
- }
-
- set(phoneNumberAtom, (prevState) => prevState.slice(0, -1) + replaceWith);
-});
-const pressBackspaceAtom = atom(null, (get, set) => {
- if (get(phoneNumberAtom).length === 0) {
- return;
- }
-
- set(phoneNumberAtom, (prevState) => prevState.slice(0, -1));
-});