Skip to content

Commit 0035fe2

Browse files
committed
Very UI started
1 parent 7df6fed commit 0035fe2

File tree

5 files changed

+311
-10
lines changed

5 files changed

+311
-10
lines changed

examples/with-sdk-js/src/app/global.css

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,3 @@
3131
BlinkMacSystemFont,
3232
sans-serif;
3333
}
34-
35-
button {
36-
border-radius: 8px;
37-
padding: 8px 16px;
38-
cursor: pointer;
39-
}

packages/react-wallet-kit/src/components/design/Svg.tsx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,80 @@ export function SolanaLogo(props: SVGProps) {
167167
</svg>
168168
);
169169
}
170+
171+
export function VerifyBorderSVG(props: SVGProps) {
172+
return (
173+
<svg
174+
xmlns="http://www.w3.org/2000/svg"
175+
width={88}
176+
height={88}
177+
fill="none"
178+
{...props}
179+
>
180+
<path
181+
fill="currentColor"
182+
d="M4 68v14a2 2 0 0 0 2 2h14v4H6a6 6 0 0 1-6-6V68h4Zm84 14a6 6 0 0 1-6 6H68v-4h14a2 2 0 0 0 2-2V68h4v14ZM82 0a6 6 0 0 1 6 6v14h-4V6a2 2 0 0 0-2-2H68V0h14ZM20 4H6a2 2 0 0 0-2 2v14H0V6a6 6 0 0 1 6-6h14v4Z"
183+
style={{
184+
fill: "currentColor",
185+
fillOpacity: 1,
186+
}}
187+
/>
188+
</svg>
189+
);
190+
}
191+
192+
export function VerifyInnerSVG(props: SVGProps) {
193+
return (
194+
<svg
195+
xmlns="http://www.w3.org/2000/svg"
196+
width={56}
197+
height={40}
198+
fill="none"
199+
{...props}
200+
>
201+
<rect
202+
width={56}
203+
height={4}
204+
fill="#868C95"
205+
rx={2}
206+
style={{
207+
fill: "#868c95",
208+
fillOpacity: 1,
209+
}}
210+
/>
211+
<rect
212+
width={46.421}
213+
height={4}
214+
y={12}
215+
fill="#868C95"
216+
rx={2}
217+
style={{
218+
fill: "#868c95",
219+
fillOpacity: 1,
220+
}}
221+
/>
222+
<rect
223+
width={56}
224+
height={4}
225+
y={24}
226+
fill="#868C95"
227+
rx={2}
228+
style={{
229+
fill: "#868c95",
230+
fillOpacity: 1,
231+
}}
232+
/>
233+
<rect
234+
width={46.421}
235+
height={4}
236+
y={36}
237+
fill="#868C95"
238+
rx={2}
239+
style={{
240+
fill: "#868c95",
241+
fillOpacity: 1,
242+
}}
243+
/>
244+
</svg>
245+
);
246+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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+
}

packages/react-wallet-kit/src/components/wallet/CreateWallet.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import clsx from "clsx";
2-
import { useModal } from "../../providers";
2+
import { useModal } from "../../providers/modal/Hook";
33
import type { HandleCreateWalletParams } from "../../types/method-types";
44
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
55
import { faWallet } from "@fortawesome/free-solid-svg-icons";
66
import { ActionButton } from "../design/Buttons";
77
import { parseCreateWalletInput, v1WalletAccountParams } from "@turnkey/core";
8+
import { VerifyPage } from "../design/Verify";
89

910
type CreateWalletProps = HandleCreateWalletParams & {
1011
onSuccess: (walletId: string) => void;
@@ -30,7 +31,7 @@ function DetailEntry(props: { name: string; value: string | number | object }) {
3031

3132
export function CreateWallet(props: CreateWalletProps) {
3233
const { walletName, mnemonicLength, accounts } = props;
33-
const { isMobile } = useModal();
34+
const { isMobile, pushPage } = useModal();
3435

3536
const parsedAccounts: v1WalletAccountParams[] = parseCreateWalletInput({
3637
accounts,
@@ -71,9 +72,22 @@ export function CreateWallet(props: CreateWalletProps) {
7172
</div>
7273
</div>
7374
</div>
74-
<div className="flex my-2 mt-0">
75+
<div className="flex my-2">
7576
<ActionButton
76-
onClick={() => {}}
77+
onClick={() => {
78+
pushPage({
79+
key: "verify",
80+
showTitle: false,
81+
preventBack: true,
82+
content: (
83+
<VerifyPage
84+
action={async () => {
85+
await new Promise((resolve) => setTimeout(resolve, 2000));
86+
}}
87+
/>
88+
),
89+
});
90+
}}
7791
loading={false}
7892
className="w-full md:max-w-md bg-primary-light dark:bg-primary-dark text-primary-text-light dark:text-primary-text-dark"
7993
>

packages/react-wallet-kit/src/index.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@
4949
transform: translateX(5px);
5050
}
5151
}
52+
53+
--animate-scan: scan 1000ms ease-in-out infinite;
54+
@keyframes scan {
55+
0% {
56+
transform: translateY(-100%);
57+
opacity: 0;
58+
}
59+
30% {
60+
transform: translateY(0%);
61+
opacity: 1;
62+
}
63+
80% {
64+
transform: translateY(0%);
65+
opacity: 1;
66+
}
67+
100% {
68+
transform: translateY(-100%);
69+
opacity: 0;
70+
}
71+
}
5272
}
5373

5474
@layer base {

0 commit comments

Comments
 (0)