diff --git a/crux-frontend/.env b/crux-frontend/.env
new file mode 100644
index 0000000..85af4f3
--- /dev/null
+++ b/crux-frontend/.env
@@ -0,0 +1,2 @@
+EXPO_PUBLIC_COGNITO_USER_POOL_ID=eu-west-2_6fPDsUm5T
+EXPO_PUBLIC_COGNITO_CLIENT_ID=samt4bt8omlj5o4jj8vm1o9ig
\ No newline at end of file
diff --git a/crux-frontend/app.json b/crux-frontend/app.json
index 373cc4b..3976ead 100644
--- a/crux-frontend/app.json
+++ b/crux-frontend/app.json
@@ -31,14 +31,12 @@
"plugins": [
"expo-router",
[
- "expo-build-properties",
+ "expo-splash-screen",
{
- "ios": {
- "newArchEnabled": true
- },
- "android": {
- "newArchEnabled": true
- }
+ "image": "./assets/images/splash.png",
+ "imageWidth": 200,
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff"
}
]
],
diff --git a/crux-frontend/app/(authenticated)/(tabs)/_layout.tsx b/crux-frontend/app/(authenticated)/(tabs)/_layout.tsx
index 561a8c6..86b2dd0 100644
--- a/crux-frontend/app/(authenticated)/(tabs)/_layout.tsx
+++ b/crux-frontend/app/(authenticated)/(tabs)/_layout.tsx
@@ -3,7 +3,7 @@ import {Tabs} from 'expo-router';
import {Home, Folder, Plus, Edit2, User} from 'lucide-react-native';
const {width} = Dimensions.get('window');
-const TAB_BAR_WIDTH = width * 0.8; // 90% of screen width
+const TAB_BAR_WIDTH = width * 0.8; // 80% of screen width
import {BottomTabBarProps} from '@react-navigation/bottom-tabs';
diff --git a/crux-frontend/app/(authenticated)/(tabs)/home.tsx b/crux-frontend/app/(authenticated)/(tabs)/home.tsx
index e2deeb3..e9d854e 100644
--- a/crux-frontend/app/(authenticated)/(tabs)/home.tsx
+++ b/crux-frontend/app/(authenticated)/(tabs)/home.tsx
@@ -1,4 +1,3 @@
-import React from 'react';
import {
View,
Text,
@@ -7,10 +6,13 @@ import {
TouchableOpacity,
StyleSheet,
Dimensions,
+ Platform,
+ KeyboardAvoidingView,
} from 'react-native';
import {Plus, ChevronRight} from 'lucide-react-native';
import Header from '@/components/Header';
-import HorizontalAccordion from '@/components/horizontal-accordion';
+import {HorizontalAccordion} from '@/components/HorizontalAccordion';
+import {Card} from '@/components/Card';
const {width} = Dimensions.get('window');
const CARD_WIDTH = (width - 48) / 2;
@@ -68,94 +70,100 @@ export default function HomeScreen() {
],
};
- const renderFolderCard = (folder: (typeof folders)[0]) => (
-
- {folder.avatar ? (
-
-
- {folder.type}
-
- ) : (
- {folder.type}
- )}
-
- {folder.name}
- {folder.photos} photos
-
-
-
- );
+ const keyboardVerticalOffset = Platform.OS === 'ios' ? 80 : 0;
return (
-
-
-
-
- Camera roll
-
+
+
+
- Legacy folders
- {folders.map(renderFolderCard)}
+
+ Camera roll
+
-
- Nominee Progress
-
-
+ Legacy folders
+
+ {folders.map((fold) => (
+
+ ))}
- 38% left
-
- Your nominees
-
-
-
-
- {nominees.map((nominee) => (
-
- ))}
-
+
+ Nominee Progress
+
+
+
+ 38% left
+
- Recents
- {folders.map(renderFolderCard)}
+ Your nominees
+
+
+
+
+ {nominees.map((nominee) => (
+
+ ))}
+
-
-
- {storage.breakdown.map((item) => (
-
-
- {item.type}
- {item.size}
-
+ Recents
+
+ {folders.map((fold) => (
+
))}
-
-
- 0.3 GB
- / 1 GB
+
+
+
+ {storage.breakdown.map((item) => (
+
+
+ {item.type}
+ {item.size}
+
+ ))}
+
+
+ 0.3 GB
+ / 1 GB
+
+
+
+ Upgrade storage ↑
+
-
- Upgrade storage ↑
-
-
-
-
+
+
+
);
}
diff --git a/crux-frontend/app/(authenticated)/storage.tsx b/crux-frontend/app/(authenticated)/storage.tsx
index cfbdbf2..0e835c4 100644
--- a/crux-frontend/app/(authenticated)/storage.tsx
+++ b/crux-frontend/app/(authenticated)/storage.tsx
@@ -1,16 +1,15 @@
-import React from "react";
-import { View, Text, Pressable, StyleSheet } from "react-native";
-import { ChevronRight, Trash2 } from "lucide-react-native";
-import Header from "@/components/Header";
+import {View, Text, Pressable, StyleSheet} from 'react-native';
+import {ChevronRight, Trash2} from 'lucide-react-native';
+import Header from '@/components/Header';
export default function StorageScreen() {
const storageData = {
used: 0.3,
free: 0.7,
breakdown: [
- { type: "Video", size: "0.2MB", color: "#4A55A2" },
- { type: "Photo", size: "0.1MB", color: "#FF4081" },
- { type: "Files", size: "0.1MB", color: "#00C853" },
+ {type: 'Video', size: '0.2MB', color: '#4A55A2'},
+ {type: 'Photo', size: '0.1MB', color: '#FF4081'},
+ {type: 'Files', size: '0.1MB', color: '#00C853'},
],
};
@@ -53,7 +52,7 @@ export default function StorageScreen() {
{storageData.breakdown.map((item, index) => (
-
+
{item.type}
@@ -80,81 +79,81 @@ export default function StorageScreen() {
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: "#f8f9fa",
+ backgroundColor: '#f8f9fa',
},
content: {
padding: 16,
},
title: {
fontSize: 28,
- fontWeight: "bold",
+ fontWeight: 'bold',
marginBottom: 8,
},
subtitle: {
fontSize: 16,
- color: "#666",
+ color: '#666',
marginBottom: 24,
},
card: {
- backgroundColor: "#fff",
+ backgroundColor: '#fff',
borderRadius: 16,
padding: 16,
marginBottom: 24,
},
storageBar: {
height: 8,
- backgroundColor: "#f0f0f0",
+ backgroundColor: '#f0f0f0',
borderRadius: 4,
- overflow: "hidden",
+ overflow: 'hidden',
marginBottom: 8,
},
storageUsed: {
- height: "100%",
- backgroundColor: "#4A55A2",
+ height: '100%',
+ backgroundColor: '#4A55A2',
borderRadius: 4,
},
storageInfo: {
- flexDirection: "row",
- justifyContent: "space-between",
+ flexDirection: 'row',
+ justifyContent: 'space-between',
marginBottom: 16,
},
storageText: {
fontSize: 14,
- color: "#666",
+ color: '#666',
},
upgradeButton: {
- backgroundColor: "#FF4081",
+ backgroundColor: '#FF4081',
borderRadius: 25,
paddingVertical: 12,
- alignItems: "center",
+ alignItems: 'center',
},
upgradeButtonText: {
- color: "#fff",
+ color: '#fff',
fontSize: 16,
- fontWeight: "600",
+ fontWeight: '600',
},
breakdownCard: {
- backgroundColor: "#fff",
+ backgroundColor: '#fff',
borderRadius: 16,
padding: 16,
marginBottom: 24,
},
breakdownTitle: {
fontSize: 20,
- fontWeight: "bold",
+ fontWeight: 'bold',
marginBottom: 16,
},
breakdownItem: {
- flexDirection: "row",
- justifyContent: "space-between",
- alignItems: "center",
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
paddingVertical: 12,
borderBottomWidth: 1,
- borderBottomColor: "#f0f0f0",
+ borderBottomColor: '#f0f0f0',
},
breakdownLeft: {
- flexDirection: "row",
- alignItems: "center",
+ flexDirection: 'row',
+ alignItems: 'center',
},
dot: {
width: 8,
@@ -166,34 +165,34 @@ const styles = StyleSheet.create({
fontSize: 16,
},
breakdownRight: {
- flexDirection: "row",
- alignItems: "center",
+ flexDirection: 'row',
+ alignItems: 'center',
gap: 8,
},
breakdownSize: {
fontSize: 16,
- color: "#666",
+ color: '#666',
},
clearCacheButton: {
- backgroundColor: "#FFE0F0",
+ backgroundColor: '#FFE0F0',
borderRadius: 25,
paddingVertical: 12,
- alignItems: "center",
+ alignItems: 'center',
marginBottom: 12,
},
clearCacheText: {
- color: "#FF4081",
+ color: '#FF4081',
fontSize: 16,
- fontWeight: "500",
+ fontWeight: '500',
},
emptyTrashButton: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "center",
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
gap: 8,
},
emptyTrashText: {
- color: "#FF4081",
+ color: '#FF4081',
fontSize: 16,
},
});
diff --git a/crux-frontend/app/allset.tsx b/crux-frontend/app/allset.tsx
index 7642a21..ed4aea2 100644
--- a/crux-frontend/app/allset.tsx
+++ b/crux-frontend/app/allset.tsx
@@ -1,197 +1,277 @@
-import {useEffect} from 'react';
-import {View, Text, Pressable, StyleSheet, Animated, Image} from 'react-native';
-import {useRouter} from 'expo-router';
-import {ArrowLeft, ArrowRight} from 'lucide-react-native';
+import {useEffect, useRef} from 'react';
+import {
+ View,
+ Text,
+ TouchableOpacity,
+ StyleSheet,
+ Animated,
+ Dimensions,
+ Image,
+} from 'react-native';
+import {ArrowRight} from 'react-native-feather';
+import Svg, {Path} from 'react-native-svg';
-type Nominee = {
- id: string;
+const {width: SCREEN_WIDTH, height: SCREEN_HEIGHT} = Dimensions.get('window');
+
+interface NomineeCardProps {
name: string;
- type: string;
- avatar: string;
+ relation: string;
color: string;
+ delay: number;
+ avatarUrl: string;
+ wavePattern: string;
+}
+
+const WaveBackground = ({color, pattern}: {color: string; pattern: string}) => (
+
+);
+
+const NomineeCard = ({
+ name,
+ relation,
+ color,
+ delay,
+ avatarUrl,
+ wavePattern,
+}: NomineeCardProps) => {
+ const translateY = useRef(new Animated.Value(SCREEN_HEIGHT)).current;
+ const opacity = useRef(new Animated.Value(0)).current;
+
+ useEffect(() => {
+ Animated.sequence([
+ Animated.delay(delay),
+ Animated.parallel([
+ Animated.spring(translateY, {
+ toValue: 0,
+ damping: 15,
+ mass: 1,
+ stiffness: 130,
+ useNativeDriver: true,
+ }),
+ Animated.timing(opacity, {
+ toValue: 1,
+ duration: 400,
+ useNativeDriver: true,
+ }),
+ ]),
+ ]).start();
+ }, [delay]);
+
+ return (
+
+
+
+
+
+
+ {relation}
+ {name}
+
+
+
+
+
+
+
+ );
};
-export default function AllSet() {
- const router = useRouter();
- const nominees: Nominee[] = [
+const AllSet = () => {
+ const nominees = [
{
- id: '1',
name: 'Johny',
- type: 'Brother',
- avatar: '@/assets/images/placeholder1.png',
- color: '#E8F5E9',
+ relation: 'Brother',
+ color: '#BAFFC9',
+ delay: 0,
+ avatarUrl: '@/assets/images/placeholder1.png',
+ wavePattern:
+ 'M0,32L48,37.3C96,43,192,53,288,80C384,107,480,149,576,160C672,171,768,149,864,133.3C960,117,1056,107,1152,101.3C1248,96,1344,96,1392,96L1440,96L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z',
},
{
- id: '2',
name: 'Mom',
- type: 'Mom',
- avatar: '@/assets/images/placeholder1.png',
- color: '#E3F2FD',
+ relation: 'Mom',
+ color: '#BAE1FF',
+ delay: 200,
+ avatarUrl: '@/assets/images/placeholder1.png',
+ wavePattern:
+ 'M0,64L48,80C96,96,192,128,288,133.3C384,139,480,117,576,101.3C672,85,768,75,864,90.7C960,107,1056,149,1152,160C1248,171,1344,149,1392,138.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z',
},
{
- id: '3',
name: 'Max',
- type: 'Friend',
- avatar: '@/assets/images/placeholder1.png',
- color: '#FCE4EC',
+ relation: 'Friend',
+ color: '#FFB3BA',
+ delay: 400,
+ avatarUrl: '@/assets/images/placeholder1.png',
+ wavePattern:
+ 'M0,96L48,112C96,128,192,160,288,176C384,192,480,192,576,176C672,160,768,128,864,117.3C960,107,1056,117,1152,122.7C1248,128,1344,128,1392,128L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z',
},
];
- // Create animated values for each nominee
- const animations = nominees.map(() => new Animated.Value(0));
+ const titleOpacity = useRef(new Animated.Value(0)).current;
+ const buttonOpacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
- // Sequence the animations
- const sequence = nominees.map((_, index) => {
- return Animated.timing(animations[index], {
+ Animated.sequence([
+ Animated.timing(titleOpacity, {
toValue: 1,
duration: 500,
- delay: index * 200, // Stagger the animations
useNativeDriver: true,
- });
- });
-
- // Start the sequence after a brief delay
- setTimeout(() => {
- Animated.sequence(sequence).start();
- }, 500);
+ }),
+ Animated.delay(1200),
+ Animated.timing(buttonOpacity, {
+ toValue: 1,
+ duration: 500,
+ useNativeDriver: true,
+ }),
+ ]).start();
}, []);
return (
- router.back()}>
-
-
-
-
+
All set!
We created folders for your nominees, you can start adding your files.
+
-
- {nominees.map((nominee, index) => (
-
-
-
-
- {nominee.type}
- {nominee.name}
-
-
-
-
-
-
- ))}
-
+
+ {nominees.map((nominee) => (
+
+ ))}
+
- router.push('/profile')}
- >
+
+
Upload files
-
-
+
+
);
-}
+};
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: '#fff',
- },
- backButton: {
+ backgroundColor: '#FFFFFF',
padding: 20,
- paddingTop: 40,
},
- content: {
- flex: 1,
- padding: 20,
- alignItems: 'center',
+ header: {
+ marginTop: 40,
+ marginBottom: 20,
},
title: {
- fontSize: 28,
+ fontSize: 32,
fontWeight: 'bold',
marginBottom: 8,
textAlign: 'center',
},
subtitle: {
fontSize: 16,
- color: '#666',
+ color: '#666666',
textAlign: 'center',
- marginBottom: 40,
- paddingHorizontal: 20,
+ marginBottom: 20,
},
cardsContainer: {
- width: '100%',
+ flex: 1,
+ justifyContent: 'center',
gap: 16,
- marginBottom: 40,
},
- card: {
+ nomineeCard: {
+ height: 100,
borderRadius: 16,
- padding: 16,
- marginBottom: 16,
+ overflow: 'hidden',
+ position: 'relative',
},
- cardContent: {
+ nomineeContent: {
+ flex: 1,
flexDirection: 'row',
- justifyContent: 'space-between',
alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: 16,
},
- cardType: {
- fontSize: 14,
- color: '#666',
- marginBottom: 4,
+ leftContent: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 12,
},
avatar: {
width: 40,
height: 40,
borderRadius: 20,
- marginRight: 12,
+ backgroundColor: '#FFFFFF',
},
- cardName: {
- fontSize: 24,
- fontWeight: '600',
+ textContent: {
+ gap: 4,
},
- cardButton: {
- width: 40,
- height: 40,
- borderRadius: 20,
- backgroundColor: '#fff',
+ nomineeRelation: {
+ fontSize: 14,
+ color: '#666666',
+ textTransform: 'uppercase',
+ borderRadius: 18,
+ paddingVertical: 4,
+ paddingHorizontal: 8,
+ backgroundColor: '#FFFFFF',
+ alignItems: 'center',
justifyContent: 'center',
+ shadowColor: '#000000',
+ shadowOffset: {width: 0, height: 2},
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 2,
+ },
+ nomineeName: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ },
+ arrowButton: {
+ width: 36,
+ height: 36,
+ borderRadius: 18,
+ backgroundColor: '#FFFFFF',
alignItems: 'center',
+ justifyContent: 'center',
+ shadowColor: '#000000',
+ shadowOffset: {width: 0, height: 2},
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 2,
},
uploadButton: {
- backgroundColor: '#FF4081',
- paddingVertical: 16,
- paddingHorizontal: 32,
+ backgroundColor: '#FF69B4',
+ height: 50,
borderRadius: 25,
- marginTop: 'auto',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: 20,
+ shadowColor: '#000000',
+ shadowOffset: {width: 0, height: 2},
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 2,
},
uploadButtonText: {
- color: 'white',
+ color: '#FFFFFF',
fontSize: 16,
- fontWeight: '600',
+ fontWeight: 'bold',
},
});
+
+export default AllSet;
diff --git a/crux-frontend/app/nominees.tsx b/crux-frontend/app/nominees.tsx
index 3cb2174..a3e02f2 100644
--- a/crux-frontend/app/nominees.tsx
+++ b/crux-frontend/app/nominees.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import React, {useState} from 'react';
import {
View,
Text,
@@ -6,9 +6,9 @@ import {
Pressable,
StyleSheet,
ScrollView,
-} from "react-native";
-import { useRouter } from "expo-router";
-import { ArrowLeft, ChevronDown, X } from "lucide-react-native";
+} from 'react-native';
+import {useRouter} from 'expo-router';
+import {ArrowLeft, ChevronDown, X} from 'lucide-react-native';
type Nominee = {
email: string;
@@ -19,15 +19,15 @@ type Nominee = {
export default function Nominees() {
const router = useRouter();
const [nominees, setNominees] = useState([
- { email: "mom@mail.com", relationship: "Mom", color: "#E3F2FD" },
- { email: "max@mail.com", relationship: "Friend", color: "#FCE4EC" },
- { email: "johny@mail.com", relationship: "Brother", color: "#E8F5E9" },
+ {email: 'mom@mail.com', relationship: 'Mom', color: '#E3F2FD'},
+ {email: 'max@mail.com', relationship: 'Friend', color: '#FCE4EC'},
+ {email: 'johny@mail.com', relationship: 'Brother', color: '#E8F5E9'},
]);
- const [newEmail, setNewEmail] = useState("");
- const [newRelationship, setNewRelationship] = useState("Friend");
+ const [newEmail, setNewEmail] = useState('');
+ const [newRelationship, setNewRelationship] = useState('Friend');
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
- const relationships = ["Friend", "Mom", "Dad", "Brother", "Sister"];
+ const relationships = ['Friend', 'Mom', 'Dad', 'Brother', 'Sister'];
const removeNominee = (email: string) => {
setNominees(nominees.filter((nominee) => nominee.email !== email));
@@ -35,14 +35,14 @@ export default function Nominees() {
const getRelationshipColor = (relationship: string) => {
switch (relationship) {
- case "Mom":
- return "#E3F2FD";
- case "Friend":
- return "#FCE4EC";
- case "Brother":
- return "#E8F5E9";
+ case 'Mom':
+ return '#E3F2FD';
+ case 'Friend':
+ return '#FCE4EC';
+ case 'Brother':
+ return '#E8F5E9';
default:
- return "#F5F5F5";
+ return '#F5F5F5';
}
};
@@ -111,7 +111,7 @@ export default function Nominees() {
{nominees.map((nominee, index) => (
{nominee.email}
@@ -134,12 +134,12 @@ export default function Nominees() {
router.push("/allset")}
+ onPress={() => router.push('/allset')}
>
@@ -149,17 +149,17 @@ export default function Nominees() {
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: "#fff",
+ backgroundColor: '#fff',
},
header: {
- flexDirection: "row",
- justifyContent: "space-between",
- alignItems: "center",
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
padding: 20,
paddingTop: 40,
},
progress: {
- color: "#666",
+ color: '#666',
},
content: {
flex: 1,
@@ -167,18 +167,18 @@ const styles = StyleSheet.create({
},
title: {
fontSize: 28,
- fontWeight: "bold",
+ fontWeight: 'bold',
marginBottom: 8,
},
subtitle: {
fontSize: 16,
- color: "#666",
+ color: '#666',
marginBottom: 32,
lineHeight: 24,
},
sectionTitle: {
fontSize: 18,
- fontWeight: "600",
+ fontWeight: '600',
marginBottom: 16,
},
form: {
@@ -187,47 +187,47 @@ const styles = StyleSheet.create({
nameInput: {
height: 50,
borderRadius: 25,
- backgroundColor: "#F5F5F5",
+ backgroundColor: '#F5F5F5',
paddingHorizontal: 20,
fontSize: 16,
marginBottom: 12,
},
row: {
- flexDirection: "row",
+ flexDirection: 'row',
gap: 12,
},
relationshipButton: {
height: 50,
borderRadius: 25,
- backgroundColor: "#F5F5F5",
+ backgroundColor: '#F5F5F5',
paddingHorizontal: 20,
- flexDirection: "row",
- alignItems: "center",
+ flexDirection: 'row',
+ alignItems: 'center',
gap: 8,
flex: 1,
},
relationshipButtonText: {
fontSize: 16,
- color: "#000",
+ color: '#000',
},
emailInput: {
height: 50,
borderRadius: 25,
- backgroundColor: "#F5F5F5",
+ backgroundColor: '#F5F5F5',
paddingHorizontal: 20,
fontSize: 16,
flex: 2,
},
dropdown: {
- position: "absolute",
+ position: 'absolute',
top: 110,
left: 0,
- right: "66%",
- backgroundColor: "#fff",
+ right: '66%',
+ backgroundColor: '#fff',
borderRadius: 12,
padding: 8,
- shadowColor: "#000",
- shadowOffset: { width: 0, height: 2 },
+ shadowColor: '#000',
+ shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 3,
@@ -241,9 +241,9 @@ const styles = StyleSheet.create({
gap: 12,
},
inviteItem: {
- flexDirection: "row",
- justifyContent: "space-between",
- alignItems: "center",
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
padding: 16,
borderRadius: 12,
},
@@ -251,37 +251,37 @@ const styles = StyleSheet.create({
fontSize: 16,
},
inviteActions: {
- flexDirection: "row",
- alignItems: "center",
+ flexDirection: 'row',
+ alignItems: 'center',
gap: 12,
},
relationshipTag: {
- backgroundColor: "#fff",
+ backgroundColor: '#fff',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
},
relationshipTagText: {
fontSize: 14,
- fontWeight: "500",
+ fontWeight: '500',
},
removeButton: {
width: 24,
height: 24,
borderRadius: 12,
- backgroundColor: "#fff",
- justifyContent: "center",
- alignItems: "center",
+ backgroundColor: '#fff',
+ justifyContent: 'center',
+ alignItems: 'center',
},
nextButton: {
- position: "absolute",
+ position: 'absolute',
bottom: 20,
right: 20,
width: 56,
height: 56,
borderRadius: 28,
- backgroundColor: "#FF4081",
- justifyContent: "center",
- alignItems: "center",
+ backgroundColor: '#FF4081',
+ justifyContent: 'center',
+ alignItems: 'center',
},
});
diff --git a/crux-frontend/assets/svg/cardbg.svg b/crux-frontend/assets/svg/cardbg.svg
new file mode 100644
index 0000000..0ec3ee1
--- /dev/null
+++ b/crux-frontend/assets/svg/cardbg.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/crux-frontend/cognito-config.js b/crux-frontend/cognito-config.js
index 04cb430..d082e69 100644
--- a/crux-frontend/cognito-config.js
+++ b/crux-frontend/cognito-config.js
@@ -1,8 +1,9 @@
-import { CognitoUserPool } from "amazon-cognito-identity-js";
+import {CognitoUserPool} from 'amazon-cognito-identity-js';
+// Ensure these are set in your .env or expo environment variables
const poolData = {
- UserPoolId: "us-east-2_j5mZ9QZsw",
- ClientId: "291imj7jueaf16qabvcbk6k2bn",
+ UserPoolId: 'eu-west-2_6fPDsUm5T',
+ ClientId: 'samt4bt8omlj5o4jj8vm1o9ig',
};
export const userPool = new CognitoUserPool(poolData);
diff --git a/crux-frontend/components/BackgroundPattern.tsx b/crux-frontend/components/BackgroundPattern.tsx
new file mode 100644
index 0000000..accbf28
--- /dev/null
+++ b/crux-frontend/components/BackgroundPattern.tsx
@@ -0,0 +1,24 @@
+import Svg, {Path} from 'react-native-svg';
+import {StyleSheet} from 'react-native';
+
+interface BackgroundPatternProps {
+ color: string;
+ opacity?: number;
+}
+
+export const BackgroundPattern = ({
+ color,
+ opacity = 0.1,
+}: BackgroundPatternProps) => {
+ return (
+
+ );
+};
diff --git a/crux-frontend/components/Card.tsx b/crux-frontend/components/Card.tsx
new file mode 100644
index 0000000..8d432f0
--- /dev/null
+++ b/crux-frontend/components/Card.tsx
@@ -0,0 +1,113 @@
+import {View, Text, Image, TouchableOpacity, StyleSheet} from 'react-native';
+import {ChevronRight} from 'lucide-react-native';
+
+import {theme} from '@/constants/Colors';
+
+interface CardProps {
+ name: string;
+ relation: string;
+ photoCount: number;
+ avatarUrl: string | undefined;
+ backgroundColor?: string;
+ onPress?: () => void;
+}
+
+export const Card: React.FC = ({
+ name,
+ relation,
+ photoCount,
+ avatarUrl,
+ backgroundColor = theme.colors.accent,
+ onPress,
+}) => {
+ return (
+
+
+
+
+
+ {name}
+ {relation}
+ {photoCount} photos
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ borderRadius: 16,
+ padding: 16,
+ minHeight: 100,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 3,
+ },
+ content: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ leftContent: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ flex: 1,
+ },
+ avatar: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ marginRight: 12,
+ },
+ textContainer: {
+ flex: 1,
+ },
+ relation: {
+ fontSize: 12,
+ fontWeight: '600',
+ color: theme.colors.text,
+ opacity: 0.7,
+ marginBottom: 4,
+ letterSpacing: 0.5,
+ },
+ name: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ color: theme.colors.text,
+ marginBottom: 4,
+ },
+ photoCount: {
+ fontSize: 14,
+ color: theme.colors.text,
+ opacity: 0.6,
+ },
+ arrowContainer: {
+ width: 32,
+ height: 32,
+ backgroundColor: 'white',
+ borderRadius: 16,
+ justifyContent: 'center',
+ alignItems: 'center',
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 1,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 2,
+ elevation: 2,
+ },
+});
diff --git a/crux-frontend/components/Collapsible.tsx b/crux-frontend/components/Collapsible.tsx
index 405d393..ed6f358 100644
--- a/crux-frontend/components/Collapsible.tsx
+++ b/crux-frontend/components/Collapsible.tsx
@@ -4,14 +4,14 @@ import {StyleSheet, Pressable, useColorScheme} from 'react-native';
import {ThemedText} from '@/components/ThemedText';
import {ThemedView} from '@/components/ThemedView';
-import {Colors} from '@/constants/Colors';
+import {theme} from '@/constants/Colors';
export function Collapsible({
children,
title,
}: PropsWithChildren & {title: string}) {
const [isOpen, setIsOpen] = useState(false);
- const theme = useColorScheme() ?? 'light';
+ const colorScheme = useColorScheme() ?? 'light';
return (
@@ -22,7 +22,9 @@ export function Collapsible({
{title}
diff --git a/crux-frontend/components/HorizontalAccordion.tsx b/crux-frontend/components/HorizontalAccordion.tsx
new file mode 100644
index 0000000..2e99f39
--- /dev/null
+++ b/crux-frontend/components/HorizontalAccordion.tsx
@@ -0,0 +1,127 @@
+import React, {useState} from 'react';
+import {
+ View,
+ Text,
+ Image,
+ TouchableOpacity,
+ StyleSheet,
+ ScrollView,
+} from 'react-native';
+
+const accordionData = [
+ {
+ id: 'boating',
+ emoji: '🚤',
+ image:
+ 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/boat.webp',
+ title: 'Boating',
+ content:
+ 'Port mutiny draught handsomely ye furl flogging line shrouds hulk. Spirits Plate Fleet code of conduct.',
+ },
+ {
+ id: 'anchoring',
+ emoji: '⚓️',
+ image:
+ 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/anchor.webp',
+ title: 'Anchoring',
+ content:
+ 'Ahoy league hands Sea Legs keelhaul salmagundi fire ship crimp Privateer galleon. Booty boom yard boatswain quarter.',
+ },
+ {
+ id: 'fishing',
+ emoji: '🎣',
+ image:
+ 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/fishing.webp',
+ title: 'Fishing',
+ content:
+ 'No prey, no pay heave down splice the main brace furl cable snow walk the plank chase guns piracy bucko.',
+ },
+ {
+ id: 'lighthouses',
+ emoji: '🔦',
+ image:
+ 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/lighthouse.webp',
+ title: 'Lighthouses',
+ content:
+ 'Deadlights squiffy salmagundi cable pinnace parrel topsail Corsair Arr mizzenmast.',
+ },
+ {
+ id: 'reefs',
+ emoji: '🪸',
+ image:
+ 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/reef.webp',
+ title: 'Reefs',
+ content:
+ 'Keel yard poop deck brigantine gaff six pounders bring a spring upon her cable interloper lad pink.',
+ },
+];
+
+export function HorizontalAccordion() {
+ const [openId, setOpenId] = useState('boating');
+
+ const toggleAccordion = (id: string) => {
+ setOpenId(openId === id ? null : id);
+ };
+
+ return (
+
+ {accordionData.map((item) => (
+
+ toggleAccordion(item.id)}
+ style={styles.summary}
+ >
+ {item.emoji}
+
+
+ {openId === item.id && (
+
+ {item.title}
+ {item.content}
+
+ )}
+
+ ))}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ accordionWrapper: {
+ flexDirection: 'row',
+ padding: 16,
+ },
+ accordionItem: {
+ width: 200,
+ marginRight: 16,
+ borderRadius: 16,
+ overflow: 'hidden',
+ backgroundColor: '#f4f6f9',
+ },
+ summary: {
+ alignItems: 'center',
+ padding: 16,
+ backgroundColor: '#fff',
+ },
+ emoji: {
+ fontSize: 24,
+ marginBottom: 8,
+ },
+ image: {
+ width: '100%',
+ height: 100,
+ borderRadius: 16,
+ },
+ detailsContent: {
+ padding: 16,
+ },
+ title: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginBottom: 8,
+ },
+ content: {
+ fontSize: 14,
+ color: '#444',
+ },
+});
diff --git a/crux-frontend/components/Input.tsx b/crux-frontend/components/Input.tsx
index 7ebf3de..9411c3b 100644
--- a/crux-frontend/components/Input.tsx
+++ b/crux-frontend/components/Input.tsx
@@ -1,12 +1,12 @@
-import { View, TextInput, Text, StyleSheet } from "react-native";
-import { inputModeOptions } from "react-native";
+import {View, TextInput, Text, StyleSheet} from 'react-native';
+import {InputModeOptions} from 'react-native';
type InputProps = {
label: string;
placeholder: string;
value: string;
onChangeText: (text: string) => void;
- inputMode: inputModeOptions;
+ inputMode: InputModeOptions;
style?: StyleSheet;
};
@@ -39,11 +39,11 @@ const styles = StyleSheet.create({
label: {
fontSize: 16,
marginBottom: 8,
- fontWeight: "500",
+ fontWeight: '500',
},
input: {
borderWidth: 1,
- borderColor: "#E0E0E0",
+ borderColor: '#E0E0E0',
borderRadius: 8,
padding: 12,
fontSize: 16,
diff --git a/crux-frontend/components/horizontal-accordion.tsx b/crux-frontend/components/horizontal-accordion.tsx
deleted file mode 100644
index efe8be1..0000000
--- a/crux-frontend/components/horizontal-accordion.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import React, { useState } from 'react'
-import { View, Text, Image, TouchableOpacity, StyleSheet, ScrollView } from 'react-native'
-
-const accordionData = [
- {
- id: 'boating',
- emoji: '🚤',
- image: 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/boat.webp',
- title: 'Boating',
- content: 'Port mutiny draught handsomely ye furl flogging line shrouds hulk. Spirits Plate Fleet code of conduct.',
- },
- {
- id: 'anchoring',
- emoji: '⚓️',
- image: 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/anchor.webp',
- title: 'Anchoring',
- content: 'Ahoy league hands Sea Legs keelhaul salmagundi fire ship crimp Privateer galleon. Booty boom yard boatswain quarter.',
- },
- {
- id: 'fishing',
- emoji: '🎣',
- image: 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/fishing.webp',
- title: 'Fishing',
- content: 'No prey, no pay heave down splice the main brace furl cable snow walk the plank chase guns piracy bucko.',
- },
- {
- id: 'lighthouses',
- emoji: '🔦',
- image: 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/lighthouse.webp',
- title: 'Lighthouses',
- content: 'Deadlights squiffy salmagundi cable pinnace parrel topsail Corsair Arr mizzenmast.',
- },
- {
- id: 'reefs',
- emoji: '🪸',
- image: 'https://raw.githubusercontent.com/kevin-powell/accordion/8551c559e3e8d9a9cca7a983c9e8903ef533f189/public/assets/reef.webp',
- title: 'Reefs',
- content: 'Keel yard poop deck brigantine gaff six pounders bring a spring upon her cable interloper lad pink.',
- },
-]
-
-export default function HorizontalAccordion() {
- const [openId, setOpenId] = useState('boating')
-
- const toggleAccordion = (id: string) => {
- setOpenId(openId === id ? null : id)
- }
-
- return (
-
- {accordionData.map(item => (
-
- toggleAccordion(item.id)} style={styles.summary}>
- {item.emoji}
-
-
- {openId === item.id && (
-
- {item.title}
- {item.content}
-
- )}
-
- ))}
-
- )
-}
-
-const styles = StyleSheet.create({
- accordionWrapper: {
- flexDirection: 'row',
- padding: 16,
- },
- accordionItem: {
- width: 200,
- marginRight: 16,
- borderRadius: 16,
- overflow: 'hidden',
- backgroundColor: '#f4f6f9',
- },
- summary: {
- alignItems: 'center',
- padding: 16,
- backgroundColor: '#fff',
- },
- emoji: {
- fontSize: 24,
- marginBottom: 8,
- },
- image: {
- width: '100%',
- height: 100,
- borderRadius: 16,
- },
- detailsContent: {
- padding: 16,
- },
- title: {
- fontSize: 18,
- fontWeight: 'bold',
- marginBottom: 8,
- },
- content: {
- fontSize: 14,
- color: '#444',
- },
-})
\ No newline at end of file
diff --git a/crux-frontend/constants/Colors.ts b/crux-frontend/constants/Colors.ts
index 14e6784..3269c62 100644
--- a/crux-frontend/constants/Colors.ts
+++ b/crux-frontend/constants/Colors.ts
@@ -6,21 +6,59 @@
const tintColorLight = '#0a7ea4';
const tintColorDark = '#fff';
-export const Colors = {
- light: {
- text: '#11181C',
- background: '#fff',
- tint: tintColorLight,
- icon: '#687076',
- tabIconDefault: '#687076',
- tabIconSelected: tintColorLight,
- },
- dark: {
- text: '#ECEDEE',
- background: '#151718',
- tint: tintColorDark,
- icon: '#9BA1A6',
- tabIconDefault: '#9BA1A6',
- tabIconSelected: tintColorDark,
+export const theme = {
+ colors: {
+ background: '#FFFFFF',
+ text: '#000000',
+ primary: '#FF69B4', // Pink color used for buttons
+ secondary: '#F0F0F0', // Light gray for input backgrounds
+ accent: '#4169E1', // Blue color used for some text and buttons
+ },
+ fontSizes: {
+ small: 12,
+ medium: 16,
+ large: 20,
+ xlarge: 24,
+ },
+ spacing: {
+ small: 8,
+ medium: 16,
+ large: 24,
+ },
+};
+
+export const globalStyles = {
+ container: {
+ flex: 1,
+ backgroundColor: theme.colors.background,
+ padding: theme.spacing.medium,
+ },
+ title: {
+ fontSize: theme.fontSizes.xlarge,
+ fontWeight: 'bold',
+ marginBottom: theme.spacing.medium,
+ },
+ subtitle: {
+ fontSize: theme.fontSizes.medium,
+ color: '#666',
+ marginBottom: theme.spacing.large,
+ },
+ input: {
+ backgroundColor: theme.colors.secondary,
+ borderRadius: 25,
+ padding: theme.spacing.medium,
+ marginBottom: theme.spacing.medium,
+ },
+ button: {
+ backgroundColor: theme.colors.primary,
+ borderRadius: 25,
+ padding: theme.spacing.medium,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ buttonText: {
+ color: theme.colors.background,
+ fontSize: theme.fontSizes.medium,
+ fontWeight: 'bold',
},
};
diff --git a/crux-frontend/hooks/useAuth.ts b/crux-frontend/hooks/useAuth.ts
index 064d768..40d7667 100644
--- a/crux-frontend/hooks/useAuth.ts
+++ b/crux-frontend/hooks/useAuth.ts
@@ -1,62 +1,105 @@
-import {useState} from 'react';
+import {useState, useCallback} from 'react';
import {
AuthenticationDetails,
CognitoUser,
CognitoUserAttribute,
+ CognitoUserPool,
+ CognitoUserSession,
} from 'amazon-cognito-identity-js';
import {userPool} from '@/cognito-config';
+// Define an interface for signup data
+interface SignupData {
+ firstName: string;
+ lastName: string;
+ email: string;
+ phone: string;
+}
-const useAuth = () => {
+// Define an interface for authentication context
+interface AuthContextType {
+ user: CognitoUser | null;
+ isAuthenticated: boolean;
+ loading: boolean;
+ error: string | null;
+ signup: (data: SignupData) => Promise;
+ signin: (identifier: string) => Promise;
+ signout: () => Promise;
+ verifyMFA: (identifier: string, mfaCode: string) => Promise;
+ resetPassword: (username: string) => Promise;
+ confirmPasswordReset: (
+ username: string,
+ verificationCode: string,
+ newPassword: string
+ ) => Promise;
+}
+
+// First, define the custom user attributes interface
+interface CustomUserAttributes {
+ email: string;
+ name: string;
+ id: string;
+ firstName: string;
+ lastName: string;
+ role: string;
+}
+
+const useAuth = (): AuthContextType => {
+ const [user, setUser] = useState<
+ | (CognitoUser & {
+ email: string;
+ name: string;
+ id: string;
+ firstName: string;
+ lastName: string;
+ role: string;
+ })
+ | null
+ >(null);
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
- const signup = async ({
- firstName,
- lastName,
- email,
- phone,
- }: {
- firstName: string;
- lastName: string;
- email: string;
- phone: string;
- }) => {
- setLoading(true);
- setError(null);
+ // Signup method with password input
+ const signup = useCallback(
+ async ({
+ firstName,
+ lastName,
+ email,
+ phone,
+ }: SignupData): Promise => {
+ setLoading(true);
+ setError(null);
+
+ const attributeList = [
+ new CognitoUserAttribute({Name: 'name', Value: firstName}),
+ new CognitoUserAttribute({Name: 'family_name', Value: lastName}),
+ new CognitoUserAttribute({Name: 'email', Value: email}),
+ new CognitoUserAttribute({Name: 'phone_number', Value: phone}),
+ ];
- const attributeList = [
- new CognitoUserAttribute({Name: 'given_name', Value: firstName}),
- new CognitoUserAttribute({Name: 'family_name', Value: lastName}),
- new CognitoUserAttribute({Name: 'email', Value: email}),
- new CognitoUserAttribute({Name: 'phone_number', Value: phone}),
- ];
-
- return new Promise((resolve) => {
- userPool.signUp(
- phone,
- 'Password123!',
- attributeList,
- [],
- (err, result) => {
+ return new Promise((resolve) => {
+ userPool.signUp(email, 'Password', attributeList, [], (err, result) => {
setLoading(false);
if (err) {
- setError(err.message || 'An error occurred');
+ setError(err.message || 'Signup failed');
resolve(false);
return;
}
resolve(true);
- }
- );
- });
- };
+ });
+ });
+ },
+ []
+ );
- const signin = async (identifier: string) => {
+ // Signin method with dynamic credentials
+ const signin = useCallback(async (identifier: string): Promise => {
setLoading(true);
setError(null);
const authenticationDetails = new AuthenticationDetails({
Username: identifier,
- Password: 'Password123!',
+ Password: 'Password',
});
const cognitoUser = new CognitoUser({
@@ -64,53 +107,151 @@ const useAuth = () => {
Pool: userPool,
});
- return new Promise((resolve) => {
+ return new Promise((resolve) => {
cognitoUser.authenticateUser(authenticationDetails, {
- onSuccess: () => {
+ onSuccess: (session: CognitoUserSession) => {
+ const idToken = session.getIdToken();
+ const payload = idToken.decodePayload();
+ // Then modify the user object creation to properly extend CognitoUser
+ const user = Object.assign(cognitoUser, {
+ email: payload.email,
+ name: payload.name,
+ id: payload.sub,
+ firstName: payload['custom:firstName'],
+ lastName: payload['custom:lastName'],
+ role: payload['custom:role'],
+ }) as CognitoUser & CustomUserAttributes;
+
+ setUser(user);
+ setIsAuthenticated(true);
setLoading(false);
resolve(true);
},
onFailure: (err) => {
+ setError(err.message || 'Authentication failed');
setLoading(false);
- setError(err.message || 'An error occurred');
resolve(false);
},
mfaRequired: (challengeName, challengeParameters) => {
- // Handle MFA challenge
+ // Indicate MFA is required
+ setError('MFA Required');
setLoading(false);
- // You might want to store the cognitoUser instance to use it later for MFA verification
- // You can navigate to MFA verification screen here
resolve(false);
},
});
});
- };
+ }, []);
- const verifyMFA = async (identifier: string, mfaCode: string) => {
- setLoading(true);
- setError(null);
+ // Signout method
+ const signout = useCallback(async (): Promise => {
+ if (user) {
+ user.signOut();
+ setUser(null);
+ setIsAuthenticated(false);
+ }
+ }, [user]);
- const cognitoUser = new CognitoUser({
- Username: identifier,
- Pool: userPool,
- });
+ // MFA Verification
+ const verifyMFA = useCallback(
+ async (identifier: string, mfaCode: string): Promise => {
+ setLoading(true);
+ setError(null);
- return new Promise((resolve) => {
- cognitoUser.sendMFACode(mfaCode, {
- onSuccess: () => {
- setLoading(false);
- resolve(true);
- },
- onFailure: (err) => {
- setLoading(false);
- setError(err.message || 'MFA verification failed');
- resolve(false);
- },
+ const cognitoUser = new CognitoUser({
+ Username: identifier,
+ Pool: userPool,
+ });
+
+ return new Promise((resolve) => {
+ cognitoUser.sendMFACode(mfaCode, {
+ onSuccess: () => {
+ setIsAuthenticated(true);
+ setLoading(false);
+ resolve(true);
+ },
+ onFailure: (err) => {
+ setError(err.message || 'MFA verification failed');
+ setLoading(false);
+ resolve(false);
+ },
+ });
+ });
+ },
+ []
+ );
+
+ // Password Reset Initiation
+ const resetPassword = useCallback(
+ async (username: string): Promise => {
+ setLoading(true);
+ setError(null);
+
+ const cognitoUser = new CognitoUser({
+ Username: username,
+ Pool: userPool,
});
- });
- };
- return {signup, signin, verifyMFA, loading, error};
+ return new Promise((resolve) => {
+ cognitoUser.forgotPassword({
+ onSuccess: () => {
+ setLoading(false);
+ resolve(true);
+ },
+ onFailure: (err) => {
+ setError(err.message || 'Password reset failed');
+ setLoading(false);
+ resolve(false);
+ },
+ });
+ });
+ },
+ []
+ );
+
+ // Password Reset Confirmation
+ const confirmPasswordReset = useCallback(
+ async (
+ username: string,
+ verificationCode: string,
+ newPassword: string
+ ): Promise => {
+ setLoading(true);
+ setError(null);
+
+ const cognitoUser = new CognitoUser({
+ Username: username,
+ Pool: userPool,
+ });
+
+ return new Promise((resolve) => {
+ cognitoUser.confirmPassword(verificationCode, newPassword, {
+ onSuccess: () => {
+ setLoading(false);
+ resolve(true);
+ },
+ onFailure: (err) => {
+ setError(err.message || 'Password reset confirmation failed');
+ setLoading(false);
+ resolve(false);
+ },
+ });
+ });
+ },
+ []
+ );
+
+ return {
+ user,
+ isAuthenticated,
+ loading,
+ error,
+ signup,
+ signin,
+ signout,
+ verifyMFA,
+ resetPassword,
+ confirmPasswordReset,
+ };
};
export default useAuth;
diff --git a/crux-frontend/package.json b/crux-frontend/package.json
index 6bdd99b..3253ea5 100644
--- a/crux-frontend/package.json
+++ b/crux-frontend/package.json
@@ -15,43 +15,49 @@
"preset": "jest-expo"
},
"dependencies": {
- "@expo/vector-icons": "^14.0.4",
- "@react-native-async-storage/async-storage": "^2.0.0",
- "@react-navigation/native": "^6.0.2",
+ "@expo/vector-icons": "^14.0.2",
+ "@react-native-async-storage/async-storage": "2.1.0",
+ "@react-navigation/bottom-tabs": "^7.0.0",
+ "@react-navigation/native": "^7.0.0",
"amazon-cognito-identity-js": "^6.3.12",
- "expo": "^51.0.0",
- "expo-build-properties": "~0.11.1",
- "expo-constants": "~16.0.2",
- "expo-dev-client": "~3.3.9",
- "expo-dom": "^27.0.2",
- "expo-font": "~12.0.9",
- "expo-linking": "~6.3.1",
- "expo-router": "~3.5.23",
- "expo-splash-screen": "~0.27.5",
- "expo-status-bar": "~1.12.1",
- "expo-system-ui": "~3.0.7",
- "expo-web-browser": "~13.0.3",
+ "expo": "~52.0.9",
+ "expo-blur": "~14.0.1",
+ "expo-constants": "~17.0.3",
+ "expo-document-picker": "~13.0.1",
+ "expo-file-system": "~18.0.4",
+ "expo-font": "~13.0.1",
+ "expo-haptics": "~14.0.0",
+ "expo-image-picker": "~16.0.2",
+ "expo-linking": "~7.0.3",
+ "expo-router": "~4.0.7",
+ "expo-splash-screen": "~0.29.11",
+ "expo-status-bar": "~2.0.0",
+ "expo-symbols": "~0.2.0",
+ "expo-system-ui": "~4.0.3",
+ "expo-web-browser": "~14.0.1",
"lucide-react-native": "^0.454.0",
- "react": "18.2.0",
- "react-dom": "18.2.0",
- "react-native": "0.74.5",
- "react-native-gesture-handler": "~2.16.1",
- "react-native-reanimated": "~3.10.1",
- "react-native-safe-area-context": "4.10.5",
- "react-native-screens": "3.31.1",
- "react-native-web": "~0.19.10"
+ "react": "18.3.1",
+ "react-dom": "18.3.1",
+ "react-native": "0.76.2",
+ "react-native-feather": "^1.1.2",
+ "react-native-gesture-handler": "~2.20.2",
+ "react-native-reanimated": "~3.16.1",
+ "react-native-safe-area-context": "4.12.0",
+ "react-native-screens": "~4.1.0",
+ "react-native-web": "~0.19.13",
+ "react-native-webview": "13.12.4"
},
"devDependencies": {
- "@babel/core": "^7.20.0",
+ "@babel/core": "^7.25.2",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
"@types/jest": "^29.5.12",
- "@types/react": "~18.2.45",
- "@types/react-test-renderer": "^18.0.7",
+ "@types/react": "~18.3.12",
+ "@types/react-test-renderer": "^18.3.0",
"babel-plugin-module-resolver": "^5.0.0",
"jest": "^29.2.1",
- "jest-expo": "~51.0.3",
- "react-test-renderer": "18.2.0",
- "typescript": "~5.3.3"
+ "jest-expo": "~52.0.2",
+ "react-test-renderer": "18.3.1",
+ "typescript": "^5.3.3"
},
"private": true
}
diff --git a/crux-frontend/tsconfig.json b/crux-frontend/tsconfig.json
index 4a51e54..fc79723 100644
--- a/crux-frontend/tsconfig.json
+++ b/crux-frontend/tsconfig.json
@@ -1,6 +1,7 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
+ "baseUrl": ".",
"strict": true,
"jsx": "react-jsx",
"paths": {