From 98778c7a7bf581323e0efb2896b5332306863b9f Mon Sep 17 00:00:00 2001 From: m5r Date: Thu, 2 Sep 2021 05:54:14 +0800 Subject: [PATCH] refresh device token before it expires every hour --- app/phone-calls/hooks/use-device.tsx | 41 ++++++++++++++++--- app/phone-calls/hooks/use-make-call.ts | 6 +-- app/phone-calls/mutations/get-token.ts | 2 + .../pages/outgoing-call/[recipient].tsx | 14 +++---- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/app/phone-calls/hooks/use-device.tsx b/app/phone-calls/hooks/use-device.tsx index 84c1b7c..4c8a099 100644 --- a/app/phone-calls/hooks/use-device.tsx +++ b/app/phone-calls/hooks/use-device.tsx @@ -1,14 +1,36 @@ -import { useEffect } from "react"; +import { useCallback, useEffect } from "react"; import { useMutation } from "blitz"; import type { TwilioError } from "@twilio/voice-sdk"; import { Call, Device } from "@twilio/voice-sdk"; import { atom, useAtom } from "jotai"; -import getToken from "../mutations/get-token"; +import getToken, { ttl } from "../mutations/get-token"; +import appLogger from "../../../integrations/logger"; + +const logger = appLogger.child({ module: "use-device" }); export default function useDevice() { const [device, setDevice] = useAtom(deviceAtom); const [getTokenMutation] = useMutation(getToken); + const refreshToken = useCallback(async () => { + if (!device) { + logger.error("Tried refreshing accessToken for an uninitialized device"); + return; + } + + const token = await getTokenMutation(); + device.updateToken(token); + }, [device, getTokenMutation]); + const isDeviceReady = device?.state === Device.State.Registered; + + useEffect(() => { + if (!isDeviceReady) { + return; + } + + const intervalId = setInterval(refreshToken, ttl - 30); + return () => clearInterval(intervalId); + }, [isDeviceReady, refreshToken]); useEffect(() => { (async () => { @@ -22,14 +44,16 @@ export default function useDevice() { device.register(); setDevice(device); })(); - }, [getTokenMutation]); + }, [getTokenMutation, setDevice]); useEffect(() => { if (!device) { return; } - (window as any).device = device; + console.log("ok"); + // @ts-ignore + window.device = device; device.on("error", onDeviceError); device.on("incoming", onDeviceIncoming); @@ -39,7 +63,14 @@ export default function useDevice() { }; }, [device]); - return device; + // @ts-ignore + window.refreshToken = refreshToken; + + return { + device, + isDeviceReady, + refreshToken, + }; function onDeviceError(error: TwilioError.TwilioError, call?: Call) { // TODO gracefully handle errors: possibly hang up the call, redirect to keypad diff --git a/app/phone-calls/hooks/use-make-call.ts b/app/phone-calls/hooks/use-make-call.ts index adc8f1a..2b50391 100644 --- a/app/phone-calls/hooks/use-make-call.ts +++ b/app/phone-calls/hooks/use-make-call.ts @@ -12,7 +12,7 @@ type Params = { export default function useMakeCall({ recipient, onHangUp }: Params) { const [outgoingConnection, setOutgoingConnection] = useState(null); const [state, setState] = useState("initial"); - const device = useDevice(); + const { device, isDeviceReady } = useDevice(); const router = useRouter(); return { @@ -23,8 +23,8 @@ export default function useMakeCall({ recipient, onHangUp }: Params) { }; async function makeCall() { - if (!device) { - console.error("no device initialized, can't make the call"); + if (!device || !isDeviceReady) { + console.error("device is not ready yet, can't make the call"); return; } diff --git a/app/phone-calls/mutations/get-token.ts b/app/phone-calls/mutations/get-token.ts index 926b9c8..cd0d1e2 100644 --- a/app/phone-calls/mutations/get-token.ts +++ b/app/phone-calls/mutations/get-token.ts @@ -4,6 +4,8 @@ import Twilio from "twilio"; import db from "db"; import getCurrentPhoneNumber from "../../phone-numbers/queries/get-current-phone-number"; +export const ttl = 3600; + export default resolver.pipe(resolver.authorize(), async (_ = null, context) => { const phoneNumber = await getCurrentPhoneNumber({}, context); if (!phoneNumber) { diff --git a/app/phone-calls/pages/outgoing-call/[recipient].tsx b/app/phone-calls/pages/outgoing-call/[recipient].tsx index 3b08f07..dc15b5d 100644 --- a/app/phone-calls/pages/outgoing-call/[recipient].tsx +++ b/app/phone-calls/pages/outgoing-call/[recipient].tsx @@ -1,6 +1,6 @@ import type { BlitzPage } from "blitz"; import { Routes, useRouter } from "blitz"; -import { useEffect, useMemo } from "react"; +import { useCallback, useEffect } from "react"; import { atom, useAtom } from "jotai"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPhoneAlt as faPhone } from "@fortawesome/pro-solid-svg-icons"; @@ -15,13 +15,13 @@ const OutgoingCall: BlitzPage = () => { useRequireOnboarding(); const [phoneNumber, setPhoneNumber] = useAtom(phoneNumberAtom); const router = useRouter(); - const device = useDevice(); + const { isDeviceReady } = useDevice(); const recipient = decodeURIComponent(router.params.recipient); - const onHangUp = useMemo(() => () => setPhoneNumber(""), [setPhoneNumber]); + const onHangUp = useCallback(() => setPhoneNumber(""), [setPhoneNumber]); const call = useMakeCall({ recipient, onHangUp }); const pressDigit = useAtom(pressDigitAtom)[1]; - const onDigitPressProps = useMemo( - () => (digit: string) => ({ + const onDigitPressProps = useCallback( + (digit: string) => ({ onPress() { pressDigit(digit); @@ -32,10 +32,10 @@ const OutgoingCall: BlitzPage = () => { ); useEffect(() => { - if (device !== null) { + if (isDeviceReady) { call.makeCall(); } - }, [device]); + }, [isDeviceReady]); return (