@getpara/create-para-app 0.5.0 → 2.7.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.
- package/README.md +71 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +306 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -48
- package/dist/index.js.map +1 -0
- package/dist/package-builder.d.ts +13 -0
- package/dist/package-builder.d.ts.map +1 -0
- package/dist/package-builder.js +89 -0
- package/dist/package-builder.js.map +1 -0
- package/dist/post-scaffold.d.ts +3 -0
- package/dist/post-scaffold.d.ts.map +1 -0
- package/dist/post-scaffold.js +64 -0
- package/dist/post-scaffold.js.map +1 -0
- package/dist/prompt-orchestrator.d.ts +3 -0
- package/dist/prompt-orchestrator.d.ts.map +1 -0
- package/dist/prompt-orchestrator.js +198 -0
- package/dist/prompt-orchestrator.js.map +1 -0
- package/dist/scaffolder.d.ts +3 -0
- package/dist/scaffolder.d.ts.map +1 -0
- package/dist/scaffolder.js +82 -0
- package/dist/scaffolder.js.map +1 -0
- package/dist/template-registry.d.ts +12 -0
- package/dist/template-registry.d.ts.map +1 -0
- package/dist/template-registry.js +29 -0
- package/dist/template-registry.js.map +1 -0
- package/dist/template-renderer.d.ts +11 -0
- package/dist/template-renderer.d.ts.map +1 -0
- package/dist/template-renderer.js +133 -0
- package/dist/template-renderer.js.map +1 -0
- package/dist/template-strategies/expo-template.d.ts +18 -0
- package/dist/template-strategies/expo-template.d.ts.map +1 -0
- package/dist/template-strategies/expo-template.js +173 -0
- package/dist/template-strategies/expo-template.js.map +1 -0
- package/dist/template-strategies/index.d.ts +4 -0
- package/dist/template-strategies/index.d.ts.map +1 -0
- package/dist/template-strategies/index.js +3 -0
- package/dist/template-strategies/index.js.map +1 -0
- package/dist/template-strategies/nextjs-template.d.ts +12 -0
- package/dist/template-strategies/nextjs-template.d.ts.map +1 -0
- package/dist/template-strategies/nextjs-template.js +123 -0
- package/dist/template-strategies/nextjs-template.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/fs.d.ts +11 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +38 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +25 -19
- package/dist/utils/logger.js.map +1 -0
- package/package.json +35 -30
- package/templates/expo/_env.example +3 -0
- package/templates/expo/_gitignore +48 -0
- package/templates/expo/_yarnrc.yml +1 -0
- package/templates/expo/app/(auth)/_layout.tsx +12 -0
- package/templates/expo/app/(auth)/index.tsx.template +86 -0
- package/templates/expo/app/(tabs)/_layout.tsx +16 -0
- package/templates/expo/app/(tabs)/index.tsx +112 -0
- package/templates/expo/app/(tabs)/send.tsx +111 -0
- package/templates/expo/app/_layout.tsx +17 -0
- package/templates/expo/app/index.tsx +22 -0
- package/templates/expo/app.json.template +32 -0
- package/templates/expo/assets/adaptive-icon.png +0 -0
- package/templates/expo/assets/favicon.png +0 -0
- package/templates/expo/assets/icon.png +0 -0
- package/templates/expo/assets/splash.png +0 -0
- package/templates/expo/babel.config.cjs +12 -0
- package/templates/expo/components/features/AuthForm.tsx.template +138 -0
- package/templates/expo/components/features/OAuthButtons.tsx.template +27 -0
- package/templates/expo/components/features/index.ts.template +4 -0
- package/templates/expo/components/ui/Button.tsx +58 -0
- package/templates/expo/components/ui/Card.tsx +11 -0
- package/templates/expo/components/ui/Divider.tsx +19 -0
- package/templates/expo/components/ui/Input.tsx +23 -0
- package/templates/expo/components/ui/WalletCard.tsx +44 -0
- package/templates/expo/components/ui/index.ts +5 -0
- package/templates/expo/eslint.config.cjs +15 -0
- package/templates/expo/global.css +3 -0
- package/templates/expo/hooks/useOneClickLogin.ts.template +161 -0
- package/templates/expo/hooks/useViemClient.ts +118 -0
- package/templates/expo/hooks/useWallets.ts +52 -0
- package/templates/expo/index.js +2 -0
- package/templates/expo/lib/auth.ts +54 -0
- package/templates/expo/lib/constants.ts.template +2 -0
- package/templates/expo/lib/para.ts +13 -0
- package/templates/expo/metro.config.cjs +14 -0
- package/templates/expo/nativewind-env.d.ts +2 -0
- package/templates/expo/prettier.config.cjs +10 -0
- package/templates/expo/providers/ParaProvider.tsx +140 -0
- package/templates/expo/tailwind.config.cjs +23 -0
- package/templates/expo/tsconfig.json +11 -0
- package/templates/expo/types/index.ts +28 -0
- package/templates/nextjs/README.md +69 -0
- package/templates/nextjs/_env.example +8 -0
- package/templates/nextjs/_gitignore +36 -0
- package/templates/nextjs/_yarnrc.yml +1 -0
- package/templates/nextjs/next.config.ts +5 -0
- package/templates/nextjs/postcss.config.mjs +7 -0
- package/templates/nextjs/public/para.svg +3 -0
- package/templates/nextjs/src/app/layout.tsx +30 -0
- package/templates/nextjs/src/app/page.tsx +40 -0
- package/templates/nextjs/src/components/ParaProvider.tsx +116 -0
- package/templates/nextjs/src/components/layout/Header.tsx +44 -0
- package/templates/nextjs/src/components/ui/ConnectCard.tsx +24 -0
- package/templates/nextjs/src/components/ui/SignMessage.tsx +53 -0
- package/templates/nextjs/src/components/ui/WalletInfo.tsx +22 -0
- package/templates/nextjs/src/hooks/useSignHelloWorld.ts +23 -0
- package/templates/nextjs/src/styles/globals.css +1 -0
- package/templates/nextjs/tsconfig.json +27 -0
- package/dist/bin/index.js +0 -2
- package/dist/commands/scaffold.js +0 -137
- package/dist/integrations/codeGenerators.js +0 -637
- package/dist/integrations/packageJsonHelpers.js +0 -77
- package/dist/integrations/sdkSetup.js +0 -24
- package/dist/integrations/sdkSetupNextjs.js +0 -111
- package/dist/integrations/sdkSetupVite.js +0 -82
- package/dist/package.json +0 -44
- package/dist/prompts/interactive.js +0 -98
- package/dist/src/commands/scaffold.js +0 -157
- package/dist/src/index.js +0 -48
- package/dist/src/integrations/codeGenerators.js +0 -637
- package/dist/src/integrations/packageJsonHelpers.js +0 -77
- package/dist/src/integrations/sdkSetup.js +0 -24
- package/dist/src/integrations/sdkSetupNextjs.js +0 -111
- package/dist/src/integrations/sdkSetupVite.js +0 -82
- package/dist/src/prompts/interactive.js +0 -98
- package/dist/src/templates/nextjs.js +0 -68
- package/dist/src/templates/vite-react.js +0 -52
- package/dist/src/utils/exec.js +0 -16
- package/dist/src/utils/formatting.js +0 -31
- package/dist/src/utils/logger.js +0 -19
- package/dist/templates/nextjs.js +0 -61
- package/dist/templates/vite-react.js +0 -43
- package/dist/utils/exec.js +0 -16
- package/dist/utils/formatting.js +0 -31
package/package.json
CHANGED
|
@@ -1,44 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getpara/create-para-app",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
3
|
+
"version": "2.7.0",
|
|
4
|
+
"description": "Create Para SDK applications with one command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"create-para-app": "dist/
|
|
7
|
+
"create-para-app": "./dist/index.js"
|
|
8
8
|
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
9
10
|
"files": [
|
|
10
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"templates"
|
|
11
13
|
],
|
|
12
|
-
"engines": {
|
|
13
|
-
"node": ">=14.0.0"
|
|
14
|
-
},
|
|
15
|
-
"license": "MIT",
|
|
16
14
|
"scripts": {
|
|
17
15
|
"build": "tsc",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"prepublishOnly": "yarn build"
|
|
20
19
|
},
|
|
21
20
|
"dependencies": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"execa": "9.5.2",
|
|
25
|
-
"fs-extra": "11.3.0",
|
|
26
|
-
"inquirer": "12.4.1",
|
|
27
|
-
"ora": "8.2.0",
|
|
28
|
-
"prettier": "3.5.0",
|
|
29
|
-
"update-notifier": "7.3.1"
|
|
21
|
+
"@clack/prompts": "^0.9.1",
|
|
22
|
+
"picocolors": "^1.1.1"
|
|
30
23
|
},
|
|
31
24
|
"devDependencies": {
|
|
32
|
-
"@types/
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
25
|
+
"@types/node": "^22.13.10",
|
|
26
|
+
"typescript": "^5.8.3"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"para",
|
|
33
|
+
"wallet",
|
|
34
|
+
"web3",
|
|
35
|
+
"scaffolding",
|
|
36
|
+
"cli",
|
|
37
|
+
"create-app",
|
|
38
|
+
"nextjs"
|
|
39
|
+
],
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/para-space/web-sdk.git",
|
|
43
|
+
"directory": "packages/create-para-app"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"license": "MIT"
|
|
44
49
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Expo
|
|
5
|
+
.expo/
|
|
6
|
+
dist/
|
|
7
|
+
web-build/
|
|
8
|
+
|
|
9
|
+
# Native builds
|
|
10
|
+
*.orig.*
|
|
11
|
+
*.jks
|
|
12
|
+
*.p8
|
|
13
|
+
*.p12
|
|
14
|
+
*.key
|
|
15
|
+
*.mobileprovision
|
|
16
|
+
|
|
17
|
+
# iOS
|
|
18
|
+
ios/
|
|
19
|
+
*.xcworkspace
|
|
20
|
+
*.xcodeproj
|
|
21
|
+
|
|
22
|
+
# Android
|
|
23
|
+
android/
|
|
24
|
+
*.apk
|
|
25
|
+
*.aab
|
|
26
|
+
|
|
27
|
+
# Environment
|
|
28
|
+
.env
|
|
29
|
+
.env.local
|
|
30
|
+
.env.*.local
|
|
31
|
+
|
|
32
|
+
# IDE
|
|
33
|
+
.idea/
|
|
34
|
+
.vscode/
|
|
35
|
+
*.swp
|
|
36
|
+
*.swo
|
|
37
|
+
|
|
38
|
+
# OS
|
|
39
|
+
.DS_Store
|
|
40
|
+
Thumbs.db
|
|
41
|
+
|
|
42
|
+
# Logs
|
|
43
|
+
npm-debug.log*
|
|
44
|
+
yarn-debug.log*
|
|
45
|
+
yarn-error.log*
|
|
46
|
+
|
|
47
|
+
# TypeScript
|
|
48
|
+
*.tsbuildinfo
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nodeLinker: node-modules
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { View, Text, ScrollView, KeyboardAvoidingView, Platform } from 'react-native';
|
|
2
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
3
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
4
|
+
import { useRouter } from 'expo-router';
|
|
5
|
+
|
|
6
|
+
// @if:hasOAuth
|
|
7
|
+
import { Divider } from '@/components/ui';
|
|
8
|
+
// @endif
|
|
9
|
+
import { AuthForm } from '@/components/features';
|
|
10
|
+
// @if:hasOAuth
|
|
11
|
+
import { OAuthButtons } from '@/components/features';
|
|
12
|
+
// @endif
|
|
13
|
+
import { useOneClickLogin } from '@/hooks/useOneClickLogin';
|
|
14
|
+
import { usePara } from '@/providers/ParaProvider';
|
|
15
|
+
|
|
16
|
+
export default function LoginScreen() {
|
|
17
|
+
const router = useRouter();
|
|
18
|
+
const { setAuthenticated } = usePara();
|
|
19
|
+
|
|
20
|
+
const handleSuccess = () => {
|
|
21
|
+
setAuthenticated(true);
|
|
22
|
+
router.replace('/(tabs)');
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
// @if:hasEmail
|
|
27
|
+
loginWithEmail,
|
|
28
|
+
// @endif
|
|
29
|
+
// @if:hasPhone
|
|
30
|
+
loginWithPhone,
|
|
31
|
+
// @endif
|
|
32
|
+
// @if:hasOAuth
|
|
33
|
+
loginWithGoogle,
|
|
34
|
+
// @endif
|
|
35
|
+
status,
|
|
36
|
+
error,
|
|
37
|
+
} = useOneClickLogin(handleSuccess);
|
|
38
|
+
|
|
39
|
+
const isLoading = status === 'loading' || status === 'verifying' || status === 'completing';
|
|
40
|
+
|
|
41
|
+
const handleAuthSubmit = async (value: string, method: 'email' | 'phone') => {
|
|
42
|
+
// @if:hasEmail
|
|
43
|
+
if (method === 'email') {
|
|
44
|
+
await loginWithEmail(value);
|
|
45
|
+
}
|
|
46
|
+
// @endif
|
|
47
|
+
// @if:hasPhone
|
|
48
|
+
if (method === 'phone') {
|
|
49
|
+
await loginWithPhone(value);
|
|
50
|
+
}
|
|
51
|
+
// @endif
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<SafeAreaView className="flex-1 bg-white">
|
|
56
|
+
<KeyboardAvoidingView
|
|
57
|
+
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
58
|
+
className="flex-1">
|
|
59
|
+
<ScrollView
|
|
60
|
+
contentContainerStyle={{ flexGrow: 1 }}
|
|
61
|
+
keyboardShouldPersistTaps="handled"
|
|
62
|
+
className="flex-1">
|
|
63
|
+
<View className="flex-1 px-6 pb-8 pt-12">
|
|
64
|
+
<View className="mb-10 items-center">
|
|
65
|
+
<View className="mb-4 h-16 w-16 items-center justify-center rounded-2xl bg-gray-900">
|
|
66
|
+
<Ionicons name="wallet-outline" size={32} color="#fff" />
|
|
67
|
+
</View>
|
|
68
|
+
<Text className="mb-2 text-2xl font-bold text-gray-900">{{expoAppName}}</Text>
|
|
69
|
+
<Text className="text-center text-gray-500">Sign in to access your wallet</Text>
|
|
70
|
+
</View>
|
|
71
|
+
<AuthForm onSubmit={handleAuthSubmit} loading={isLoading} error={error} />
|
|
72
|
+
// @if:hasOAuth
|
|
73
|
+
<Divider text="or continue with" />
|
|
74
|
+
<OAuthButtons onGooglePress={loginWithGoogle} disabled={isLoading} />
|
|
75
|
+
// @endif
|
|
76
|
+
</View>
|
|
77
|
+
<View className="px-6 pb-4">
|
|
78
|
+
<Text className="text-center text-xs text-gray-400">
|
|
79
|
+
By continuing, you agree to our Terms of Service and Privacy Policy
|
|
80
|
+
</Text>
|
|
81
|
+
</View>
|
|
82
|
+
</ScrollView>
|
|
83
|
+
</KeyboardAvoidingView>
|
|
84
|
+
</SafeAreaView>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Stack } from 'expo-router';
|
|
2
|
+
|
|
3
|
+
export default function TabsLayout() {
|
|
4
|
+
return (
|
|
5
|
+
<Stack>
|
|
6
|
+
<Stack.Screen name="index" options={{ headerShown: false }} />
|
|
7
|
+
<Stack.Screen
|
|
8
|
+
name="send"
|
|
9
|
+
options={{
|
|
10
|
+
title: 'Send',
|
|
11
|
+
headerBackTitle: 'Back',
|
|
12
|
+
}}
|
|
13
|
+
/>
|
|
14
|
+
</Stack>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { View, Text, ScrollView, RefreshControl, Alert } from 'react-native';
|
|
2
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
3
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
4
|
+
import { useRouter } from 'expo-router';
|
|
5
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
6
|
+
|
|
7
|
+
import { WalletCard, Button } from '@/components/ui';
|
|
8
|
+
import { usePara } from '@/providers/ParaProvider';
|
|
9
|
+
import { useViemClient } from '@/hooks/useViemClient';
|
|
10
|
+
|
|
11
|
+
export default function HomeScreen() {
|
|
12
|
+
const router = useRouter();
|
|
13
|
+
const { wallets, refreshAuth, logout } = usePara();
|
|
14
|
+
const { isReady, getBalance } = useViemClient();
|
|
15
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
16
|
+
const [balance, setBalance] = useState<string | null>(null);
|
|
17
|
+
|
|
18
|
+
const fetchBalance = useCallback(async () => {
|
|
19
|
+
if (isReady) {
|
|
20
|
+
const bal = await getBalance();
|
|
21
|
+
setBalance(bal);
|
|
22
|
+
}
|
|
23
|
+
}, [isReady, getBalance]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
fetchBalance();
|
|
27
|
+
}, [fetchBalance]);
|
|
28
|
+
|
|
29
|
+
const onRefresh = useCallback(async () => {
|
|
30
|
+
setRefreshing(true);
|
|
31
|
+
await refreshAuth();
|
|
32
|
+
await fetchBalance();
|
|
33
|
+
setRefreshing(false);
|
|
34
|
+
}, [refreshAuth, fetchBalance]);
|
|
35
|
+
|
|
36
|
+
const handleLogout = () => {
|
|
37
|
+
Alert.alert('Sign Out', 'Are you sure you want to sign out?', [
|
|
38
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
39
|
+
{
|
|
40
|
+
text: 'Sign Out',
|
|
41
|
+
style: 'destructive',
|
|
42
|
+
onPress: async () => {
|
|
43
|
+
await logout();
|
|
44
|
+
router.replace('/(auth)');
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const handleSend = () => {
|
|
51
|
+
router.push('/(tabs)/send');
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const primaryWallet = wallets[0];
|
|
55
|
+
const displayBalance = balance ? `${parseFloat(balance).toFixed(6)} ETH` : 'Loading...';
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<SafeAreaView testID="walletsView" className="flex-1 bg-gray-50">
|
|
59
|
+
<ScrollView
|
|
60
|
+
className="flex-1"
|
|
61
|
+
contentContainerStyle={{ padding: 24 }}
|
|
62
|
+
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}>
|
|
63
|
+
<View className="mb-6">
|
|
64
|
+
<Text className="text-2xl font-bold text-gray-900">Your Wallet</Text>
|
|
65
|
+
<Text className="mt-1 text-gray-500">Manage your assets</Text>
|
|
66
|
+
</View>
|
|
67
|
+
{primaryWallet ? (
|
|
68
|
+
<WalletCard
|
|
69
|
+
address={primaryWallet.address}
|
|
70
|
+
balance={displayBalance}
|
|
71
|
+
network="Sepolia"
|
|
72
|
+
onSend={handleSend}
|
|
73
|
+
/>
|
|
74
|
+
) : (
|
|
75
|
+
<View className="items-center rounded-2xl bg-white p-6">
|
|
76
|
+
<Text className="text-center text-gray-500">No wallet found. Pull to refresh.</Text>
|
|
77
|
+
</View>
|
|
78
|
+
)}
|
|
79
|
+
{wallets.length > 1 && (
|
|
80
|
+
<View className="mt-6">
|
|
81
|
+
<Text className="mb-3 text-lg font-semibold text-gray-900">Other Wallets</Text>
|
|
82
|
+
{wallets.slice(1).map((wallet) => (
|
|
83
|
+
<View
|
|
84
|
+
key={wallet.id}
|
|
85
|
+
className="mb-2 flex-row items-center justify-between rounded-xl bg-white p-4">
|
|
86
|
+
<Text className="font-mono text-sm text-gray-600">
|
|
87
|
+
{wallet.address.slice(0, 10)}...{wallet.address.slice(-8)}
|
|
88
|
+
</Text>
|
|
89
|
+
<View className="rounded bg-gray-100 px-2 py-1">
|
|
90
|
+
<Text className="text-xs text-gray-600">{wallet.type}</Text>
|
|
91
|
+
</View>
|
|
92
|
+
</View>
|
|
93
|
+
))}
|
|
94
|
+
</View>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
<View className="mt-8">
|
|
98
|
+
<Button
|
|
99
|
+
title="Sign Out"
|
|
100
|
+
variant="danger"
|
|
101
|
+
onPress={handleLogout}
|
|
102
|
+
icon={<Ionicons name="log-out-outline" size={20} color="#fff" />}
|
|
103
|
+
/>
|
|
104
|
+
</View>
|
|
105
|
+
|
|
106
|
+
<View className="mt-8 items-center">
|
|
107
|
+
<Text className="text-sm text-gray-400">Powered by Para</Text>
|
|
108
|
+
</View>
|
|
109
|
+
</ScrollView>
|
|
110
|
+
</SafeAreaView>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { View, Text, TextInput, Alert, KeyboardAvoidingView, Platform } from 'react-native';
|
|
2
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
3
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
4
|
+
import { useRouter } from 'expo-router';
|
|
5
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
6
|
+
import type { Hex } from 'viem';
|
|
7
|
+
|
|
8
|
+
import { Button } from '@/components/ui';
|
|
9
|
+
import { useViemClient } from '@/hooks/useViemClient';
|
|
10
|
+
|
|
11
|
+
export default function SendScreen() {
|
|
12
|
+
const router = useRouter();
|
|
13
|
+
const { isReady, getBalance, sendTransaction, isLoading, error } = useViemClient();
|
|
14
|
+
|
|
15
|
+
const [balance, setBalance] = useState<string | null>(null);
|
|
16
|
+
const [recipient, setRecipient] = useState('');
|
|
17
|
+
const [amount, setAmount] = useState('');
|
|
18
|
+
|
|
19
|
+
const fetchBalance = useCallback(async () => {
|
|
20
|
+
if (isReady) {
|
|
21
|
+
const bal = await getBalance();
|
|
22
|
+
setBalance(bal);
|
|
23
|
+
}
|
|
24
|
+
}, [isReady, getBalance]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
fetchBalance();
|
|
28
|
+
}, [fetchBalance]);
|
|
29
|
+
|
|
30
|
+
const handleSend = async () => {
|
|
31
|
+
if (!recipient) {
|
|
32
|
+
Alert.alert('Error', 'Please enter a recipient address');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!amount || parseFloat(amount) <= 0) {
|
|
36
|
+
Alert.alert('Error', 'Please enter a valid amount');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Alert.alert(
|
|
41
|
+
'Confirm Transaction',
|
|
42
|
+
`Send ${amount} ETH to\n${recipient.slice(0, 16)}...?`,
|
|
43
|
+
[
|
|
44
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
45
|
+
{
|
|
46
|
+
text: 'Send',
|
|
47
|
+
onPress: async () => {
|
|
48
|
+
const hash = await sendTransaction(recipient as Hex, amount);
|
|
49
|
+
if (hash) {
|
|
50
|
+
Alert.alert('Success', `Transaction sent!\n\nHash: ${hash.slice(0, 20)}...`, [
|
|
51
|
+
{ text: 'OK', onPress: () => router.back() },
|
|
52
|
+
]);
|
|
53
|
+
} else if (error) {
|
|
54
|
+
Alert.alert('Error', error);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const displayBalance = balance ? `${parseFloat(balance).toFixed(6)} ETH` : 'Loading...';
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<SafeAreaView className="flex-1 bg-gray-50" edges={['bottom']}>
|
|
66
|
+
<KeyboardAvoidingView
|
|
67
|
+
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
68
|
+
className="flex-1">
|
|
69
|
+
<View className="flex-1 p-6">
|
|
70
|
+
{/* Balance Display */}
|
|
71
|
+
<View className="mb-6 rounded-2xl bg-white p-4">
|
|
72
|
+
<Text className="text-sm text-gray-500">Available Balance</Text>
|
|
73
|
+
<Text className="text-2xl font-bold text-gray-900">{displayBalance}</Text>
|
|
74
|
+
</View>
|
|
75
|
+
|
|
76
|
+
{/* Form */}
|
|
77
|
+
<View className="flex-1">
|
|
78
|
+
<Text className="mb-2 text-sm font-medium text-gray-700">Recipient Address</Text>
|
|
79
|
+
<TextInput
|
|
80
|
+
className="mb-4 rounded-xl border border-gray-200 bg-white p-4 font-mono text-sm text-gray-900"
|
|
81
|
+
placeholder="0x..."
|
|
82
|
+
placeholderTextColor="#9ca3af"
|
|
83
|
+
value={recipient}
|
|
84
|
+
onChangeText={setRecipient}
|
|
85
|
+
autoCapitalize="none"
|
|
86
|
+
autoCorrect={false}
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
<Text className="mb-2 text-sm font-medium text-gray-700">Amount (ETH)</Text>
|
|
90
|
+
<TextInput
|
|
91
|
+
className="mb-6 rounded-xl border border-gray-200 bg-white p-4 text-sm text-gray-900"
|
|
92
|
+
placeholder="0.001"
|
|
93
|
+
placeholderTextColor="#9ca3af"
|
|
94
|
+
value={amount}
|
|
95
|
+
onChangeText={setAmount}
|
|
96
|
+
keyboardType="decimal-pad"
|
|
97
|
+
/>
|
|
98
|
+
</View>
|
|
99
|
+
|
|
100
|
+
{/* Send Button */}
|
|
101
|
+
<Button
|
|
102
|
+
title={isLoading ? 'Sending...' : 'Send ETH'}
|
|
103
|
+
onPress={handleSend}
|
|
104
|
+
disabled={isLoading || !isReady}
|
|
105
|
+
icon={<Ionicons name="send-outline" size={20} color="#fff" />}
|
|
106
|
+
/>
|
|
107
|
+
</View>
|
|
108
|
+
</KeyboardAvoidingView>
|
|
109
|
+
</SafeAreaView>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import '../global.css';
|
|
2
|
+
|
|
3
|
+
import { Stack } from 'expo-router';
|
|
4
|
+
import { StatusBar } from 'expo-status-bar';
|
|
5
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
6
|
+
import { ParaProvider } from '@/providers/ParaProvider';
|
|
7
|
+
|
|
8
|
+
export default function RootLayout() {
|
|
9
|
+
return (
|
|
10
|
+
<SafeAreaProvider>
|
|
11
|
+
<ParaProvider>
|
|
12
|
+
<StatusBar style="auto" />
|
|
13
|
+
<Stack screenOptions={{ headerShown: false }} />
|
|
14
|
+
</ParaProvider>
|
|
15
|
+
</SafeAreaProvider>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Redirect } from 'expo-router';
|
|
2
|
+
import { View, ActivityIndicator, Text } from 'react-native';
|
|
3
|
+
import { usePara } from '@/providers/ParaProvider';
|
|
4
|
+
|
|
5
|
+
export default function Index() {
|
|
6
|
+
const { isAuthenticated, isLoading } = usePara();
|
|
7
|
+
|
|
8
|
+
if (isLoading) {
|
|
9
|
+
return (
|
|
10
|
+
<View className="flex-1 items-center justify-center bg-white">
|
|
11
|
+
<ActivityIndicator size="large" color="#4F46E5" />
|
|
12
|
+
<Text className="mt-4 text-gray-500">Loading...</Text>
|
|
13
|
+
</View>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (isAuthenticated) {
|
|
18
|
+
return <Redirect href="/(tabs)" />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return <Redirect href="/(auth)" />;
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"expo": {
|
|
3
|
+
"name": "{{expoAppName}}",
|
|
4
|
+
"slug": "{{expoSlug}}",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"scheme": "{{expoScheme}}",
|
|
7
|
+
"platforms": ["ios", "android"],
|
|
8
|
+
"orientation": "portrait",
|
|
9
|
+
"icon": "./assets/icon.png",
|
|
10
|
+
"userInterfaceStyle": "automatic",
|
|
11
|
+
"splash": {
|
|
12
|
+
"image": "./assets/splash.png",
|
|
13
|
+
"resizeMode": "contain",
|
|
14
|
+
"backgroundColor": "#ffffff"
|
|
15
|
+
},
|
|
16
|
+
"ios": {
|
|
17
|
+
"supportsTablet": true,
|
|
18
|
+
"bundleIdentifier": "{{bundleId}}"
|
|
19
|
+
},
|
|
20
|
+
"android": {
|
|
21
|
+
"package": "{{bundleId}}",
|
|
22
|
+
"adaptiveIcon": {
|
|
23
|
+
"foregroundImage": "./assets/adaptive-icon.png",
|
|
24
|
+
"backgroundColor": "#ffffff"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"plugins": ["expo-router"],
|
|
28
|
+
"experiments": {
|
|
29
|
+
"typedRoutes": true
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|