|
| 1 | +import clsx from "clsx"; |
| 2 | +import { VerifyBorderSVG, VerifyInnerSVG } from "./Svg"; |
| 3 | +import { useModal } from "../../providers/modal/Hook"; |
| 4 | +import { useEffect, useRef, useState } from "react"; |
| 5 | +import { Transition, TransitionChild } from "@headlessui/react"; |
| 6 | +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
| 7 | +import { faCheck } from "@fortawesome/free-solid-svg-icons"; |
| 8 | + |
| 9 | +interface VerifyPageProps { |
| 10 | + action: () => Promise<void>; |
| 11 | +} |
| 12 | + |
| 13 | +export function VerifyPage(props: VerifyPageProps) { |
| 14 | + const { action } = props; |
| 15 | + const { isMobile } = useModal(); |
| 16 | + const [isCompleted, setIsCompleted] = useState(false); |
| 17 | + const [isTextPinging, setIsTextPinging] = useState(false); |
| 18 | + const [showWallets, setShowWallets] = useState(false); |
| 19 | + |
| 20 | + const handleComplete = () => { |
| 21 | + setIsCompleted(true); |
| 22 | + setTimeout(() => { |
| 23 | + setIsTextPinging(true); |
| 24 | + }, 500); |
| 25 | + setTimeout(() => { |
| 26 | + setIsTextPinging(false); |
| 27 | + }, 1500); |
| 28 | + setTimeout(() => { |
| 29 | + setShowWallets(true); |
| 30 | + }, 1700); |
| 31 | + }; |
| 32 | + |
| 33 | + return ( |
| 34 | + <div |
| 35 | + className={clsx( |
| 36 | + "flex flex-col items-center justify-center py-5 transition-all duration-300", |
| 37 | + isMobile ? "w-full" : "w-72", |
| 38 | + )} |
| 39 | + > |
| 40 | + <VerifyAnimation action={action} onComplete={handleComplete} /> |
| 41 | + <Transition |
| 42 | + as="div" |
| 43 | + show={isCompleted} |
| 44 | + className="w-full text-center text-lg font-semibold flex flex-col items-center justify-center mt-2 relative" |
| 45 | + enter="transition-all ease-out duration-200 delay-200" |
| 46 | + enterFrom="opacity-0 -translate-y-2" |
| 47 | + enterTo="opacity-100 translate-y-0" |
| 48 | + > |
| 49 | + <span>Verified by Turnkey!</span> |
| 50 | + {isTextPinging && ( |
| 51 | + <span className="absolute animate-ping text-icon-text-light dark:text-icon-background-dark"> |
| 52 | + Verified by Turnkey! |
| 53 | + </span> |
| 54 | + )} |
| 55 | + </Transition> |
| 56 | + |
| 57 | + <Transition |
| 58 | + as="div" |
| 59 | + show={showWallets} |
| 60 | + className="w-full flex flex-col items-center justify-center mt-2 relative" |
| 61 | + enter="transition-all ease-out duration-250 delay-100 " |
| 62 | + enterFrom="opacity-0 -translate-y-4" |
| 63 | + enterTo="opacity-100 translate-y-0" |
| 64 | + > |
| 65 | + <span className="text-icon-text-light dark:text-icon-text-dark text-center !p-0"> |
| 66 | + Your wallet was created and{" "} |
| 67 | + {/* TODO (Amir): link to prod page when ready */} |
| 68 | + <a |
| 69 | + className="underline cursor-pointer" |
| 70 | + target="_blank" |
| 71 | + rel="noreferrer" |
| 72 | + href="https://turnkey-0e7c1f5b-rno-verified.mintlify.app/security/turnkey-verified" |
| 73 | + > |
| 74 | + verified by Turnkey's Secure Enclaves! |
| 75 | + </a> |
| 76 | + </span> |
| 77 | + |
| 78 | + <span className="text-icon-text-light dark:text-icon-text-dark text-xs w-full items-start text-start mt-5"> |
| 79 | + Addresses: |
| 80 | + </span> |
| 81 | + <div className="w-full h-full overflow-y-scroll tk-scrollbar flex flex-col max-h-56 rounded-md border border-modal-background-dark/10 dark:border-modal-background-light/10 bg-icon-background-light dark:bg-icon-background-dark text-icon-text-light dark:text-icon-text-dark text-sm font-mono!"> |
| 82 | + <div className="gap-2 flex flex-col p-3"> |
| 83 | + <div className="font-mono!"> |
| 84 | + {"0xcc30cb0a3c72759755d81927c4df97804a3af5fb".slice(0, 10)}... |
| 85 | + {"0xCC30cb0A3C72759755D81927C4dF97804A3AF5fB".slice(-10)} |
| 86 | + </div> |
| 87 | + <div className="font-mono!"> |
| 88 | + {"AH6eMzHupvDWseNF6DMVLjum9jeeaUMMxnirojHqLEyR".slice(0, 10)}... |
| 89 | + {"AH6eMzHupvDWseNF6DMVLjum9jeeaUMMxnirojHqLEyR".slice(-10)} |
| 90 | + </div> |
| 91 | + </div> |
| 92 | + </div> |
| 93 | + </Transition> |
| 94 | + </div> |
| 95 | + ); |
| 96 | +} |
| 97 | + |
| 98 | +interface VerifyAnimationProps { |
| 99 | + action: () => Promise<void>; |
| 100 | + onComplete?: () => void; |
| 101 | +} |
| 102 | + |
| 103 | +export function VerifyAnimation(props: VerifyAnimationProps) { |
| 104 | + const { action } = props; |
| 105 | + const { closeModal } = useModal(); |
| 106 | + const hasRun = useRef(false); |
| 107 | + |
| 108 | + const [hasCompleted, setHasCompleted] = useState(false); |
| 109 | + |
| 110 | + const handleComplete = () => { |
| 111 | + setHasCompleted(true); |
| 112 | + if (props.onComplete) { |
| 113 | + props.onComplete(); |
| 114 | + } |
| 115 | + }; |
| 116 | + |
| 117 | + useEffect(() => { |
| 118 | + if (hasRun.current) return; |
| 119 | + hasRun.current = true; |
| 120 | + const runAction = async () => { |
| 121 | + if (action) { |
| 122 | + try { |
| 123 | + await action(); |
| 124 | + handleComplete(); |
| 125 | + } catch (error) { |
| 126 | + closeModal(); |
| 127 | + throw new Error(`${error}`); |
| 128 | + } |
| 129 | + } |
| 130 | + }; |
| 131 | + runAction(); |
| 132 | + }, []); |
| 133 | + |
| 134 | + return ( |
| 135 | + <div className="relative flex flex-col items-center justify-center size-[88px] overflow-hidden transition-all duration-300"> |
| 136 | + <Transition |
| 137 | + as="div" |
| 138 | + show={!hasCompleted} |
| 139 | + appear={true} |
| 140 | + enter="transition-opacity duration-150" |
| 141 | + enterFrom="opacity-0" |
| 142 | + enterTo="opacity-100" |
| 143 | + leave="transition-all duration-150 ease-out" |
| 144 | + leaveFrom="opacity-100 scale-100" |
| 145 | + leaveTo="opacity-0 scale-80" |
| 146 | + className="flex items-center justify-center" |
| 147 | + > |
| 148 | + <VerifyInnerSVG className="absolute" /> |
| 149 | + <div className="absolute size-full animate-scan flex items-center justify-center"> |
| 150 | + <div className="size-full bg-gradient-to-b from-primary-light/0 to-primary-light/80 dark:from-primary-dark/0 dark:to-primary-dark/100 rounded" /> |
| 151 | + <div className="w-full h-1 bg-primary-light dark:bg-primary-dark absolute bottom-0 rounded" /> |
| 152 | + </div> |
| 153 | + |
| 154 | + {/* Corner borders. This is kinda a hacky fix to cover the corners of the border SVG */} |
| 155 | + <div className="absolute inset-0 pointer-events-none z-40"> |
| 156 | + {/* Top-left */} |
| 157 | + <div className="absolute top-0 left-0 w-4 h-4 border-t-2 border-l-2 border-modal-background-light dark:border-modal-background-dark" /> |
| 158 | + {/* Top-right */} |
| 159 | + <div className="absolute top-0 right-0 w-4 h-4 border-t-2 border-r-2 border-modal-background-light dark:border-modal-background-dark" /> |
| 160 | + {/* Bottom-left */} |
| 161 | + <div className="absolute bottom-0 left-0 w-4 h-4 border-b-2 border-l-2 border-modal-background-light dark:border-modal-background-dark" /> |
| 162 | + {/* Bottom-right */} |
| 163 | + <div className="absolute bottom-0 right-0 w-4 h-4 border-b-2 border-r-2 border-modal-background-light dark:border-modal-background-dark" /> |
| 164 | + </div> |
| 165 | + |
| 166 | + <VerifyBorderSVG className="text-primary-light dark:text-primary-dark z-50" /> |
| 167 | + </Transition> |
| 168 | + |
| 169 | + <Transition |
| 170 | + as="div" |
| 171 | + show={hasCompleted} |
| 172 | + enter="transition-all duration-600 ease-in-out" |
| 173 | + enterFrom="opacity-0 scale-40" |
| 174 | + enterTo="opacity-100 scale-100" |
| 175 | + className=" absolute size-full flex flex-col items-center justify-center" |
| 176 | + > |
| 177 | + <div className="size-full border-2 border-primary-light dark:border-primary-dark text-primary-light dark:text-primary-dark rounded-full flex flex-col items-center justify-center box-border"> |
| 178 | + <TransitionChild |
| 179 | + as="div" |
| 180 | + enter="transition-all duration-200 delay-300 ease-out" |
| 181 | + enterFrom="opacity-0 scale-60" |
| 182 | + enterTo="opacity-100 scale-100" |
| 183 | + className="relative flex flex-col items-center justify-center" |
| 184 | + > |
| 185 | + <FontAwesomeIcon icon={faCheck} size="2x" /> |
| 186 | + <FontAwesomeIcon |
| 187 | + className="absolute animate-ping" |
| 188 | + icon={faCheck} |
| 189 | + size="2x" |
| 190 | + /> |
| 191 | + </TransitionChild> |
| 192 | + </div> |
| 193 | + </Transition> |
| 194 | + </div> |
| 195 | + ); |
| 196 | +} |
0 commit comments