@getpara/cli 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/README.md +97 -0
  2. package/dist/api/client.d.ts +49 -0
  3. package/dist/api/client.d.ts.map +1 -0
  4. package/dist/api/client.js +247 -0
  5. package/dist/api/errors.d.ts +9 -0
  6. package/dist/api/errors.d.ts.map +1 -0
  7. package/dist/api/errors.js +29 -0
  8. package/dist/api/types.d.ts +155 -0
  9. package/dist/api/types.d.ts.map +1 -0
  10. package/dist/api/types.js +58 -0
  11. package/dist/auth/polling-flow.d.ts +7 -0
  12. package/dist/auth/polling-flow.d.ts.map +1 -0
  13. package/dist/auth/polling-flow.js +94 -0
  14. package/dist/auth/session-manager.d.ts +7 -0
  15. package/dist/auth/session-manager.d.ts.map +1 -0
  16. package/dist/auth/session-manager.js +42 -0
  17. package/dist/cli.d.ts +16 -0
  18. package/dist/cli.d.ts.map +1 -0
  19. package/dist/cli.js +151 -0
  20. package/dist/commands/auth/index.d.ts +3 -0
  21. package/dist/commands/auth/index.d.ts.map +1 -0
  22. package/dist/commands/auth/index.js +18 -0
  23. package/dist/commands/auth/login.d.ts +3 -0
  24. package/dist/commands/auth/login.d.ts.map +1 -0
  25. package/dist/commands/auth/login.js +70 -0
  26. package/dist/commands/auth/logout.d.ts +3 -0
  27. package/dist/commands/auth/logout.d.ts.map +1 -0
  28. package/dist/commands/auth/logout.js +45 -0
  29. package/dist/commands/auth/status.d.ts +3 -0
  30. package/dist/commands/auth/status.d.ts.map +1 -0
  31. package/dist/commands/auth/status.js +54 -0
  32. package/dist/commands/config.d.ts +3 -0
  33. package/dist/commands/config.d.ts.map +1 -0
  34. package/dist/commands/config.js +122 -0
  35. package/dist/commands/create.d.ts +3 -0
  36. package/dist/commands/create.d.ts.map +1 -0
  37. package/dist/commands/create.js +587 -0
  38. package/dist/commands/doctor.d.ts +3 -0
  39. package/dist/commands/doctor.d.ts.map +1 -0
  40. package/dist/commands/doctor.js +67 -0
  41. package/dist/commands/init.d.ts +3 -0
  42. package/dist/commands/init.d.ts.map +1 -0
  43. package/dist/commands/init.js +45 -0
  44. package/dist/commands/keys/archive.d.ts +3 -0
  45. package/dist/commands/keys/archive.d.ts.map +1 -0
  46. package/dist/commands/keys/archive.js +49 -0
  47. package/dist/commands/keys/config/branding.d.ts +23 -0
  48. package/dist/commands/keys/config/branding.d.ts.map +1 -0
  49. package/dist/commands/keys/config/branding.js +246 -0
  50. package/dist/commands/keys/config/categories.d.ts +15 -0
  51. package/dist/commands/keys/config/categories.d.ts.map +1 -0
  52. package/dist/commands/keys/config/categories.js +67 -0
  53. package/dist/commands/keys/config/index.d.ts +3 -0
  54. package/dist/commands/keys/config/index.d.ts.map +1 -0
  55. package/dist/commands/keys/config/index.js +56 -0
  56. package/dist/commands/keys/config/ramps.d.ts +18 -0
  57. package/dist/commands/keys/config/ramps.d.ts.map +1 -0
  58. package/dist/commands/keys/config/ramps.js +185 -0
  59. package/dist/commands/keys/config/security.d.ts +15 -0
  60. package/dist/commands/keys/config/security.d.ts.map +1 -0
  61. package/dist/commands/keys/config/security.js +212 -0
  62. package/dist/commands/keys/config/setup.d.ts +16 -0
  63. package/dist/commands/keys/config/setup.d.ts.map +1 -0
  64. package/dist/commands/keys/config/setup.js +235 -0
  65. package/dist/commands/keys/config/webhooks.d.ts +18 -0
  66. package/dist/commands/keys/config/webhooks.d.ts.map +1 -0
  67. package/dist/commands/keys/config/webhooks.js +279 -0
  68. package/dist/commands/keys/create.d.ts +3 -0
  69. package/dist/commands/keys/create.d.ts.map +1 -0
  70. package/dist/commands/keys/create.js +64 -0
  71. package/dist/commands/keys/get.d.ts +3 -0
  72. package/dist/commands/keys/get.d.ts.map +1 -0
  73. package/dist/commands/keys/get.js +91 -0
  74. package/dist/commands/keys/index.d.ts +3 -0
  75. package/dist/commands/keys/index.d.ts.map +1 -0
  76. package/dist/commands/keys/index.js +22 -0
  77. package/dist/commands/keys/list.d.ts +3 -0
  78. package/dist/commands/keys/list.d.ts.map +1 -0
  79. package/dist/commands/keys/list.js +66 -0
  80. package/dist/commands/keys/rotate.d.ts +3 -0
  81. package/dist/commands/keys/rotate.d.ts.map +1 -0
  82. package/dist/commands/keys/rotate.js +59 -0
  83. package/dist/commands/orgs/index.d.ts +3 -0
  84. package/dist/commands/orgs/index.d.ts.map +1 -0
  85. package/dist/commands/orgs/index.js +15 -0
  86. package/dist/commands/orgs/list.d.ts +3 -0
  87. package/dist/commands/orgs/list.d.ts.map +1 -0
  88. package/dist/commands/orgs/list.js +53 -0
  89. package/dist/commands/orgs/switch.d.ts +3 -0
  90. package/dist/commands/orgs/switch.d.ts.map +1 -0
  91. package/dist/commands/orgs/switch.js +61 -0
  92. package/dist/commands/projects/archive.d.ts +3 -0
  93. package/dist/commands/projects/archive.d.ts.map +1 -0
  94. package/dist/commands/projects/archive.js +51 -0
  95. package/dist/commands/projects/create.d.ts +3 -0
  96. package/dist/commands/projects/create.d.ts.map +1 -0
  97. package/dist/commands/projects/create.js +48 -0
  98. package/dist/commands/projects/index.d.ts +3 -0
  99. package/dist/commands/projects/index.d.ts.map +1 -0
  100. package/dist/commands/projects/index.js +25 -0
  101. package/dist/commands/projects/list.d.ts +3 -0
  102. package/dist/commands/projects/list.d.ts.map +1 -0
  103. package/dist/commands/projects/list.js +52 -0
  104. package/dist/commands/projects/restore.d.ts +3 -0
  105. package/dist/commands/projects/restore.d.ts.map +1 -0
  106. package/dist/commands/projects/restore.js +36 -0
  107. package/dist/commands/projects/switch.d.ts +3 -0
  108. package/dist/commands/projects/switch.d.ts.map +1 -0
  109. package/dist/commands/projects/switch.js +57 -0
  110. package/dist/commands/projects/update.d.ts +3 -0
  111. package/dist/commands/projects/update.d.ts.map +1 -0
  112. package/dist/commands/projects/update.js +67 -0
  113. package/dist/commands/whoami.d.ts +3 -0
  114. package/dist/commands/whoami.d.ts.map +1 -0
  115. package/dist/commands/whoami.js +61 -0
  116. package/dist/config/config-manager.d.ts +3 -0
  117. package/dist/config/config-manager.d.ts.map +1 -0
  118. package/dist/config/config-manager.js +32 -0
  119. package/dist/config/config-store.d.ts +10 -0
  120. package/dist/config/config-store.d.ts.map +1 -0
  121. package/dist/config/config-store.js +37 -0
  122. package/dist/config/credential-store.d.ts +12 -0
  123. package/dist/config/credential-store.d.ts.map +1 -0
  124. package/dist/config/credential-store.js +94 -0
  125. package/dist/config/paths.d.ts +5 -0
  126. package/dist/config/paths.d.ts.map +1 -0
  127. package/dist/config/paths.js +29 -0
  128. package/dist/config/project-config.d.ts +8 -0
  129. package/dist/config/project-config.d.ts.map +1 -0
  130. package/dist/config/project-config.js +41 -0
  131. package/dist/core/constants.d.ts +27 -0
  132. package/dist/core/constants.d.ts.map +1 -0
  133. package/dist/core/constants.js +60 -0
  134. package/dist/core/error-handler.d.ts +24 -0
  135. package/dist/core/error-handler.d.ts.map +1 -0
  136. package/dist/core/error-handler.js +83 -0
  137. package/dist/core/types.d.ts +21 -0
  138. package/dist/core/types.d.ts.map +1 -0
  139. package/dist/core/types.js +0 -0
  140. package/dist/core/update-check.d.ts +6 -0
  141. package/dist/core/update-check.d.ts.map +1 -0
  142. package/dist/core/update-check.js +78 -0
  143. package/dist/diagnostics/checks/chain-dependencies.d.ts +2 -0
  144. package/dist/diagnostics/checks/chain-dependencies.d.ts.map +1 -0
  145. package/dist/diagnostics/checks/chain-dependencies.js +130 -0
  146. package/dist/diagnostics/checks/css-import.d.ts +2 -0
  147. package/dist/diagnostics/checks/css-import.d.ts.map +1 -0
  148. package/dist/diagnostics/checks/css-import.js +57 -0
  149. package/dist/diagnostics/checks/deprecated-packages.d.ts +2 -0
  150. package/dist/diagnostics/checks/deprecated-packages.d.ts.map +1 -0
  151. package/dist/diagnostics/checks/deprecated-packages.js +93 -0
  152. package/dist/diagnostics/checks/env-api-key.d.ts +2 -0
  153. package/dist/diagnostics/checks/env-api-key.d.ts.map +1 -0
  154. package/dist/diagnostics/checks/env-api-key.js +75 -0
  155. package/dist/diagnostics/checks/env-var-prefix.d.ts +2 -0
  156. package/dist/diagnostics/checks/env-var-prefix.d.ts.map +1 -0
  157. package/dist/diagnostics/checks/env-var-prefix.js +52 -0
  158. package/dist/diagnostics/checks/index.d.ts +13 -0
  159. package/dist/diagnostics/checks/index.d.ts.map +1 -0
  160. package/dist/diagnostics/checks/index.js +32 -0
  161. package/dist/diagnostics/checks/para-provider.d.ts +2 -0
  162. package/dist/diagnostics/checks/para-provider.d.ts.map +1 -0
  163. package/dist/diagnostics/checks/para-provider.js +42 -0
  164. package/dist/diagnostics/checks/query-client.d.ts +2 -0
  165. package/dist/diagnostics/checks/query-client.d.ts.map +1 -0
  166. package/dist/diagnostics/checks/query-client.js +58 -0
  167. package/dist/diagnostics/checks/use-client-directive.d.ts +2 -0
  168. package/dist/diagnostics/checks/use-client-directive.d.ts.map +1 -0
  169. package/dist/diagnostics/checks/use-client-directive.js +81 -0
  170. package/dist/diagnostics/checks/version-consistency.d.ts +2 -0
  171. package/dist/diagnostics/checks/version-consistency.d.ts.map +1 -0
  172. package/dist/diagnostics/checks/version-consistency.js +93 -0
  173. package/dist/diagnostics/context.d.ts +3 -0
  174. package/dist/diagnostics/context.d.ts.map +1 -0
  175. package/dist/diagnostics/context.js +56 -0
  176. package/dist/diagnostics/detectors/framework.d.ts +5 -0
  177. package/dist/diagnostics/detectors/framework.d.ts.map +1 -0
  178. package/dist/diagnostics/detectors/framework.js +73 -0
  179. package/dist/diagnostics/detectors/package-manager.d.ts +3 -0
  180. package/dist/diagnostics/detectors/package-manager.d.ts.map +1 -0
  181. package/dist/diagnostics/detectors/package-manager.js +17 -0
  182. package/dist/diagnostics/detectors/sdk.d.ts +7 -0
  183. package/dist/diagnostics/detectors/sdk.d.ts.map +1 -0
  184. package/dist/diagnostics/detectors/sdk.js +48 -0
  185. package/dist/diagnostics/runner.d.ts +3 -0
  186. package/dist/diagnostics/runner.d.ts.map +1 -0
  187. package/dist/diagnostics/runner.js +44 -0
  188. package/dist/diagnostics/types.d.ts +70 -0
  189. package/dist/diagnostics/types.d.ts.map +1 -0
  190. package/dist/diagnostics/types.js +14 -0
  191. package/dist/diagnostics/utils/code-search.d.ts +10 -0
  192. package/dist/diagnostics/utils/code-search.d.ts.map +1 -0
  193. package/dist/diagnostics/utils/code-search.js +98 -0
  194. package/dist/diagnostics/utils/file-system.d.ts +7 -0
  195. package/dist/diagnostics/utils/file-system.d.ts.map +1 -0
  196. package/dist/diagnostics/utils/file-system.js +72 -0
  197. package/dist/diagnostics/utils/package-json.d.ts +12 -0
  198. package/dist/diagnostics/utils/package-json.d.ts.map +1 -0
  199. package/dist/diagnostics/utils/package-json.js +51 -0
  200. package/dist/index.d.ts +2 -0
  201. package/dist/index.d.ts.map +1 -0
  202. package/dist/index.js +20 -0
  203. package/dist/output/formatter.d.ts +25 -0
  204. package/dist/output/formatter.d.ts.map +1 -0
  205. package/dist/output/formatter.js +76 -0
  206. package/dist/output/mask.d.ts +2 -0
  207. package/dist/output/mask.d.ts.map +1 -0
  208. package/dist/output/mask.js +15 -0
  209. package/dist/output/prompts.d.ts +25 -0
  210. package/dist/output/prompts.d.ts.map +1 -0
  211. package/dist/output/prompts.js +86 -0
  212. package/dist/output/spinner.d.ts +6 -0
  213. package/dist/output/spinner.d.ts.map +1 -0
  214. package/dist/output/spinner.js +7 -0
  215. package/dist/output/table.d.ts +9 -0
  216. package/dist/output/table.d.ts.map +1 -0
  217. package/dist/output/table.js +21 -0
  218. package/dist/scaffolding/scaffolder.d.ts +3 -0
  219. package/dist/scaffolding/scaffolder.d.ts.map +1 -0
  220. package/dist/scaffolding/scaffolder.js +78 -0
  221. package/dist/scaffolding/strategies/expo-template.d.ts +16 -0
  222. package/dist/scaffolding/strategies/expo-template.d.ts.map +1 -0
  223. package/dist/scaffolding/strategies/expo-template.js +160 -0
  224. package/dist/scaffolding/strategies/index.d.ts +5 -0
  225. package/dist/scaffolding/strategies/index.d.ts.map +1 -0
  226. package/dist/scaffolding/strategies/index.js +25 -0
  227. package/dist/scaffolding/strategies/nextjs-template.d.ts +12 -0
  228. package/dist/scaffolding/strategies/nextjs-template.d.ts.map +1 -0
  229. package/dist/scaffolding/strategies/nextjs-template.js +122 -0
  230. package/dist/scaffolding/template-renderer.d.ts +11 -0
  231. package/dist/scaffolding/template-renderer.d.ts.map +1 -0
  232. package/dist/scaffolding/template-renderer.js +99 -0
  233. package/dist/scaffolding/types.d.ts +69 -0
  234. package/dist/scaffolding/types.d.ts.map +1 -0
  235. package/dist/scaffolding/types.js +21 -0
  236. package/dist/scaffolding/utils/detect-package-manager.d.ts +23 -0
  237. package/dist/scaffolding/utils/detect-package-manager.d.ts.map +1 -0
  238. package/dist/scaffolding/utils/detect-package-manager.js +57 -0
  239. package/dist/scaffolding/utils/fs.d.ts +11 -0
  240. package/dist/scaffolding/utils/fs.d.ts.map +1 -0
  241. package/dist/scaffolding/utils/fs.js +45 -0
  242. package/dist/scaffolding/utils/resolve-para-version.d.ts +7 -0
  243. package/dist/scaffolding/utils/resolve-para-version.d.ts.map +1 -0
  244. package/dist/scaffolding/utils/resolve-para-version.js +21 -0
  245. package/dist/validation/auth-methods.d.ts +14 -0
  246. package/dist/validation/auth-methods.d.ts.map +1 -0
  247. package/dist/validation/auth-methods.js +23 -0
  248. package/dist/validation/cidr.d.ts +11 -0
  249. package/dist/validation/cidr.d.ts.map +1 -0
  250. package/dist/validation/cidr.js +35 -0
  251. package/dist/validation/hex-color.d.ts +7 -0
  252. package/dist/validation/hex-color.d.ts.map +1 -0
  253. package/dist/validation/hex-color.js +10 -0
  254. package/dist/validation/index.d.ts +9 -0
  255. package/dist/validation/index.d.ts.map +1 -0
  256. package/dist/validation/index.js +49 -0
  257. package/dist/validation/native-passkey.d.ts +31 -0
  258. package/dist/validation/native-passkey.d.ts.map +1 -0
  259. package/dist/validation/native-passkey.js +45 -0
  260. package/dist/validation/session-length.d.ts +18 -0
  261. package/dist/validation/session-length.d.ts.map +1 -0
  262. package/dist/validation/session-length.js +30 -0
  263. package/dist/validation/url.d.ts +26 -0
  264. package/dist/validation/url.d.ts.map +1 -0
  265. package/dist/validation/url.js +50 -0
  266. package/dist/validation/wallet-types.d.ts +14 -0
  267. package/dist/validation/wallet-types.d.ts.map +1 -0
  268. package/dist/validation/wallet-types.js +22 -0
  269. package/dist/validation/webhook.d.ts +4 -0
  270. package/dist/validation/webhook.d.ts.map +1 -0
  271. package/dist/validation/webhook.js +33 -0
  272. package/package.json +60 -0
  273. package/templates/expo/_env.example +3 -0
  274. package/templates/expo/_gitignore +48 -0
  275. package/templates/expo/_yarnrc.yml +1 -0
  276. package/templates/expo/app/(auth)/_layout.tsx +12 -0
  277. package/templates/expo/app/(auth)/index.tsx.template +86 -0
  278. package/templates/expo/app/(tabs)/_layout.tsx +16 -0
  279. package/templates/expo/app/(tabs)/index.tsx +112 -0
  280. package/templates/expo/app/(tabs)/send.tsx +111 -0
  281. package/templates/expo/app/_layout.tsx +17 -0
  282. package/templates/expo/app/index.tsx +22 -0
  283. package/templates/expo/app.json.template +32 -0
  284. package/templates/expo/assets/adaptive-icon.png +0 -0
  285. package/templates/expo/assets/favicon.png +0 -0
  286. package/templates/expo/assets/icon.png +0 -0
  287. package/templates/expo/assets/splash.png +0 -0
  288. package/templates/expo/babel.config.cjs +12 -0
  289. package/templates/expo/components/features/AuthForm.tsx.template +138 -0
  290. package/templates/expo/components/features/OAuthButtons.tsx.template +27 -0
  291. package/templates/expo/components/features/index.ts.template +4 -0
  292. package/templates/expo/components/ui/Button.tsx +58 -0
  293. package/templates/expo/components/ui/Card.tsx +11 -0
  294. package/templates/expo/components/ui/Divider.tsx +19 -0
  295. package/templates/expo/components/ui/Input.tsx +23 -0
  296. package/templates/expo/components/ui/WalletCard.tsx +44 -0
  297. package/templates/expo/components/ui/index.ts +5 -0
  298. package/templates/expo/eslint.config.cjs +15 -0
  299. package/templates/expo/global.css +3 -0
  300. package/templates/expo/hooks/useOneClickLogin.ts.template +161 -0
  301. package/templates/expo/hooks/useViemClient.ts +118 -0
  302. package/templates/expo/hooks/useWallets.ts +52 -0
  303. package/templates/expo/index.js +2 -0
  304. package/templates/expo/lib/auth.ts +54 -0
  305. package/templates/expo/lib/constants.ts.template +2 -0
  306. package/templates/expo/lib/para.ts +14 -0
  307. package/templates/expo/metro.config.cjs +14 -0
  308. package/templates/expo/nativewind-env.d.ts +2 -0
  309. package/templates/expo/prettier.config.cjs +10 -0
  310. package/templates/expo/providers/ParaProvider.tsx +140 -0
  311. package/templates/expo/tailwind.config.cjs +23 -0
  312. package/templates/expo/tsconfig.json +11 -0
  313. package/templates/expo/types/index.ts +28 -0
  314. package/templates/nextjs/README.md +69 -0
  315. package/templates/nextjs/_env.example +8 -0
  316. package/templates/nextjs/_gitignore +36 -0
  317. package/templates/nextjs/_yarnrc.yml +1 -0
  318. package/templates/nextjs/eslint.config.mjs +10 -0
  319. package/templates/nextjs/next.config.ts +5 -0
  320. package/templates/nextjs/postcss.config.mjs +7 -0
  321. package/templates/nextjs/public/para.svg +3 -0
  322. package/templates/nextjs/src/app/layout.tsx +30 -0
  323. package/templates/nextjs/src/app/page.tsx +40 -0
  324. package/templates/nextjs/src/components/ParaProvider.tsx +116 -0
  325. package/templates/nextjs/src/components/layout/Header.tsx +44 -0
  326. package/templates/nextjs/src/components/ui/ConnectCard.tsx +24 -0
  327. package/templates/nextjs/src/components/ui/SignMessage.tsx +53 -0
  328. package/templates/nextjs/src/components/ui/WalletInfo.tsx +22 -0
  329. package/templates/nextjs/src/hooks/useSignHelloWorld.ts +23 -0
  330. package/templates/nextjs/src/styles/globals.css +1 -0
  331. package/templates/nextjs/tsconfig.json +27 -0
@@ -0,0 +1,138 @@
1
+ import { useState } from 'react';
2
+ import { View, Text, Alert } from 'react-native';
3
+
4
+ import { Button, Input } from '@/components/ui';
5
+ // @if:hasEmail
6
+ import { isValidEmail } from '@/lib/auth';
7
+ // @endif
8
+ // @if:hasPhone
9
+ import { isValidPhone } from '@/lib/auth';
10
+ // @endif
11
+
12
+ // @if:hasEmail && hasPhone
13
+ type AuthMethod = 'email' | 'phone';
14
+ // @endif
15
+
16
+ interface AuthFormProps {
17
+ onSubmit: (value: string, method: 'email' | 'phone') => Promise<void>;
18
+ loading?: boolean;
19
+ error?: string | null;
20
+ }
21
+
22
+ export function AuthForm({ onSubmit, loading = false, error }: AuthFormProps) {
23
+ // @if:hasEmail && hasPhone
24
+ const [method, setMethod] = useState<AuthMethod>('email');
25
+ // @endif
26
+ // @if:hasEmail
27
+ const [email, setEmail] = useState('');
28
+ // @endif
29
+ // @if:hasPhone
30
+ const [phone, setPhone] = useState('');
31
+ // @endif
32
+
33
+ const handleSubmit = async () => {
34
+ // @if:hasEmail && hasPhone
35
+ if (method === 'email') {
36
+ if (!isValidEmail(email)) {
37
+ Alert.alert('Invalid Email', 'Please enter a valid email address');
38
+ return;
39
+ }
40
+ await onSubmit(email, 'email');
41
+ } else {
42
+ if (!isValidPhone(phone)) {
43
+ Alert.alert('Invalid Phone', 'Please enter a valid phone number');
44
+ return;
45
+ }
46
+ await onSubmit(phone, 'phone');
47
+ }
48
+ // @endif
49
+ // @if:hasEmail && !hasPhone
50
+ if (!isValidEmail(email)) {
51
+ Alert.alert('Invalid Email', 'Please enter a valid email address');
52
+ return;
53
+ }
54
+ await onSubmit(email, 'email');
55
+ // @endif
56
+ // @if:hasPhone && !hasEmail
57
+ if (!isValidPhone(phone)) {
58
+ Alert.alert('Invalid Phone', 'Please enter a valid phone number');
59
+ return;
60
+ }
61
+ await onSubmit(phone, 'phone');
62
+ // @endif
63
+ };
64
+
65
+ return (
66
+ <View>
67
+ // @if:hasEmail && hasPhone
68
+ <View className="mb-4 flex-row rounded-xl bg-gray-100 p-1">
69
+ <Button
70
+ title="Email"
71
+ variant={method === 'email' ? 'primary' : 'secondary'}
72
+ onPress={() => setMethod('email')}
73
+ fullWidth={false}
74
+ />
75
+ <Button
76
+ title="Phone"
77
+ variant={method === 'phone' ? 'primary' : 'secondary'}
78
+ onPress={() => setMethod('phone')}
79
+ fullWidth={false}
80
+ />
81
+ </View>
82
+ // @endif
83
+
84
+ // @if:hasEmail && hasPhone
85
+ {method === 'email' ? (
86
+ <Input
87
+ label="Email Address"
88
+ placeholder="you@example.com"
89
+ value={email}
90
+ onChangeText={setEmail}
91
+ keyboardType="email-address"
92
+ autoCapitalize="none"
93
+ autoCorrect={false}
94
+ />
95
+ ) : (
96
+ <Input
97
+ label="Phone Number"
98
+ placeholder="+1 (555) 000-0000"
99
+ value={phone}
100
+ onChangeText={setPhone}
101
+ keyboardType="phone-pad"
102
+ />
103
+ )}
104
+ // @endif
105
+ // @if:hasEmail && !hasPhone
106
+ <Input
107
+ label="Email Address"
108
+ placeholder="you@example.com"
109
+ value={email}
110
+ onChangeText={setEmail}
111
+ keyboardType="email-address"
112
+ autoCapitalize="none"
113
+ autoCorrect={false}
114
+ />
115
+ // @endif
116
+ // @if:hasPhone && !hasEmail
117
+ <Input
118
+ label="Phone Number"
119
+ placeholder="+1 (555) 000-0000"
120
+ value={phone}
121
+ onChangeText={setPhone}
122
+ keyboardType="phone-pad"
123
+ />
124
+ // @endif
125
+
126
+ {error && (
127
+ <Text className="mb-4 text-center text-sm text-red-500">{error}</Text>
128
+ )}
129
+
130
+ <Button
131
+ title={loading ? 'Signing in...' : 'Continue'}
132
+ onPress={handleSubmit}
133
+ disabled={loading}
134
+ loading={loading}
135
+ />
136
+ </View>
137
+ );
138
+ }
@@ -0,0 +1,27 @@
1
+ // @if:hasOAuth
2
+ import { View } from 'react-native';
3
+ import { Ionicons } from '@expo/vector-icons';
4
+
5
+ import { Button } from '@/components/ui';
6
+
7
+ interface OAuthButtonsProps {
8
+ onGooglePress?: () => void;
9
+ disabled?: boolean;
10
+ }
11
+
12
+ export function OAuthButtons({ onGooglePress, disabled = false }: OAuthButtonsProps) {
13
+ return (
14
+ <View className="gap-3">
15
+ {onGooglePress && (
16
+ <Button
17
+ title="Continue with Google"
18
+ variant="outline"
19
+ onPress={onGooglePress}
20
+ disabled={disabled}
21
+ icon={<Ionicons name="logo-google" size={20} color="#111" />}
22
+ />
23
+ )}
24
+ </View>
25
+ );
26
+ }
27
+ // @endif
@@ -0,0 +1,4 @@
1
+ export { AuthForm } from './AuthForm';
2
+ // @if:hasOAuth
3
+ export { OAuthButtons } from './OAuthButtons';
4
+ // @endif
@@ -0,0 +1,58 @@
1
+ import { TouchableOpacity, Text, View, ActivityIndicator } from 'react-native';
2
+ import type { ReactNode } from 'react';
3
+
4
+ interface ButtonProps {
5
+ title: string;
6
+ onPress: () => void;
7
+ variant?: 'primary' | 'secondary' | 'danger' | 'outline';
8
+ disabled?: boolean;
9
+ loading?: boolean;
10
+ icon?: ReactNode;
11
+ fullWidth?: boolean;
12
+ }
13
+
14
+ export function Button({
15
+ title,
16
+ onPress,
17
+ variant = 'primary',
18
+ disabled = false,
19
+ loading = false,
20
+ icon,
21
+ fullWidth = true,
22
+ }: ButtonProps) {
23
+ const baseClasses = 'flex-row items-center justify-center rounded-xl py-4 px-6';
24
+ const widthClass = fullWidth ? 'w-full' : '';
25
+
26
+ const variantClasses = {
27
+ primary: 'bg-gray-900',
28
+ secondary: 'bg-gray-100',
29
+ danger: 'bg-red-500',
30
+ outline: 'bg-transparent border border-gray-300',
31
+ };
32
+
33
+ const textClasses = {
34
+ primary: 'text-white',
35
+ secondary: 'text-gray-900',
36
+ danger: 'text-white',
37
+ outline: 'text-gray-900',
38
+ };
39
+
40
+ const disabledClasses = disabled || loading ? 'opacity-50' : '';
41
+
42
+ return (
43
+ <TouchableOpacity
44
+ onPress={onPress}
45
+ disabled={disabled || loading}
46
+ className={`${baseClasses} ${widthClass} ${variantClasses[variant]} ${disabledClasses}`}
47
+ activeOpacity={0.8}>
48
+ {loading ? (
49
+ <ActivityIndicator color={variant === 'outline' || variant === 'secondary' ? '#111' : '#fff'} />
50
+ ) : (
51
+ <View className="flex-row items-center gap-2">
52
+ {icon}
53
+ <Text className={`text-base font-semibold ${textClasses[variant]}`}>{title}</Text>
54
+ </View>
55
+ )}
56
+ </TouchableOpacity>
57
+ );
58
+ }
@@ -0,0 +1,11 @@
1
+ import { View } from 'react-native';
2
+ import type { ReactNode } from 'react';
3
+
4
+ interface CardProps {
5
+ children: ReactNode;
6
+ className?: string;
7
+ }
8
+
9
+ export function Card({ children, className = '' }: CardProps) {
10
+ return <View className={`rounded-2xl bg-white p-6 shadow-sm ${className}`}>{children}</View>;
11
+ }
@@ -0,0 +1,19 @@
1
+ import { View, Text } from 'react-native';
2
+
3
+ interface DividerProps {
4
+ text?: string;
5
+ }
6
+
7
+ export function Divider({ text }: DividerProps) {
8
+ if (!text) {
9
+ return <View className="my-4 h-px bg-gray-200" />;
10
+ }
11
+
12
+ return (
13
+ <View className="my-6 flex-row items-center">
14
+ <View className="h-px flex-1 bg-gray-200" />
15
+ <Text className="mx-4 text-sm text-gray-400">{text}</Text>
16
+ <View className="h-px flex-1 bg-gray-200" />
17
+ </View>
18
+ );
19
+ }
@@ -0,0 +1,23 @@
1
+ import { TextInput, View, Text } from 'react-native';
2
+ import type { TextInputProps } from 'react-native';
3
+
4
+ interface InputProps extends Omit<TextInputProps, 'className'> {
5
+ label?: string;
6
+ error?: string;
7
+ }
8
+
9
+ export function Input({ label, error, ...props }: InputProps) {
10
+ return (
11
+ <View className="mb-4">
12
+ {label && <Text className="mb-2 text-sm font-medium text-gray-700">{label}</Text>}
13
+ <TextInput
14
+ className={`rounded-xl border bg-white p-4 text-base ${
15
+ error ? 'border-red-500' : 'border-gray-200'
16
+ }`}
17
+ placeholderTextColor="#9CA3AF"
18
+ {...props}
19
+ />
20
+ {error && <Text className="mt-1 text-sm text-red-500">{error}</Text>}
21
+ </View>
22
+ );
23
+ }
@@ -0,0 +1,44 @@
1
+ import { View, Text, TouchableOpacity } from 'react-native';
2
+ import { Ionicons } from '@expo/vector-icons';
3
+ import * as Clipboard from 'expo-clipboard';
4
+
5
+ interface WalletCardProps {
6
+ address: string;
7
+ balance: string;
8
+ network: string;
9
+ onSend?: () => void;
10
+ }
11
+
12
+ export function WalletCard({ address, balance, network, onSend }: WalletCardProps) {
13
+ const handleCopyAddress = async () => {
14
+ await Clipboard.setStringAsync(address);
15
+ };
16
+
17
+ const shortAddress = `${address.slice(0, 10)}...${address.slice(-8)}`;
18
+
19
+ return (
20
+ <View className="rounded-2xl border border-gray-200 bg-white p-6">
21
+ <View className="mb-4 flex-row items-center justify-between">
22
+ <View className="rounded-full bg-gray-100 px-3 py-1">
23
+ <Text className="text-xs font-medium text-gray-600">{network}</Text>
24
+ </View>
25
+ <TouchableOpacity onPress={handleCopyAddress} className="flex-row items-center gap-1">
26
+ <Text className="font-mono text-xs text-gray-500">{shortAddress}</Text>
27
+ <Ionicons name="copy-outline" size={14} color="#6b7280" />
28
+ </TouchableOpacity>
29
+ </View>
30
+
31
+ <Text className="mb-1 text-sm text-gray-500">Balance</Text>
32
+ <Text className="mb-6 text-3xl font-bold text-gray-900">{balance}</Text>
33
+
34
+ {onSend && (
35
+ <TouchableOpacity
36
+ onPress={onSend}
37
+ className="flex-row items-center justify-center gap-2 rounded-xl bg-gray-900 py-3">
38
+ <Ionicons name="send-outline" size={18} color="#fff" />
39
+ <Text className="font-semibold text-white">Send</Text>
40
+ </TouchableOpacity>
41
+ )}
42
+ </View>
43
+ );
44
+ }
@@ -0,0 +1,5 @@
1
+ export { Button } from './Button';
2
+ export { Card } from './Card';
3
+ export { Divider } from './Divider';
4
+ export { Input } from './Input';
5
+ export { WalletCard } from './WalletCard';
@@ -0,0 +1,15 @@
1
+ /* eslint-env node */
2
+ const { defineConfig } = require('eslint/config');
3
+ const expoConfig = require('eslint-config-expo/flat');
4
+
5
+ module.exports = defineConfig([
6
+ expoConfig,
7
+ {
8
+ ignores: ['dist/*'],
9
+ },
10
+ {
11
+ rules: {
12
+ 'react/display-name': 'off',
13
+ },
14
+ },
15
+ ]);
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
@@ -0,0 +1,161 @@
1
+ import { useState, useCallback } from 'react';
2
+ import { para } from '@/lib/para';
3
+ import { openAuthUrl } from '@/lib/auth';
4
+ import type { AuthStatus } from '@/types';
5
+
6
+ interface UseOneClickLoginResult {
7
+ status: AuthStatus;
8
+ error: string | null;
9
+ // @if:hasEmail
10
+ loginWithEmail: (email: string) => Promise<boolean>;
11
+ // @endif
12
+ // @if:hasPhone
13
+ loginWithPhone: (phone: string) => Promise<boolean>;
14
+ // @endif
15
+ // @if:hasOAuth
16
+ loginWithGoogle: () => Promise<boolean>;
17
+ // @endif
18
+ reset: () => void;
19
+ }
20
+
21
+ export function useOneClickLogin(onSuccess: () => void): UseOneClickLoginResult {
22
+ const [status, setStatus] = useState<AuthStatus>('idle');
23
+ const [error, setError] = useState<string | null>(null);
24
+
25
+ const reset = useCallback(() => {
26
+ setStatus('idle');
27
+ setError(null);
28
+ }, []);
29
+
30
+ // @if:hasEmail
31
+ const loginWithEmail = useCallback(
32
+ async (email: string): Promise<boolean> => {
33
+ try {
34
+ reset();
35
+ setStatus('loading');
36
+
37
+ const authState = await para.signUpOrLogIn({ auth: { email } });
38
+
39
+ if (authState?.stage === 'verify' && 'loginUrl' in authState && authState.loginUrl) {
40
+ const result = await openAuthUrl(authState.loginUrl);
41
+
42
+ if (!result.success) {
43
+ throw new Error('Authentication was cancelled');
44
+ }
45
+
46
+ if (authState.nextStage === 'login') {
47
+ await para.waitForLogin({});
48
+ } else {
49
+ await para.waitForWalletCreation({});
50
+ }
51
+
52
+ setStatus('success');
53
+ onSuccess();
54
+ return true;
55
+ }
56
+
57
+ throw new Error('One-click login not available for this account');
58
+ } catch (err) {
59
+ const message = err instanceof Error ? err.message : 'Login failed';
60
+ setError(message);
61
+ setStatus('error');
62
+ return false;
63
+ }
64
+ },
65
+ [reset, onSuccess]
66
+ );
67
+ // @endif
68
+
69
+ // @if:hasPhone
70
+ const loginWithPhone = useCallback(
71
+ async (phone: string): Promise<boolean> => {
72
+ try {
73
+ reset();
74
+ setStatus('loading');
75
+
76
+ const authState = await para.signUpOrLogIn({
77
+ auth: { phone: phone as `+${number}` },
78
+ });
79
+
80
+ if (authState?.stage === 'verify' && 'loginUrl' in authState && authState.loginUrl) {
81
+ const result = await openAuthUrl(authState.loginUrl);
82
+
83
+ if (!result.success) {
84
+ throw new Error('Authentication was cancelled');
85
+ }
86
+
87
+ if (authState.nextStage === 'login') {
88
+ await para.waitForLogin({});
89
+ } else {
90
+ await para.waitForWalletCreation({});
91
+ }
92
+
93
+ setStatus('success');
94
+ onSuccess();
95
+ return true;
96
+ }
97
+
98
+ throw new Error('One-click login not available for this account');
99
+ } catch (err) {
100
+ const message = err instanceof Error ? err.message : 'Login failed';
101
+ setError(message);
102
+ setStatus('error');
103
+ return false;
104
+ }
105
+ },
106
+ [reset, onSuccess]
107
+ );
108
+ // @endif
109
+
110
+ // @if:hasOAuth
111
+ const loginWithGoogle = useCallback(async (): Promise<boolean> => {
112
+ try {
113
+ reset();
114
+ setStatus('loading');
115
+
116
+ const oauthUrl = await para.getOAuthUrl({ method: 'GOOGLE' });
117
+ const result = await openAuthUrl(oauthUrl);
118
+
119
+ if (!result.success) {
120
+ throw new Error('Authentication was cancelled');
121
+ }
122
+
123
+ const authState = await para.verifyOAuth({ method: 'GOOGLE' });
124
+
125
+ if (authState.stage === 'done') {
126
+ if (authState.isNewUser) {
127
+ await para.waitForWalletCreation({});
128
+ } else {
129
+ await para.waitForLogin({});
130
+ }
131
+
132
+ setStatus('success');
133
+ onSuccess();
134
+ return true;
135
+ }
136
+
137
+ throw new Error('Unexpected OAuth state');
138
+ } catch (err) {
139
+ const message = err instanceof Error ? err.message : 'Google login failed';
140
+ setError(message);
141
+ setStatus('error');
142
+ return false;
143
+ }
144
+ }, [reset, onSuccess]);
145
+ // @endif
146
+
147
+ return {
148
+ status,
149
+ error,
150
+ // @if:hasEmail
151
+ loginWithEmail,
152
+ // @endif
153
+ // @if:hasPhone
154
+ loginWithPhone,
155
+ // @endif
156
+ // @if:hasOAuth
157
+ loginWithGoogle,
158
+ // @endif
159
+ reset,
160
+ };
161
+ }
@@ -0,0 +1,118 @@
1
+ import { useMemo, useCallback, useState } from 'react';
2
+ import {
3
+ createWalletClient,
4
+ createPublicClient,
5
+ http,
6
+ formatEther,
7
+ parseEther,
8
+ type WalletClient,
9
+ type PublicClient,
10
+ type LocalAccount,
11
+ type Hex,
12
+ } from 'viem';
13
+ import { sepolia } from 'viem/chains';
14
+ import { createParaAccount } from '@getpara/viem-v2-integration';
15
+
16
+ import { para } from '@/lib/para';
17
+ import { usePara } from '@/providers/ParaProvider';
18
+
19
+ interface UseViemClientResult {
20
+ account: LocalAccount | null;
21
+ walletClient: WalletClient | null;
22
+ publicClient: PublicClient | null;
23
+ isReady: boolean;
24
+ getBalance: () => Promise<string | null>;
25
+ sendTransaction: (to: Hex, amount: string) => Promise<Hex | null>;
26
+ isLoading: boolean;
27
+ error: string | null;
28
+ }
29
+
30
+ export function useViemClient(): UseViemClientResult {
31
+ const { isAuthenticated, wallets } = usePara();
32
+ const [isLoading, setIsLoading] = useState(false);
33
+ const [error, setError] = useState<string | null>(null);
34
+
35
+ const { account, walletClient, publicClient } = useMemo(() => {
36
+ if (!isAuthenticated || wallets.length === 0) {
37
+ return { account: null, walletClient: null, publicClient: null };
38
+ }
39
+
40
+ const walletAddress = wallets[0].address as Hex;
41
+ const paraAccount = createParaAccount(para, walletAddress);
42
+
43
+ const wallet = createWalletClient({
44
+ account: paraAccount,
45
+ chain: sepolia,
46
+ transport: http(),
47
+ });
48
+
49
+ const public_ = createPublicClient({
50
+ chain: sepolia,
51
+ transport: http(),
52
+ });
53
+
54
+ return { account: paraAccount, walletClient: wallet, publicClient: public_ };
55
+ }, [isAuthenticated, wallets]);
56
+
57
+ const getBalance = useCallback(async (): Promise<string | null> => {
58
+ if (!publicClient || !account) {
59
+ return null;
60
+ }
61
+
62
+ try {
63
+ setIsLoading(true);
64
+ setError(null);
65
+
66
+ const balance = await publicClient.getBalance({ address: account.address });
67
+ const formatted = formatEther(balance);
68
+
69
+ return formatted;
70
+ } catch (err) {
71
+ const message = err instanceof Error ? err.message : 'Failed to get balance';
72
+ setError(message);
73
+ return null;
74
+ } finally {
75
+ setIsLoading(false);
76
+ }
77
+ }, [publicClient, account]);
78
+
79
+ const sendTransaction = useCallback(
80
+ async (to: Hex, amount: string): Promise<Hex | null> => {
81
+ if (!walletClient || !account) {
82
+ return null;
83
+ }
84
+
85
+ try {
86
+ setIsLoading(true);
87
+ setError(null);
88
+ const value = parseEther(amount);
89
+
90
+ const hash = await walletClient.sendTransaction({
91
+ to,
92
+ value,
93
+ chain: sepolia,
94
+ });
95
+
96
+ return hash;
97
+ } catch (err) {
98
+ const message = err instanceof Error ? err.message : 'Failed to send transaction';
99
+ setError(message);
100
+ return null;
101
+ } finally {
102
+ setIsLoading(false);
103
+ }
104
+ },
105
+ [walletClient, account]
106
+ );
107
+
108
+ return {
109
+ account,
110
+ walletClient,
111
+ publicClient,
112
+ isReady: account !== null && walletClient !== null && publicClient !== null,
113
+ getBalance,
114
+ sendTransaction,
115
+ isLoading,
116
+ error,
117
+ };
118
+ }
@@ -0,0 +1,52 @@
1
+ import { useState, useCallback } from 'react';
2
+ import { para } from '@/lib/para';
3
+ import type { Wallet } from '@/types';
4
+
5
+ interface UseWalletsResult {
6
+ wallets: Wallet[];
7
+ isLoading: boolean;
8
+ error: string | null;
9
+ loadWallets: () => Promise<void>;
10
+ clearWallets: () => void;
11
+ }
12
+
13
+ export function useWallets(): UseWalletsResult {
14
+ const [wallets, setWallets] = useState<Wallet[]>([]);
15
+ const [isLoading, setIsLoading] = useState(false);
16
+ const [error, setError] = useState<string | null>(null);
17
+
18
+ const loadWallets = useCallback(async () => {
19
+ setIsLoading(true);
20
+ setError(null);
21
+
22
+ try {
23
+ const evmWallets = await para.getWalletsByType('EVM');
24
+
25
+ const walletList: Wallet[] = Object.values(evmWallets || {}).map((w) => ({
26
+ id: w.id,
27
+ address: w.address || '',
28
+ type: 'EVM',
29
+ }));
30
+
31
+ setWallets(walletList);
32
+ } catch (err) {
33
+ const message = err instanceof Error ? err.message : 'Failed to load wallets';
34
+ setError(message);
35
+ } finally {
36
+ setIsLoading(false);
37
+ }
38
+ }, []);
39
+
40
+ const clearWallets = useCallback(() => {
41
+ setWallets([]);
42
+ setError(null);
43
+ }, []);
44
+
45
+ return {
46
+ wallets,
47
+ isLoading,
48
+ error,
49
+ loadWallets,
50
+ clearWallets,
51
+ };
52
+ }
@@ -0,0 +1,2 @@
1
+ import '@getpara/react-native-wallet/shim';
2
+ import 'expo-router/entry';