Skip to content
Merged
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
14 changes: 12 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"ios": {
"googleServicesFile": "./GoogleService-Info.plist",
"supportsTablet": true,
"bundleIdentifier": "com.builtbybennett.nameflame",
"infoPlist": {
"ITSAppUsesNonExemptEncryption": false
}
},
"android": {
"googleServicesFile": "./google-services.json",
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
Expand All @@ -41,7 +43,9 @@
[
"expo-font",
{
"fonts": ["./assets/fonts/BricolageGrotesque.ttf"]
"fonts": [
"./assets/fonts/BricolageGrotesque.ttf"
]
}
]
],
Expand All @@ -56,6 +60,12 @@
"projectId": "4a498677-22c5-4a41-a8ca-3fdcc2c436b1"
}
},
"owner": "builtbybennett"
"owner": "builtbybennett",
"runtimeVersion": {
"policy": "appVersion"
},
"updates": {
"url": "https://u.expo.dev/4a498677-22c5-4a41-a8ca-3fdcc2c436b1"
}
}
}
6 changes: 3 additions & 3 deletions app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { Colors } from '../../constants/Colors';
import { MaterialIcons } from '@expo/vector-icons';
import { useRouter, useLocalSearchParams } from 'expo-router';

import { useToken } from '../../contexts/authCtx';
import { useAuth } from '../../contexts/authCtx';
import useApi from '@/hooks/useApi';
import { useActiveNameContext } from '../../contexts/activeNameContext';
import { ThemedView } from '@/components/ThemedView';

export default function AppLayout() {
const { token, isLoading } = useToken();
const { user, isLoading } = useAuth();

const router = useRouter();
const api = useApi();
Expand All @@ -29,7 +29,7 @@ export default function AppLayout() {

// Only require authentication within the (app) group's layout as users
// need to be able to access the (auth) group and sign in again.
if (!token) {
if (!user) {
// On web, static rendering will stop here as the user is not authenticated
// in the headless Node process that the pages are rendered in.
return <Redirect href="../sign-in" />;
Expand Down
50 changes: 39 additions & 11 deletions app/(app)/nameContext/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import { Dropdown } from 'react-native-element-dropdown';
import { Colors } from '@/constants/Colors';
import useApi from '@/hooks/useApi';
import { useActiveNameContext } from '@/contexts/activeNameContext';
import { useConfirmationContext } from '@/contexts/confirmationCtx';
import { useErrorContext } from '@/contexts/errorCtx';
import { MaterialIcons } from '@expo/vector-icons';

export default function NameContextDetailsView() {
const { id } = useLocalSearchParams<{ id: string }>();
const router = useRouter();
const activeNameContext = useActiveNameContext();
const { requireConfirmation } = useConfirmationContext();
const { addApiError } = useErrorContext();
const api = useApi();
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [error, setError] = useState<any>({});
// form values
const [nameValue, setNameValue] = useState('');
const [descriptionValue, setDescriptionValue] = useState('');
Expand Down Expand Up @@ -66,12 +70,14 @@ export default function NameContextDetailsView() {
})

}).catch((err) => {
setError(err);
addApiError(err);
}).finally(() => {
setLoading(false);
});
}

function saveNameContext() {
setLoading(true);
api.post('/nameContext', {
name: nameValue,
description: descriptionValue,
Expand All @@ -83,24 +89,28 @@ export default function NameContextDetailsView() {
},
participants
}).then(() => {
alert('Name context created');
router.push('/nameContext');
}).catch((err) => {
console.log(err);
alert('Failed to create name context');
if (err.response?.status === 500) {
addApiError(err);
}
else {
setError(err.response?.data?.error || {});
}
}).finally(() => {
setLoading(false);
});
}

function handleDelete() {
if (confirm('Are you sure you want to delete this name context?')) {
setLoading(true);
api.delete(`/nameContext/${id}`).then(() => {
alert('Name context deleted');
router.push('/nameContext');
}).catch((err) => {
console.log(err);
alert('Failed to delete name context');
addApiError(err);
}).finally(() => {
setLoading(false);
});
}
}

if (loading) {
Expand All @@ -115,15 +125,23 @@ export default function NameContextDetailsView() {
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Text style={{...styles.label, textAlign: 'center' }}>Basic Information</Text>
{isExistingNameContxt &&
<TouchableOpacity onPress={handleDelete} style={{ padding: 5, backgroundColor: Colors.core.purple, borderRadius: 5 }}>
<TouchableOpacity onPress={() => {
requireConfirmation({
primaryActionTitle: 'Delete Name Context',
primaryAction: handleDelete,
message: 'Are you sure you want to delete this name context?'
});
}} style={{ padding: 5, backgroundColor: Colors.core.purple, borderRadius: 5 }}>
<MaterialIcons name='delete' size={24} color={Colors.core.white} />
</TouchableOpacity>}
<TouchableOpacity onPress={saveNameContext} style={{ padding: 5, backgroundColor: Colors.core.orange, borderRadius: 5 }}>
<MaterialIcons name='save' size={24} color={Colors.core.white} />
</TouchableOpacity>
{isExistingNameContxt &&
<TouchableOpacity onPress={() => router.push(`/nameContext/${id}/match`)} style={{ padding: 5, backgroundColor: Colors.core.orange, borderRadius: 5 }}>
<MaterialIcons name='local-fire-department' size={24} color={Colors.core.white} />
</TouchableOpacity>
}
</View>
<Text style={styles.label}>Name</Text>
<TextInput
Expand All @@ -135,6 +153,7 @@ export default function NameContextDetailsView() {
value={nameValue}
onChangeText={setNameValue}
/>
{error.name?.message ? <Text style={styles.errorText}>{error.name?.message}</Text> : null}

<Text style={styles.label}>Description</Text>
<TextInput
Expand All @@ -145,6 +164,7 @@ export default function NameContextDetailsView() {
value={descriptionValue}
onChangeText={setDescriptionValue}
/>
{error.description?.message ? <Text style={styles.errorText}>{error.description?.message}</Text> : null}

<Text style={styles.label}>Noun</Text>
<TextInput
Expand All @@ -154,6 +174,7 @@ export default function NameContextDetailsView() {
value={nounValue}
onChangeText={setNounValue}
/>
{error.noun?.message ? <Text style={styles.errorText}>{error.noun?.message}</Text> : null}

<Text style={styles.label}>Max Characters</Text>
<TextInput
Expand All @@ -163,6 +184,7 @@ export default function NameContextDetailsView() {
value={maxCharacters}
onChangeText={setMaxCharacters}
/>
{error.maxCharacters?.message ? <Text style={styles.errorText}>{error.maxCharacters?.message}</Text> : null}

<Text style={styles.label}>Gender</Text>
<Dropdown
Expand All @@ -180,6 +202,7 @@ export default function NameContextDetailsView() {
setGenderValue(item.value);
}}
/>
{error.gender?.message ? <Text style={styles.errorText}>{error.gender.message}</Text> : null}

<Text style={styles.label}>Starts with the letter</Text>
<TextInput
Expand All @@ -188,6 +211,7 @@ export default function NameContextDetailsView() {
onChangeText={(value)=> setStartsWithValue(String(value).toUpperCase())}
maxLength={1}
/>
{error.startsWithLetter?.message ? <Text style={styles.errorText}>{error.startsWithLetter?.message}</Text> : null}
</View>
</ThemedView>
);
Expand Down Expand Up @@ -255,4 +279,8 @@ const styles = StyleSheet.create({
color: Colors.core.white,
fontSize: 16,
},
errorText: {
color: Colors.core.orange,
fontSize: 12,
},
});
5 changes: 0 additions & 5 deletions app/(app)/nameContext/[id]/match.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ export default function NameContextDetailsMatchs() {
function fetchNewName() {
api.get('/name/random').then((resp) => {
setCurrentName(resp.data);
}).catch((err) => {
alert(err);
});
}

Expand All @@ -45,9 +43,6 @@ export default function NameContextDetailsMatchs() {
}).then(() => {
setSearchValue('');
fetchNewName();
}
).catch((err) => {
alert(err);
});
}

Expand Down
2 changes: 0 additions & 2 deletions app/(app)/nameContext/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ export default function NameContextListView() {
setRefreshing(true);
api.get('/nameContexts').then((resp) => {
setNameContexts(resp.data);
console.log(resp);
setRefreshing(false);
}).catch((err) => {
setRefreshing(false);
alert(err);
})
};

Expand Down
51 changes: 45 additions & 6 deletions app/(app)/settings/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
import { useState, useEffect } from "react";
import { View, Button, Switch } from 'react-native';
import { useState } from "react";
import { View, Button, Switch, Pressable, ActivityIndicator } from 'react-native';
import { Colors } from '../../../constants/Colors';
import { ThemedView } from '../../../components/ThemedView';
import { ThemedText } from '../../../components/ThemedText';

import { useToken } from '../../../contexts/authCtx';
import { useAuth } from '../../../contexts/authCtx';
import { useConfirmationContext } from '../../../contexts/confirmationCtx';
import useApi from '../../../hooks/useApi';

export default function SettingsView() {
const { signOut } = useToken();
const { signOutUser } = useAuth();
const api = useApi();
const [loading, setLoading] = useState(false);

const [sendNotifications, setSendNotifications] = useState(true);
const { requireConfirmation, resolveModal } = useConfirmationContext();

function handleAccoundDeletion() {
requireConfirmation({
message: 'Are you sure you want to delete your account? Deleting your account will remove all of your data including shared name contexts and cannot be undone.',
primaryActionTitle: 'Delete Account',
primaryAction: async () => {
setLoading(true);
try {
await api.delete('/user');
resolveModal();
signOutUser();
} catch (error) {
console.error(error);
}
setLoading(false);
}
});
}

if (loading) {
return (
<ThemedView
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
darkColor={Colors.core.purple}
lightColor={Colors.core.tan}
>
<ActivityIndicator size='large' color={Colors.core.orange} />
</ThemedView>
);
}

return (
<ThemedView
Expand Down Expand Up @@ -37,10 +72,14 @@ export default function SettingsView() {
</ThemedText>
</View>
<View style={{ paddingTop: 10 }}>
<Button title="Delete Account" color={Colors.core.black}/>
<Pressable onPress={handleAccoundDeletion}>
<ThemedText lightColor={Colors.core.black} darkColor={Colors.core.black} type='default'>
Delete Account
</ThemedText>
</Pressable>
</View>
<View style={{ paddingTop: 10 }}>
<Button onPress={signOut} title='Sign Out' color={Colors.core.orange}/>
<Button onPress={signOutUser} title='Sign Out' color={Colors.core.orange}/>
</View>
</View>
</ThemedView>
Expand Down
8 changes: 3 additions & 5 deletions app/(app)/sign-out.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { useToken } from '../../contexts/authCtx';
import { Redirect } from 'expo-router';
import { useAuth } from '../../contexts/authCtx';

export default function SignOutEmptyView() {
const { signOut } = useToken();
signOut();
return <Redirect href="/" />;
const { signOutUser } = useAuth();
signOutUser();
}
23 changes: 16 additions & 7 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { Slot } from 'expo-router';
import { TokenProvider } from '../contexts/authCtx';
import { AuthProvider } from '../contexts/authCtx';
import { ActiveNameProvider } from '../contexts/activeNameContext';
import { ErrorProvider } from '@/contexts/errorCtx';
import { ConfirmationProvider } from '@/contexts/confirmationCtx';
import { ErrorModal } from '@/components/ErrorModal';
import { ConfirmationModal } from '@/components/ConfirmationModal';

export default function Root() {
// Set up the auth context and render our layout inside of it.
return (
<TokenProvider>
<ActiveNameProvider>
<Slot />
</ActiveNameProvider>
</TokenProvider>
<ErrorProvider>
<ConfirmationProvider>
<AuthProvider>
<ActiveNameProvider>
<ErrorModal />
<ConfirmationModal />
<Slot />
</ActiveNameProvider>
</AuthProvider>
</ConfirmationProvider>
</ErrorProvider>
);
}
Loading
Loading