Truly an MVP Now. Needs to look good

This commit is contained in:
Gabriel Brown 2024-09-10 13:30:33 -05:00
parent 274c3a5151
commit 1e6b2f8df7
8 changed files with 246 additions and 32 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
EXPO_PUBLIC_API_KEY=I_Love_Madeline
EXPO_PUBLIC_BASE_URL=https://ismadelinethecutest.gibbyb.com/api

View File

@ -1,12 +1,13 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { StyleSheet, ActivityIndicator } from 'react-native'; import { StyleSheet, ActivityIndicator, TouchableOpacity } from 'react-native';
import { ThemedText } from '@/components/ThemedText'; import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView'; import { ThemedView } from '@/components/ThemedView';
import axios from 'axios'; import axios from 'axios';
import ChangeDateDrawer from '@/components/ChangeDateDrawer';
const API_KEY = process.env.EXPO_PUBLIC_API_KEY;
const BASE_URL = process.env.EXPO_PUBLIC_BASE_URL;
const API_KEY = 'I_Love_Madeline';
//const BASE_URL = 'http://192.168.0.39:3000/api';
const BASE_URL = 'https://ismadelinethecutest.gibbyb.com/api';
// Separate API call function // Separate API call function
const fetchCountdownDate = async () => { const fetchCountdownDate = async () => {
@ -14,7 +15,6 @@ const fetchCountdownDate = async () => {
const response = await axios.get(`${BASE_URL}/getCountdown`, { const response = await axios.get(`${BASE_URL}/getCountdown`, {
params: { apiKey: API_KEY } params: { apiKey: API_KEY }
}); });
//const response = await axios.get(`${BASE_URL}/getCountdown?apiKey=I_Love_Madeline`);
console.log('API response:', response.data); console.log('API response:', response.data);
if (response.data && response.data[0] && response.data[0].countdown) { if (response.data && response.data[0] && response.data[0].countdown) {
console.log('Countdown date:', response.data[0].countdown); console.log('Countdown date:', response.data[0].countdown);
@ -38,6 +38,7 @@ export default function TabTwoScreen() {
}); });
const [targetDate, setTargetDate] = useState<Date | null>(null); const [targetDate, setTargetDate] = useState<Date | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [isDateDrawerVisible, setIsDateDrawerVisible] = useState(false);
useEffect(() => { useEffect(() => {
const loadCountdownDate = async () => { const loadCountdownDate = async () => {
@ -45,9 +46,6 @@ export default function TabTwoScreen() {
const date = await fetchCountdownDate(); const date = await fetchCountdownDate();
if (date) { if (date) {
setTargetDate(date); setTargetDate(date);
} else {
// Fallback to a default date if API call fails
setTargetDate(new Date());
} }
setIsLoading(false); setIsLoading(false);
}; };
@ -78,6 +76,10 @@ export default function TabTwoScreen() {
return () => clearInterval(interval); return () => clearInterval(interval);
}, [targetDate]); }, [targetDate]);
const handleDateChange = (newDate: Date) => {
setTargetDate(newDate);
};
if (isLoading) { if (isLoading) {
return ( return (
<ThemedView style={styles.container}> <ThemedView style={styles.container}>
@ -95,6 +97,20 @@ export default function TabTwoScreen() {
<CountdownItem value={countdown.minutes} label="Minutes" /> <CountdownItem value={countdown.minutes} label="Minutes" />
<CountdownItem value={countdown.seconds} label="Seconds" /> <CountdownItem value={countdown.seconds} label="Seconds" />
</ThemedView> </ThemedView>
<TouchableOpacity
style={styles.changeButton}
onPress={() => setIsDateDrawerVisible(true)}
>
<ThemedText style={styles.changeButtonText}>Change Date</ThemedText>
</TouchableOpacity>
{targetDate && (
<ChangeDateDrawer
isVisible={isDateDrawerVisible}
onClose={() => setIsDateDrawerVisible(false)}
onDateChange={handleDateChange}
currentDate={targetDate}
/>
)}
</ThemedView> </ThemedView>
); );
} }
@ -140,4 +156,15 @@ const styles = StyleSheet.create({
countdownLabel: { countdownLabel: {
fontSize: 24, fontSize: 24,
}, },
changeButton: {
backgroundColor: '#007AFF',
padding: 10,
borderRadius: 5,
marginTop: 20,
},
changeButtonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
}); });

View File

@ -5,8 +5,8 @@ import { ThemedView } from '@/components/ThemedView';
import AsyncStorage from '@react-native-async-storage/async-storage'; import AsyncStorage from '@react-native-async-storage/async-storage';
import axios from 'axios'; import axios from 'axios';
const API_KEY = 'I_Love_Madeline'; const API_KEY = process.env.EXPO_PUBLIC_API_KEY;
const BASE_URL = 'https://ismadelinethecutest.gibbyb.com/api'; const BASE_URL = process.env.EXPO_PUBLIC_BASE_URL;
export default function HomeScreen() { export default function HomeScreen() {
const [message, setMessage] = useState('Loading message...'); const [message, setMessage] = useState('Loading message...');

View File

@ -1,12 +1,21 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { StyleSheet, TextInput, TouchableOpacity, Alert, Keyboard } from 'react-native'; import {
StyleSheet,
TextInput,
TouchableOpacity,
Alert,
Keyboard,
TouchableWithoutFeedback,
KeyboardAvoidingView,
Platform
} from 'react-native';
import { ThemedText } from '@/components/ThemedText'; import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView'; import { ThemedView } from '@/components/ThemedView';
import AsyncStorage from '@react-native-async-storage/async-storage'; import AsyncStorage from '@react-native-async-storage/async-storage';
import axios from 'axios'; import axios from 'axios';
const API_KEY = 'I_Love_Madeline'; const API_KEY = process.env.EXPO_PUBLIC_API_KEY;
const BASE_URL = 'https://ismadelinethecutest.gibbyb.com/api'; const BASE_URL = process.env.EXPO_PUBLIC_BASE_URL;
export default function SendMessageScreen() { export default function SendMessageScreen() {
const [message, setMessage] = useState(''); const [message, setMessage] = useState('');
@ -53,6 +62,11 @@ export default function SendMessageScreen() {
}; };
return ( return (
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={{flex: 1}}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<ThemedView style={styles.container}> <ThemedView style={styles.container}>
<ThemedText style={styles.title}>Send a Message</ThemedText> <ThemedText style={styles.title}>Send a Message</ThemedText>
<TextInput <TextInput
@ -67,6 +81,8 @@ export default function SendMessageScreen() {
<ThemedText style={styles.buttonText}>Send Message</ThemedText> <ThemedText style={styles.buttonText}>Send Message</ThemedText>
</TouchableOpacity> </TouchableOpacity>
</ThemedView> </ThemedView>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
); );
} }
@ -78,14 +94,15 @@ const styles = StyleSheet.create({
padding: 20, padding: 20,
}, },
title: { title: {
fontSize: 24, fontSize: 36,
lineHeight: 40,
fontWeight: 'bold', fontWeight: 'bold',
marginBottom: 20, marginBottom: 20,
textAlign: 'center', textAlign: 'center',
}, },
input: { input: {
width: '100%', width: '100%',
height: 100, height: 80,
borderColor: '#ccc', borderColor: '#ccc',
borderWidth: 1, borderWidth: 1,
borderRadius: 5, borderRadius: 5,
@ -93,6 +110,7 @@ const styles = StyleSheet.create({
marginBottom: 20, marginBottom: 20,
textAlignVertical: 'top', textAlignVertical: 'top',
color: '#FFF', color: '#FFF',
fontSize: 18,
}, },
button: { button: {
backgroundColor: '#007AFF', backgroundColor: '#007AFF',

View File

@ -0,0 +1,154 @@
import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity, Modal, Platform, View } from 'react-native';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import DateTimePicker from '@react-native-community/datetimepicker';
import axios from 'axios';
const API_KEY = process.env.EXPO_PUBLIC_API_KEY;
const BASE_URL = process.env.EXPO_PUBLIC_BASE_URL;
interface ChangeDateDrawerProps {
isVisible: boolean;
onClose: () => void;
onDateChange: (date: Date) => void;
currentDate: Date;
}
export default function ChangeDateDrawer({ isVisible, onClose, onDateChange, currentDate }: ChangeDateDrawerProps) {
const [date, setDate] = useState(currentDate);
const [showDatePicker, setShowDatePicker] = useState(false);
const [showTimePicker, setShowTimePicker] = useState(false);
const handleDateChange = (event: any, selectedDate?: Date) => {
const currentDate = selectedDate || date;
setShowDatePicker(Platform.OS === 'ios');
setDate(currentDate);
};
const handleTimeChange = (event: any, selectedTime?: Date) => {
const currentTime = selectedTime || date;
setShowTimePicker(Platform.OS === 'ios');
setDate(currentTime);
};
const handleSave = async () => {
try {
await axios.post(`${BASE_URL}/setCountdown`, null, {
params: { apiKey: API_KEY, countdown: date.toISOString() }
});
onDateChange(date);
onClose();
} catch (error) {
console.error('Failed to update countdown date:', error);
// You might want to show an error message to the user here
}
};
return (
<Modal
animationType="slide"
transparent={true}
visible={isVisible}
onRequestClose={onClose}
>
<ThemedView style={styles.centeredView}>
<ThemedView style={styles.modalView}>
<ThemedText style={styles.modalText}>Set New Countdown Date and Time</ThemedText>
<TouchableOpacity style={styles.button} onPress={() => setShowDatePicker(true)}>
<ThemedText style={styles.buttonText}>Select Date</ThemedText>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={() => setShowTimePicker(true)}>
<ThemedText style={styles.buttonText}>Select Time</ThemedText>
</TouchableOpacity>
{showDatePicker && (
<DateTimePicker
testID="datePicker"
value={date}
mode="date"
is24Hour={true}
display="default"
onChange={handleDateChange}
/>
)}
{showTimePicker && (
<DateTimePicker
testID="timePicker"
value={date}
mode="time"
is24Hour={true}
display="default"
onChange={handleTimeChange}
/>
)}
<ThemedText style={styles.dateText}>
Selected: {date.toLocaleString()}
</ThemedText>
<TouchableOpacity style={styles.button} onPress={handleSave}>
<ThemedText style={styles.buttonText}>Save</ThemedText>
</TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.cancelButton]} onPress={onClose}>
<ThemedText style={styles.buttonText}>Cancel</ThemedText>
</TouchableOpacity>
</ThemedView>
</ThemedView>
</Modal>
);
}
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalView: {
margin: 20,
backgroundColor: 'black',
borderRadius: 20,
padding: 35,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5
},
button: {
backgroundColor: '#2196F3',
borderRadius: 20,
padding: 10,
elevation: 2,
marginVertical: 10,
minWidth: 120,
},
cancelButton: {
backgroundColor: '#FF3B30',
},
buttonText: {
color: 'white',
fontWeight: 'bold',
textAlign: 'center',
},
modalText: {
marginBottom: 15,
textAlign: 'center',
fontWeight: 'bold',
fontSize: 18,
},
dateText: {
marginVertical: 10,
fontSize: 16,
},
});

View File

@ -15,9 +15,8 @@ interface UserSelectionProps {
onUserSelected: (user: User) => void; onUserSelected: (user: User) => void;
} }
const API_KEY = 'I_Love_Madeline'; const API_KEY = process.env.EXPO_PUBLIC_API_KEY;
const BASE_URL = 'https://ismadelinethecutest.gibbyb.com/api'; const BASE_URL = process.env.EXPO_PUBLIC_BASE_URL;
//const BASE_URL = 'http://192.168.0.39:3000/api';
const UserSelection: React.FC<UserSelectionProps> = ({ onUserSelected }) => { const UserSelection: React.FC<UserSelectionProps> = ({ onUserSelected }) => {
const [users, setUsers] = useState<User[]>([]); const [users, setUsers] = useState<User[]>([]);

13
package-lock.json generated
View File

@ -25,6 +25,7 @@
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-native": "0.74.5", "react-native": "0.74.5",
"react-native-dotenv": "^3.4.11",
"react-native-gesture-handler": "~2.16.1", "react-native-gesture-handler": "~2.16.1",
"react-native-reanimated": "~3.10.1", "react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5", "react-native-safe-area-context": "4.10.5",
@ -16927,6 +16928,18 @@
} }
} }
}, },
"node_modules/react-native-dotenv": {
"version": "3.4.11",
"resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-3.4.11.tgz",
"integrity": "sha512-6vnIE+WHABSeHCaYP6l3O1BOEhWxKH6nHAdV7n/wKn/sciZ64zPPp2NUdEUf1m7g4uuzlLbjgr+6uDt89q2DOg==",
"license": "MIT",
"dependencies": {
"dotenv": "^16.4.5"
},
"peerDependencies": {
"@babel/runtime": "^7.20.6"
}
},
"node_modules/react-native-gesture-handler": { "node_modules/react-native-gesture-handler": {
"version": "2.16.2", "version": "2.16.2",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz",

View File

@ -32,6 +32,7 @@
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-native": "0.74.5", "react-native": "0.74.5",
"react-native-dotenv": "^3.4.11",
"react-native-gesture-handler": "~2.16.1", "react-native-gesture-handler": "~2.16.1",
"react-native-reanimated": "~3.10.1", "react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5", "react-native-safe-area-context": "4.10.5",