Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions apps/mobile/src/components/ProfileLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// components/ProfileLink.tsx

import React from 'react';
import {
Linking,
Pressable,
Text,
View,
StyleSheet,
} from 'react-native';

type ProfileLinkProps = {
platform: string;
username: string;
url: string;
onPress?: () => void;
};
import {
COLORS,
SPACING,
FONT_SIZE,
BORDER_RADIUS,
} from '../theme/tokens';
export default function ProfileLink({
platform,
username,
url,
onPress,
}: ProfileLinkProps) {
const handlePress = () => {
if (onPress) {
onPress();
return;
}

Linking.openURL(url);
};

return (
<Pressable style={styles.card} onPress={handlePress}>
<View>
<Text style={styles.platform}>{platform}</Text>
<Text style={styles.username}>{username}</Text>
</View>

<Text style={styles.link}>Open</Text>
</Pressable>
Comment on lines +24 to +47
);
}

const styles = StyleSheet.create({
card: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: SPACING.md,
borderRadius: BORDER_RADIUS.md,
backgroundColor: COLORS.bgCard,
marginBottom: SPACING.sm,
},

platform: {
fontSize: FONT_SIZE.md,
fontWeight: '600',
color: COLORS.textPrimary,
},

username: {
marginTop: SPACING.xs,
fontSize: FONT_SIZE.sm,
color: COLORS.textMuted,
},

link: {
fontSize: FONT_SIZE.sm,
fontWeight: '600',
color: COLORS.primary,
},
});
157 changes: 83 additions & 74 deletions apps/mobile/src/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { PLATFORMS } from '@devcard/shared';
import { APP_URL, API_BASE_URL } from '../config';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RootStackParamList } from '../navigation/MainTabs';
import ProfileLink from '../components/ProfileLink';


Comment on lines 18 to 23
type Props = {
navigation: NativeStackNavigationProp<RootStackParamList>;
Expand All @@ -38,7 +40,7 @@ export default function HomeScreen({ navigation }: Props) {
const [showQR, setShowQR] = useState(false);
const [refreshing, setRefreshing] = useState(false);

const profileUrl = user?.defaultCardId
const profileUrl = user?.defaultCardId
? `${APP_URL}/devcard/${user.defaultCardId}`
: `${APP_URL}/u/${user?.username}`;

Expand Down Expand Up @@ -133,95 +135,98 @@ export default function HomeScreen({ navigation }: Props) {

{user?.bio && <Text style={styles.bio}>{user.bio}</Text>}

{/* Platform Links Summary */}
<View style={styles.linksSummary}>
{/* Platform Links */}
<View style={styles.linksContainer}>
{links.slice(0, 4).map(link => {
const platform = PLATFORMS[link.platform];

return (
<View key={link.id} style={styles.linkBadge}>
<Text style={styles.linkBadgeText}>
{platform?.name || link.platform}
</Text>
</View>
<ProfileLink
key={link.id}
platform={platform?.name || link.platform}
username={link.username}
url={link.url}
/>
Comment on lines +144 to +149
);
})}
{links.length > 4 && (
<View style={styles.linkBadge}>
<Text style={styles.linkBadgeText}>+{links.length - 4}</Text>
</View>
)}
</View>
{links.length > 4 && (
<View style={styles.linkBadge}>
<Text style={styles.linkBadgeText}>+{links.length - 4}</Text>
</View>
)}
</View>
</View>

{/* QR Code Section */}
{/* QR Code Section */}
<TouchableOpacity
style={styles.qrSection}
onPress={() => setShowQR(!showQR)}
activeOpacity={0.85}>
{showQR ? (
<View style={styles.qrContainer}>
<QRCode
value={profileUrl}
size={200}
color={COLORS.textPrimary}
backgroundColor={COLORS.bgCard}
/>
<Text style={styles.qrHint}>Scan to open your DevCard</Text>
</View>
) : (
<View style={styles.qrToggle}>
<Text style={styles.qrToggleEmoji}>📱</Text>
<Text style={styles.qrToggleText}>Tap to show QR code</Text>
</View>
)}
</TouchableOpacity>

{/* Action Buttons */}
<View style={styles.actions}>
<TouchableOpacity
style={styles.qrSection}
onPress={() => setShowQR(!showQR)}
style={styles.actionButton}
onPress={handleShare}
activeOpacity={0.85}>
{showQR ? (
<View style={styles.qrContainer}>
<QRCode
value={profileUrl}
size={200}
color={COLORS.textPrimary}
backgroundColor={COLORS.bgCard}
/>
<Text style={styles.qrHint}>Scan to open your DevCard</Text>
</View>
) : (
<View style={styles.qrToggle}>
<Text style={styles.qrToggleEmoji}>📱</Text>
<Text style={styles.qrToggleText}>Tap to show QR code</Text>
</View>
)}
<Text style={styles.actionEmoji}>📤</Text>
<Text style={styles.actionText}>Share Card</Text>
</TouchableOpacity>

{/* Action Buttons */}
<View style={styles.actions}>
<TouchableOpacity
style={styles.actionButton}
onPress={handleShare}
activeOpacity={0.85}>
<Text style={styles.actionEmoji}>📤</Text>
<Text style={styles.actionText}>Share Card</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.actionButton}
onPress={() => (navigation as any).navigate('Views')}
activeOpacity={0.85}>
<Text style={styles.actionEmoji}>📈</Text>
<Text style={styles.actionText}>Analytics</Text>
</TouchableOpacity>

<TouchableOpacity
style={styles.actionButton}
onPress={() => (navigation as any).navigate('Views')}
activeOpacity={0.85}>
<Text style={styles.actionEmoji}>📈</Text>
<Text style={styles.actionText}>Analytics</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.actionButton}
onPress={() => (navigation as any).navigate('DevCardView', { username: user?.username || '' })}
activeOpacity={0.85}>
<Text style={styles.actionEmoji}>👁️</Text>
<Text style={styles.actionText}>Preview</Text>
</TouchableOpacity>
</View>

<TouchableOpacity
style={styles.actionButton}
onPress={() => (navigation as any).navigate('DevCardView', { username: user?.username || '' })}
activeOpacity={0.85}>
<Text style={styles.actionEmoji}>👁️</Text>
<Text style={styles.actionText}>Preview</Text>
</TouchableOpacity>
{/* Stats */}
<View style={styles.stats}>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{links.length}</Text>
<Text style={styles.statLabel}>Links</Text>
</View>

{/* Stats */}
<View style={styles.stats}>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{links.length}</Text>
<Text style={styles.statLabel}>Links</Text>
</View>
<View style={styles.statDivider} />
<View style={styles.statItem}>
<Text style={styles.statNumber}>{analytics?.totalViews || 0}</Text>
<Text style={styles.statLabel}>Views</Text>
</View>
<View style={styles.statDivider} />
<View style={styles.statItem}>
<Text style={styles.statNumber}>{analytics?.followsCount || 0}</Text>
<Text style={styles.statLabel}>Follows</Text>
</View>
<View style={styles.statDivider} />
<View style={styles.statItem}>
<Text style={styles.statNumber}>{analytics?.totalViews || 0}</Text>
<Text style={styles.statLabel}>Views</Text>
</View>
<View style={styles.statDivider} />
<View style={styles.statItem}>
<Text style={styles.statNumber}>{analytics?.followsCount || 0}</Text>
<Text style={styles.statLabel}>Follows</Text>
</View>
</ScrollView>
</SafeAreaView>
</View>
</ScrollView>
</SafeAreaView >
);
}

Expand Down Expand Up @@ -299,4 +304,8 @@ const styles = StyleSheet.create({
statNumber: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.primary },
statLabel: { fontSize: FONT_SIZE.xs, color: COLORS.textMuted, marginTop: 4 },
statDivider: { width: 1, backgroundColor: COLORS.border },
linksContainer: {
marginTop: SPACING.md,
gap: SPACING.sm,
},
Comment on lines +308 to +310
});