zopassport 0.1.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/LICENSE +22 -0
- package/README.md +407 -0
- package/app/.env.example +15 -0
- package/app/README.md +28 -0
- package/app/package.json +24 -0
- package/app/reanimated-mock.js +102 -0
- package/app/reanimated-mock.jsx +97 -0
- package/app/src/App.tsx +331 -0
- package/app/src/components/FounderBadge.tsx +26 -0
- package/app/src/components/OTPInput.tsx +149 -0
- package/app/src/components/PhoneInput.tsx +109 -0
- package/app/src/components/ZoAuth.tsx +320 -0
- package/app/src/components/ZoAvatar.tsx +87 -0
- package/app/src/components/ZoLanding.tsx +231 -0
- package/app/src/components/ZoOnboarding.tsx +524 -0
- package/app/src/components/ZoPassportCard.tsx +183 -0
- package/app/src/components/ZoProgressRing.tsx +57 -0
- package/app/src/components/index.ts +16 -0
- package/app/src/components/wallet/MovingShine.tsx +43 -0
- package/app/src/components/wallet/TransactionItem.tsx +84 -0
- package/app/src/components/wallet/TransactionList.tsx +65 -0
- package/app/src/components/wallet/WalletCard.tsx +152 -0
- package/app/src/components/wallet/WalletScreen.tsx +190 -0
- package/app/src/components/wallet/ZoToken.tsx +69 -0
- package/app/src/components/wallet/index.ts +8 -0
- package/app/src/components/wallet/styles/index.ts +4 -0
- package/app/src/components/wallet/styles/walletStyles.ts +210 -0
- package/app/src/sdk/ZoPassportSDK.ts +277 -0
- package/app/src/sdk/lib/api/auth.ts +223 -0
- package/app/src/sdk/lib/api/avatar.ts +155 -0
- package/app/src/sdk/lib/api/client.ts +135 -0
- package/app/src/sdk/lib/api/index.ts +8 -0
- package/app/src/sdk/lib/api/profile.ts +80 -0
- package/app/src/sdk/lib/api/wallet.ts +59 -0
- package/app/src/sdk/lib/types/auth.ts +78 -0
- package/app/src/sdk/lib/types/avatar.ts +22 -0
- package/app/src/sdk/lib/types/index.ts +8 -0
- package/app/src/sdk/lib/types/profile.ts +18 -0
- package/app/src/sdk/lib/types/wallet.ts +103 -0
- package/app/src/sdk/lib/types.ts +205 -0
- package/app/src/sdk/lib/utils/index.ts +6 -0
- package/app/src/sdk/lib/utils/phone.ts +71 -0
- package/app/src/sdk/lib/utils/storage.ts +116 -0
- package/app/src/sdk/lib/utils/wallet.ts +73 -0
- package/app/src/sdk/types.ts +205 -0
- package/app/src/styles.css +154 -0
- package/app/svg-mock.js +125 -0
- package/app/svg-mock.jsx +120 -0
- package/app/vite.config.ts +70 -0
- package/assets/ASSETS_MANIFEST.md +124 -0
- package/assets/bae.png +0 -0
- package/assets/bro.png +0 -0
- package/assets/cultural-stickers/Business.png +0 -0
- package/assets/cultural-stickers/Default (2).jpg +0 -0
- package/assets/cultural-stickers/Design.png +0 -0
- package/assets/cultural-stickers/FollowYourHeart.png +0 -0
- package/assets/cultural-stickers/Food.png +0 -0
- package/assets/cultural-stickers/Game.png +0 -0
- package/assets/cultural-stickers/Health&Fitness.png +0 -0
- package/assets/cultural-stickers/Home&Lifestyle.png +0 -0
- package/assets/cultural-stickers/Law.png +0 -0
- package/assets/cultural-stickers/Literature&Stories.png +0 -0
- package/assets/cultural-stickers/Music&Entertainment.png +0 -0
- package/assets/cultural-stickers/Nature&Wildlife.png +0 -0
- package/assets/cultural-stickers/Photography.png +0 -0
- package/assets/cultural-stickers/Science&Technology.png +0 -0
- package/assets/cultural-stickers/Spiritual.png +0 -0
- package/assets/cultural-stickers/Sport.png +0 -0
- package/assets/cultural-stickers/Stories&Journal.png +0 -0
- package/assets/cultural-stickers/Television&Cinema.png +0 -0
- package/assets/cultural-stickers/Travel&Adventure.png +0 -0
- package/assets/cultural-stickers/z.jpg (1).jpg +0 -0
- package/assets/figma-assets/landing-zo-logo.png +0 -0
- package/assets/images/rank1.jpeg +0 -0
- package/assets/index.ts +76 -0
- package/assets/lotties/loader.json +1216 -0
- package/assets/lotties/spinner.json +1 -0
- package/assets/videos/loading-screen-background.mp4 +0 -0
- package/assets/videos/opening-disks.mp4 +0 -0
- package/assets/wallet/constants.ts +38 -0
- package/assets/zo-coin.gif +0 -0
- package/assets/zo-fallback.png +0 -0
- package/dist/assets/index.d.mts +136 -0
- package/dist/assets/index.d.ts +136 -0
- package/dist/assets/index.js +133 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/assets/index.mjs +100 -0
- package/dist/assets/index.mjs.map +1 -0
- package/dist/index.d.mts +789 -0
- package/dist/index.d.ts +789 -0
- package/dist/index.js +1118 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1060 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react-native.d.mts +537 -0
- package/dist/react-native.d.ts +537 -0
- package/dist/react-native.js +1617 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +1588 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/react.d.mts +824 -0
- package/dist/react.d.ts +824 -0
- package/dist/react.js +3856 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +3801 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +112 -0
- package/scripts/init.js +196 -0
- package/scripts/postinstall.js +174 -0
- package/scripts/verify-build.js +121 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/components/ZoProgressRing.tsx
|
|
2
|
+
// Circular progress ring component
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
export interface ZoProgressRingProps {
|
|
7
|
+
/** Progress percentage (0-100) */
|
|
8
|
+
progress: number;
|
|
9
|
+
/** Size in pixels (default: 140) */
|
|
10
|
+
size?: number;
|
|
11
|
+
/** Stroke width in pixels (default: 4) */
|
|
12
|
+
strokeWidth?: number;
|
|
13
|
+
/** Primary stroke color (default: #FFFFFF) */
|
|
14
|
+
primaryColor?: string;
|
|
15
|
+
/** Secondary/background stroke color (default: rgba(255,255,255,0.2)) */
|
|
16
|
+
secondaryColor?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const ZoProgressRing: React.FC<ZoProgressRingProps> = ({
|
|
20
|
+
progress,
|
|
21
|
+
size = 140,
|
|
22
|
+
strokeWidth = 4,
|
|
23
|
+
primaryColor = '#FFFFFF',
|
|
24
|
+
secondaryColor = 'rgba(255,255,255,0.2)',
|
|
25
|
+
}) => {
|
|
26
|
+
const radius = (size - strokeWidth) / 2;
|
|
27
|
+
const circumference = radius * 2 * Math.PI;
|
|
28
|
+
const strokeDashoffset = circumference - (progress / 100) * circumference;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
|
|
32
|
+
{/* Background circle */}
|
|
33
|
+
<circle
|
|
34
|
+
cx={size / 2}
|
|
35
|
+
cy={size / 2}
|
|
36
|
+
r={radius}
|
|
37
|
+
strokeWidth={strokeWidth}
|
|
38
|
+
fill="none"
|
|
39
|
+
stroke={secondaryColor}
|
|
40
|
+
/>
|
|
41
|
+
{/* Progress circle */}
|
|
42
|
+
<circle
|
|
43
|
+
cx={size / 2}
|
|
44
|
+
cy={size / 2}
|
|
45
|
+
r={radius}
|
|
46
|
+
stroke={primaryColor}
|
|
47
|
+
strokeWidth={strokeWidth}
|
|
48
|
+
fill="none"
|
|
49
|
+
strokeDasharray={circumference}
|
|
50
|
+
strokeDashoffset={strokeDashoffset}
|
|
51
|
+
strokeLinecap="round"
|
|
52
|
+
style={{ transition: 'stroke-dashoffset 0.5s ease-in-out' }}
|
|
53
|
+
/>
|
|
54
|
+
</svg>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/components/index.ts
|
|
2
|
+
// Re-export all components
|
|
3
|
+
|
|
4
|
+
export { ZoPassportCard, type ZoPassportCardProps } from './ZoPassportCard';
|
|
5
|
+
export { ZoProgressRing, type ZoProgressRingProps } from './ZoProgressRing';
|
|
6
|
+
export { ZoAvatar, type ZoAvatarProps } from './ZoAvatar';
|
|
7
|
+
export { PhoneInput, type PhoneInputProps } from './PhoneInput';
|
|
8
|
+
export { OTPInput, type OTPInputProps } from './OTPInput';
|
|
9
|
+
export { ZoAuth, type ZoAuthProps } from './ZoAuth';
|
|
10
|
+
export { ZoLanding, type ZoLandingProps } from './ZoLanding';
|
|
11
|
+
export { ZoOnboarding, type ZoOnboardingProps } from './ZoOnboarding';
|
|
12
|
+
export { FounderBadge } from './FounderBadge';
|
|
13
|
+
|
|
14
|
+
// Wallet components
|
|
15
|
+
export * from './wallet';
|
|
16
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Moving Shine Effect - Extracted from Zostel app
|
|
2
|
+
import React, { memo, useEffect } from 'react';
|
|
3
|
+
import { View, Image, StyleSheet } from 'react-native';
|
|
4
|
+
import Animated, {
|
|
5
|
+
useSharedValue,
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
withRepeat,
|
|
8
|
+
withTiming,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
10
|
+
import { WALLET_ASSETS, ANIMATIONS } from '../../../assets/wallet/constants';
|
|
11
|
+
import { walletStyles } from './styles/walletStyles';
|
|
12
|
+
import type { MovingShineProps } from '../../lib/types/wallet';
|
|
13
|
+
|
|
14
|
+
export const MovingShine: React.FC<MovingShineProps> = memo(
|
|
15
|
+
({ duration = ANIMATIONS.shineDuration }) => {
|
|
16
|
+
const tx = useSharedValue(-100);
|
|
17
|
+
|
|
18
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
19
|
+
transform: [{ rotate: '30deg' }, { translateX: tx.value }],
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
tx.value = withRepeat(withTiming(420, { duration }), -1, false);
|
|
24
|
+
}, [duration]);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Animated.View style={[styles.shineEffect, animatedStyle]}>
|
|
28
|
+
<Image
|
|
29
|
+
source={{ uri: WALLET_ASSETS.shine }}
|
|
30
|
+
style={StyleSheet.absoluteFillObject}
|
|
31
|
+
resizeMode="cover"
|
|
32
|
+
/>
|
|
33
|
+
</Animated.View>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
MovingShine.displayName = 'MovingShine';
|
|
39
|
+
|
|
40
|
+
const styles = StyleSheet.create({
|
|
41
|
+
shineEffect: walletStyles.shineEffect,
|
|
42
|
+
});
|
|
43
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Transaction Item Component - Extracted from Zostel app
|
|
2
|
+
import React, { memo } from 'react';
|
|
3
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
4
|
+
import moment from 'moment';
|
|
5
|
+
import { ZoToken } from './ZoToken';
|
|
6
|
+
import { walletStyles } from './styles/walletStyles';
|
|
7
|
+
import { formatBalance, getTransactionColor } from '../../lib/utils/wallet';
|
|
8
|
+
import type { TransactionItemProps } from '../../lib/types/wallet';
|
|
9
|
+
|
|
10
|
+
const StatusIcon: React.FC = memo(() => {
|
|
11
|
+
return (
|
|
12
|
+
<View style={styles.iconBgTilted}>
|
|
13
|
+
<Text style={styles.iconText}>←</Text>
|
|
14
|
+
</View>
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
StatusIcon.displayName = 'StatusIcon';
|
|
19
|
+
|
|
20
|
+
export const TransactionItem: React.FC<TransactionItemProps> = memo(
|
|
21
|
+
({ transaction, showDate = true }) => {
|
|
22
|
+
const color = getTransactionColor(transaction.action);
|
|
23
|
+
const formattedAmount = formatBalance(transaction.amount);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<View style={styles.txnRow}>
|
|
27
|
+
<StatusIcon />
|
|
28
|
+
|
|
29
|
+
<View style={styles.flex}>
|
|
30
|
+
<Text style={styles.description}>{transaction.description}</Text>
|
|
31
|
+
{showDate && transaction.claimed_at && (
|
|
32
|
+
<Text style={styles.date}>
|
|
33
|
+
{moment(transaction.claimed_at).format('DD MMM hh:mm A')}
|
|
34
|
+
</Text>
|
|
35
|
+
)}
|
|
36
|
+
</View>
|
|
37
|
+
|
|
38
|
+
<View style={styles.tokenContainer}>
|
|
39
|
+
<Text
|
|
40
|
+
style={[
|
|
41
|
+
styles.textShadow,
|
|
42
|
+
{
|
|
43
|
+
shadowColor: color,
|
|
44
|
+
color: color,
|
|
45
|
+
fontWeight: '600',
|
|
46
|
+
fontSize: 16,
|
|
47
|
+
},
|
|
48
|
+
]}
|
|
49
|
+
>
|
|
50
|
+
+ {formattedAmount}
|
|
51
|
+
</Text>
|
|
52
|
+
<ZoToken size={16} />
|
|
53
|
+
</View>
|
|
54
|
+
</View>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
TransactionItem.displayName = 'TransactionItem';
|
|
60
|
+
|
|
61
|
+
const styles = StyleSheet.create({
|
|
62
|
+
txnRow: walletStyles.txnRow,
|
|
63
|
+
iconBgTilted: walletStyles.iconBgTilted,
|
|
64
|
+
tokenContainer: walletStyles.tokenContainer,
|
|
65
|
+
textShadow: walletStyles.textShadow,
|
|
66
|
+
flex: {
|
|
67
|
+
flex: 1,
|
|
68
|
+
},
|
|
69
|
+
iconText: {
|
|
70
|
+
color: 'rgba(255, 255, 255, 0.6)',
|
|
71
|
+
fontSize: 16,
|
|
72
|
+
},
|
|
73
|
+
description: {
|
|
74
|
+
color: '#FFFFFF',
|
|
75
|
+
fontSize: 16,
|
|
76
|
+
fontWeight: '500',
|
|
77
|
+
},
|
|
78
|
+
date: {
|
|
79
|
+
color: 'rgba(255, 255, 255, 0.44)',
|
|
80
|
+
fontSize: 12,
|
|
81
|
+
marginTop: 2,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Transaction List Component - Extracted from Zostel app
|
|
2
|
+
import React, { memo } from 'react';
|
|
3
|
+
import { View, ScrollView, Text, ActivityIndicator, StyleSheet } from 'react-native';
|
|
4
|
+
import { TransactionItem } from './TransactionItem';
|
|
5
|
+
import { walletStyles } from './styles/walletStyles';
|
|
6
|
+
import type { TransactionListProps } from '../../lib/types/wallet';
|
|
7
|
+
|
|
8
|
+
export const TransactionList: React.FC<TransactionListProps> = memo(
|
|
9
|
+
({ transactions, isLoading, onEndReached }) => {
|
|
10
|
+
if (isLoading && !transactions?.length) {
|
|
11
|
+
return (
|
|
12
|
+
<View style={styles.loader}>
|
|
13
|
+
<ActivityIndicator size="large" color="#00C853" />
|
|
14
|
+
</View>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!transactions?.length) {
|
|
19
|
+
return (
|
|
20
|
+
<View style={styles.emptyState}>
|
|
21
|
+
<Text style={styles.emptyText}>No transactions yet</Text>
|
|
22
|
+
<Text style={styles.emptySubtext}>
|
|
23
|
+
Complete quests to earn $Zo tokens
|
|
24
|
+
</Text>
|
|
25
|
+
</View>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<View style={styles.txnContent}>
|
|
31
|
+
{transactions.map((transaction, index) => (
|
|
32
|
+
<TransactionItem key={transaction.id || index} transaction={transaction} />
|
|
33
|
+
))}
|
|
34
|
+
</View>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
TransactionList.displayName = 'TransactionList';
|
|
40
|
+
|
|
41
|
+
const styles = StyleSheet.create({
|
|
42
|
+
txnContent: walletStyles.txnContent,
|
|
43
|
+
loader: {
|
|
44
|
+
padding: 40,
|
|
45
|
+
alignItems: 'center',
|
|
46
|
+
justifyContent: 'center',
|
|
47
|
+
},
|
|
48
|
+
emptyState: {
|
|
49
|
+
padding: 40,
|
|
50
|
+
alignItems: 'center',
|
|
51
|
+
justifyContent: 'center',
|
|
52
|
+
},
|
|
53
|
+
emptyText: {
|
|
54
|
+
color: '#FFFFFF',
|
|
55
|
+
fontSize: 18,
|
|
56
|
+
fontWeight: '600',
|
|
57
|
+
marginBottom: 8,
|
|
58
|
+
},
|
|
59
|
+
emptySubtext: {
|
|
60
|
+
color: 'rgba(255, 255, 255, 0.44)',
|
|
61
|
+
fontSize: 14,
|
|
62
|
+
textAlign: 'center',
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Wallet Card Component - Extracted from Zostel app
|
|
2
|
+
import React, { memo, useEffect } from 'react';
|
|
3
|
+
import { View, Text, Image, Pressable, StyleSheet } from 'react-native';
|
|
4
|
+
import Animated, {
|
|
5
|
+
useSharedValue,
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
withTiming,
|
|
8
|
+
FadeIn,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
10
|
+
import { MovingShine } from './MovingShine';
|
|
11
|
+
import { ZoTokenVideo } from './ZoToken';
|
|
12
|
+
import { WALLET_ASSETS, ANIMATIONS } from '../../../assets/wallet/constants';
|
|
13
|
+
import { walletStyles } from './styles/walletStyles';
|
|
14
|
+
import { formatBalance, formatWalletAddress, formatNickname } from '../../lib/utils/wallet';
|
|
15
|
+
import type { WalletCardProps } from '../../lib/types/wallet';
|
|
16
|
+
|
|
17
|
+
export const WalletCard: React.FC<WalletCardProps> = memo(
|
|
18
|
+
({ balance, user, isOpen = false, onToggle, isLoading }) => {
|
|
19
|
+
const bgY = useSharedValue(0);
|
|
20
|
+
const cardY = useSharedValue(0);
|
|
21
|
+
|
|
22
|
+
const animatedBackgroundStyle = useAnimatedStyle(() => ({
|
|
23
|
+
transform: [{ translateY: bgY.value }],
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
const animatedCardStyle = useAnimatedStyle(() => ({
|
|
27
|
+
transform: [{ translateY: cardY.value }],
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
bgY.value = withTiming(isOpen ? 200 : 0, { duration: ANIMATIONS.cardTransition });
|
|
32
|
+
cardY.value = withTiming(isOpen ? -150 : 0, { duration: ANIMATIONS.cardTransition });
|
|
33
|
+
}, [isOpen]);
|
|
34
|
+
|
|
35
|
+
const displayName = user.nickname
|
|
36
|
+
? formatNickname(user.nickname)
|
|
37
|
+
: user.first_name || 'You';
|
|
38
|
+
|
|
39
|
+
const walletText = `${displayName}'s wallet`;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Pressable
|
|
43
|
+
style={styles.cardPressContainer}
|
|
44
|
+
onPress={onToggle}
|
|
45
|
+
>
|
|
46
|
+
<Animated.View style={[styles.card, animatedBackgroundStyle]}>
|
|
47
|
+
{/* Background Image */}
|
|
48
|
+
<Image
|
|
49
|
+
source={{ uri: WALLET_ASSETS.walletBackground }}
|
|
50
|
+
style={StyleSheet.absoluteFillObject}
|
|
51
|
+
resizeMode="contain"
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
{/* Shadow */}
|
|
55
|
+
<Animated.View
|
|
56
|
+
style={styles.cardShadow}
|
|
57
|
+
entering={FadeIn.duration(ANIMATIONS.fadeInDuration)}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
{/* Card Content */}
|
|
61
|
+
<Animated.View style={[styles.cardContainer, animatedCardStyle]}>
|
|
62
|
+
<View style={styles.cardContent}>
|
|
63
|
+
{/* Balance Row */}
|
|
64
|
+
<View style={styles.balanceRow}>
|
|
65
|
+
<View style={styles.balanceWrapper}>
|
|
66
|
+
<Text style={[styles.whiteText, styles.balanceAmount]}>
|
|
67
|
+
{formatBalance(balance)}
|
|
68
|
+
</Text>
|
|
69
|
+
<Text style={[styles.grayText, styles.currency]}>
|
|
70
|
+
$Zo
|
|
71
|
+
</Text>
|
|
72
|
+
</View>
|
|
73
|
+
<ZoTokenVideo size={24} />
|
|
74
|
+
</View>
|
|
75
|
+
|
|
76
|
+
<View style={styles.flex} />
|
|
77
|
+
|
|
78
|
+
{/* User Info */}
|
|
79
|
+
{user && (
|
|
80
|
+
<View style={styles.avatarInfo}>
|
|
81
|
+
{user.avatar?.image && (
|
|
82
|
+
<Image
|
|
83
|
+
source={{ uri: user.avatar.image }}
|
|
84
|
+
style={styles.avatar}
|
|
85
|
+
/>
|
|
86
|
+
)}
|
|
87
|
+
<View style={styles.flex}>
|
|
88
|
+
<Text style={[styles.whiteText, styles.userName]}>
|
|
89
|
+
{displayName}
|
|
90
|
+
</Text>
|
|
91
|
+
<Text style={[styles.grayText, styles.walletAddress]}>
|
|
92
|
+
{formatWalletAddress(user.wallet_address || '')}
|
|
93
|
+
</Text>
|
|
94
|
+
</View>
|
|
95
|
+
</View>
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
{/* Shine Effect */}
|
|
99
|
+
<View style={styles.shineContainer}>
|
|
100
|
+
<MovingShine />
|
|
101
|
+
</View>
|
|
102
|
+
</View>
|
|
103
|
+
</Animated.View>
|
|
104
|
+
|
|
105
|
+
{/* Card Cover */}
|
|
106
|
+
<View style={styles.cardCover}>
|
|
107
|
+
<Image
|
|
108
|
+
source={{ uri: WALLET_ASSETS.walletCover }}
|
|
109
|
+
style={StyleSheet.absoluteFillObject}
|
|
110
|
+
resizeMode="cover"
|
|
111
|
+
/>
|
|
112
|
+
<View style={styles.cardCoverTextContainer}>
|
|
113
|
+
<Text style={[styles.grayText, styles.cardCoverText]}>
|
|
114
|
+
{walletText}
|
|
115
|
+
</Text>
|
|
116
|
+
</View>
|
|
117
|
+
</View>
|
|
118
|
+
</Animated.View>
|
|
119
|
+
</Pressable>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
WalletCard.displayName = 'WalletCard';
|
|
125
|
+
|
|
126
|
+
const styles = StyleSheet.create({
|
|
127
|
+
...walletStyles,
|
|
128
|
+
balanceAmount: {
|
|
129
|
+
fontSize: 24,
|
|
130
|
+
fontWeight: '700',
|
|
131
|
+
},
|
|
132
|
+
currency: {
|
|
133
|
+
fontSize: 12,
|
|
134
|
+
},
|
|
135
|
+
userName: {
|
|
136
|
+
fontSize: 16,
|
|
137
|
+
fontWeight: '600',
|
|
138
|
+
},
|
|
139
|
+
walletAddress: {
|
|
140
|
+
fontSize: 12,
|
|
141
|
+
},
|
|
142
|
+
avatar: {
|
|
143
|
+
width: 32,
|
|
144
|
+
height: 32,
|
|
145
|
+
borderRadius: 16,
|
|
146
|
+
},
|
|
147
|
+
cardCoverText: {
|
|
148
|
+
fontSize: 16,
|
|
149
|
+
fontWeight: '600',
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Wallet Screen Component - Complete wallet screen from Zostel app
|
|
2
|
+
import React, { useState, useCallback, useMemo, memo } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
View,
|
|
5
|
+
Text,
|
|
6
|
+
ScrollView,
|
|
7
|
+
Pressable,
|
|
8
|
+
ActivityIndicator,
|
|
9
|
+
StyleSheet,
|
|
10
|
+
SafeAreaView,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import Animated, { FadeIn, FadeOut, FadeInUp, FadeOutUp, FadeInDown } from 'react-native-reanimated';
|
|
13
|
+
import { WalletCard } from './WalletCard';
|
|
14
|
+
import { TransactionList } from './TransactionList';
|
|
15
|
+
import { walletStyles } from './styles/walletStyles';
|
|
16
|
+
import type { WalletScreenProps } from '../../lib/types/wallet';
|
|
17
|
+
|
|
18
|
+
// Mock data for demo
|
|
19
|
+
const MOCK_USER = {
|
|
20
|
+
avatar: {
|
|
21
|
+
image: 'https://i.pravatar.cc/150?img=12',
|
|
22
|
+
},
|
|
23
|
+
first_name: 'John',
|
|
24
|
+
nickname: '@johndoe',
|
|
25
|
+
wallet_address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const MOCK_TRANSACTIONS = [
|
|
29
|
+
{
|
|
30
|
+
id: '1',
|
|
31
|
+
created_at: '2025-12-10T10:30:00Z',
|
|
32
|
+
updated_at: '2025-12-10T10:30:00Z',
|
|
33
|
+
amount: 100,
|
|
34
|
+
description: 'Completed profile',
|
|
35
|
+
claimed_at: '2025-12-10T10:30:00Z',
|
|
36
|
+
grant: {
|
|
37
|
+
id: 'g1',
|
|
38
|
+
name: 'Profile Completion',
|
|
39
|
+
description: 'Complete your profile',
|
|
40
|
+
},
|
|
41
|
+
action: 'deposit' as const,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: '2',
|
|
45
|
+
created_at: '2025-12-09T15:20:00Z',
|
|
46
|
+
updated_at: '2025-12-09T15:20:00Z',
|
|
47
|
+
amount: 250,
|
|
48
|
+
description: 'Quest: Explore 3 locations',
|
|
49
|
+
claimed_at: '2025-12-09T15:20:00Z',
|
|
50
|
+
grant: {
|
|
51
|
+
id: 'g2',
|
|
52
|
+
name: 'Explorer Quest',
|
|
53
|
+
description: 'Visit locations',
|
|
54
|
+
},
|
|
55
|
+
action: 'deposit' as const,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: '3',
|
|
59
|
+
created_at: '2025-12-08T09:15:00Z',
|
|
60
|
+
updated_at: '2025-12-08T09:15:00Z',
|
|
61
|
+
amount: 500,
|
|
62
|
+
description: 'Booking reward',
|
|
63
|
+
claimed_at: '2025-12-08T09:15:00Z',
|
|
64
|
+
grant: {
|
|
65
|
+
id: 'g3',
|
|
66
|
+
name: 'Booking Reward',
|
|
67
|
+
description: 'Book a stay',
|
|
68
|
+
},
|
|
69
|
+
action: 'deposit' as const,
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
export const WalletScreen: React.FC<WalletScreenProps> = memo(({ onBack }) => {
|
|
74
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
75
|
+
const [isTitleVisible, setIsTitleVisible] = useState(false);
|
|
76
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
77
|
+
|
|
78
|
+
// In real implementation, fetch from API
|
|
79
|
+
const balance = 850;
|
|
80
|
+
const user = MOCK_USER;
|
|
81
|
+
const transactions = MOCK_TRANSACTIONS;
|
|
82
|
+
|
|
83
|
+
const toggleView = useCallback(() => {
|
|
84
|
+
setIsOpen((prev) => !prev);
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
const handleScroll = useCallback((event: any) => {
|
|
88
|
+
const scrollY = event.nativeEvent.contentOffset.y;
|
|
89
|
+
setIsTitleVisible(scrollY > 200);
|
|
90
|
+
}, []);
|
|
91
|
+
|
|
92
|
+
const backdrop = useMemo(
|
|
93
|
+
() =>
|
|
94
|
+
isOpen ? (
|
|
95
|
+
<Pressable onPress={toggleView} style={styles.openBg}>
|
|
96
|
+
<View />
|
|
97
|
+
</Pressable>
|
|
98
|
+
) : null,
|
|
99
|
+
[isOpen, toggleView]
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const description = useMemo(
|
|
103
|
+
() =>
|
|
104
|
+
isOpen ? (
|
|
105
|
+
<Animated.View entering={FadeIn} exiting={FadeOut} style={styles.zoDescriptionContainer}>
|
|
106
|
+
<Text style={styles.description}>
|
|
107
|
+
Get Zo World coins as airdrop by completing quests, stay & trips.
|
|
108
|
+
</Text>
|
|
109
|
+
</Animated.View>
|
|
110
|
+
) : null,
|
|
111
|
+
[isOpen]
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const walletText = `${user.nickname || user.first_name}'s wallet`;
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<View style={styles.screen}>
|
|
118
|
+
{/* Header */}
|
|
119
|
+
<View style={styles.header}>
|
|
120
|
+
<Pressable onPress={onBack}>
|
|
121
|
+
<Text style={styles.backButton}>←</Text>
|
|
122
|
+
</Pressable>
|
|
123
|
+
<Animated.View pointerEvents="none" style={styles.titleContainer}>
|
|
124
|
+
{isTitleVisible && (
|
|
125
|
+
<Animated.View entering={FadeInUp} exiting={FadeOutUp}>
|
|
126
|
+
<Text style={[styles.whiteText, styles.headerTitle]}>
|
|
127
|
+
{walletText}
|
|
128
|
+
</Text>
|
|
129
|
+
</Animated.View>
|
|
130
|
+
)}
|
|
131
|
+
</Animated.View>
|
|
132
|
+
</View>
|
|
133
|
+
|
|
134
|
+
{/* Content */}
|
|
135
|
+
<ScrollView
|
|
136
|
+
contentContainerStyle={styles.container}
|
|
137
|
+
onScroll={handleScroll}
|
|
138
|
+
scrollEventThrottle={16}
|
|
139
|
+
showsVerticalScrollIndicator={false}
|
|
140
|
+
>
|
|
141
|
+
{isLoading ? (
|
|
142
|
+
<Animated.View key="loader" entering={FadeIn} exiting={FadeOut} style={styles.loader}>
|
|
143
|
+
<ActivityIndicator size="large" color="#00C853" />
|
|
144
|
+
</Animated.View>
|
|
145
|
+
) : (
|
|
146
|
+
<Animated.View entering={FadeInDown} style={styles.container}>
|
|
147
|
+
<SafeAreaView />
|
|
148
|
+
|
|
149
|
+
{/* Transactions */}
|
|
150
|
+
<TransactionList transactions={transactions} isLoading={false} />
|
|
151
|
+
|
|
152
|
+
{/* Backdrop */}
|
|
153
|
+
{backdrop}
|
|
154
|
+
|
|
155
|
+
{/* Wallet Card */}
|
|
156
|
+
<WalletCard
|
|
157
|
+
balance={balance}
|
|
158
|
+
user={user}
|
|
159
|
+
isOpen={isOpen}
|
|
160
|
+
onToggle={toggleView}
|
|
161
|
+
isLoading={isLoading}
|
|
162
|
+
/>
|
|
163
|
+
|
|
164
|
+
<View style={styles.bar} />
|
|
165
|
+
<SafeAreaView />
|
|
166
|
+
</Animated.View>
|
|
167
|
+
)}
|
|
168
|
+
</ScrollView>
|
|
169
|
+
|
|
170
|
+
{/* Description */}
|
|
171
|
+
{description}
|
|
172
|
+
</View>
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
WalletScreen.displayName = 'WalletScreen';
|
|
177
|
+
|
|
178
|
+
const styles = StyleSheet.create({
|
|
179
|
+
...walletStyles,
|
|
180
|
+
backButton: {
|
|
181
|
+
color: '#FFFFFF',
|
|
182
|
+
fontSize: 24,
|
|
183
|
+
fontWeight: '600',
|
|
184
|
+
},
|
|
185
|
+
headerTitle: {
|
|
186
|
+
fontSize: 18,
|
|
187
|
+
fontWeight: '600',
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// $Zo Token Components - Extracted from Zostel app
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { View, StyleSheet } from 'react-native';
|
|
4
|
+
import Svg, { Circle, Defs, LinearGradient, Stop, Path } from 'react-native-svg';
|
|
5
|
+
|
|
6
|
+
interface ZoTokenProps {
|
|
7
|
+
size?: number;
|
|
8
|
+
style?: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Static $Zo Token Icon
|
|
13
|
+
*/
|
|
14
|
+
export const ZoToken: React.FC<ZoTokenProps> = ({ size = 16, style }) => {
|
|
15
|
+
return (
|
|
16
|
+
<View style={[{ width: size, height: size }, style]}>
|
|
17
|
+
<Svg width={size} height={size} viewBox="0 0 32 32" fill="none">
|
|
18
|
+
<Defs>
|
|
19
|
+
<LinearGradient id="zoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
20
|
+
<Stop offset="0%" stopColor="#00E676" stopOpacity="1" />
|
|
21
|
+
<Stop offset="100%" stopColor="#00C853" stopOpacity="1" />
|
|
22
|
+
</LinearGradient>
|
|
23
|
+
</Defs>
|
|
24
|
+
<Circle cx="16" cy="16" r="16" fill="url(#zoGradient)" />
|
|
25
|
+
<Path
|
|
26
|
+
d="M9 11h14l-10 10h10"
|
|
27
|
+
stroke="white"
|
|
28
|
+
strokeWidth="2"
|
|
29
|
+
strokeLinecap="round"
|
|
30
|
+
strokeLinejoin="round"
|
|
31
|
+
/>
|
|
32
|
+
</Svg>
|
|
33
|
+
</View>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Animated $Zo Token Video Component
|
|
39
|
+
* Note: In production this uses a video/lottie animation
|
|
40
|
+
* This is a simplified static version
|
|
41
|
+
*/
|
|
42
|
+
export const ZoTokenVideo: React.FC<ZoTokenProps> = ({ size = 24, style }) => {
|
|
43
|
+
return (
|
|
44
|
+
<View
|
|
45
|
+
style={[
|
|
46
|
+
{
|
|
47
|
+
width: size,
|
|
48
|
+
height: size,
|
|
49
|
+
borderRadius: size,
|
|
50
|
+
overflow: 'hidden',
|
|
51
|
+
backgroundColor: '#00C853',
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
justifyContent: 'center',
|
|
54
|
+
},
|
|
55
|
+
style,
|
|
56
|
+
]}
|
|
57
|
+
>
|
|
58
|
+
<ZoToken size={size * 0.6} />
|
|
59
|
+
</View>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const styles = StyleSheet.create({
|
|
64
|
+
container: {
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
justifyContent: 'center',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Export all wallet components
|
|
2
|
+
export { WalletScreen } from './WalletScreen';
|
|
3
|
+
export { WalletCard } from './WalletCard';
|
|
4
|
+
export { TransactionList } from './TransactionList';
|
|
5
|
+
export { TransactionItem } from './TransactionItem';
|
|
6
|
+
export { MovingShine } from './MovingShine';
|
|
7
|
+
export { ZoToken, ZoTokenVideo } from './ZoToken';
|
|
8
|
+
|