Skip to content

Commit 79fea95

Browse files
committed
feat(profile): added profile designs
1 parent 8fa1536 commit 79fea95

41 files changed

Lines changed: 1807 additions & 280 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ EXPO_PUBLIC_FIREBASE_DATABASE_URL=
44
EXPO_PUBLIC_FIREBASE_PROJECT_ID=
55
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=
66
EXPO_PUBLIC_FIREBASE_APP_ID=
7+
EXPO_GRAPHQL_ENDPOINT=
8+

app/(auth)/(home)/_layout.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ function HomeLayout() {
7777
name="index"
7878
options={{ href: null }}
7979
/>
80+
<Tabs.Screen
81+
name="profile/changeUsername"
82+
options={{ href: null }}
83+
/>
84+
<Tabs.Screen
85+
name="profile/language"
86+
options={{ href: null }}
87+
/>
88+
<Tabs.Screen
89+
name="profile/exploreGroups"
90+
options={{ href: null }}
91+
/>
8092
<Tabs.Screen
8193
name="projects"
8294
options={{
@@ -91,7 +103,7 @@ function HomeLayout() {
91103
}}
92104
/>
93105
<Tabs.Screen
94-
name="profile"
106+
name="profile/index"
95107
options={{
96108
title: 'Profile',
97109
// eslint-disable-next-line react/no-unstable-nested-components

app/(auth)/(home)/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { router } from 'expo-router';
33

44
function HomeIndex() {
55
useEffect(() => {
6-
router.replace('/projects');
6+
router.push('/projects');
77
}, []);
88

99
return null;

app/(auth)/(home)/profile.tsx

Lines changed: 0 additions & 84 deletions
This file was deleted.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import {
2+
useCallback,
3+
useState,
4+
} from 'react';
5+
import { useRouter } from 'expo-router';
6+
import { updateProfile } from 'firebase/auth';
7+
import {
8+
ref,
9+
update,
10+
} from 'firebase/database';
11+
12+
import BlockListView from '@/components/BlockListView';
13+
import Button from '@/components/Button';
14+
import Page from '@/components/Page';
15+
import PageHeader from '@/components/PageHeader';
16+
import TextInput from '@/components/TextInput';
17+
import { showAlert } from '@/components/Toast';
18+
import useAuth from '@/hooks/useAuth';
19+
import useFirebaseMutation from '@/hooks/useFirebaseMutation';
20+
import {
21+
MIN_USERNAME_LENGTH,
22+
usernameExists,
23+
validateUserName,
24+
} from '@/utils/common';
25+
import { firebaseDatabase } from '@/utils/firebase';
26+
27+
const usernameErrorText = 'Username must be at least 4 characters long and cannot contain space and uppercase';
28+
const usernameAlreadyExists = 'Username must be at least 4 characters long and cannot contain space and uppercase';
29+
const errorTitle = 'Failed to Change s UsernameUsername must be at least 4 characters long and cannot contain space and uppercase';
30+
const successTitle = 'Username updated successfully!';
31+
32+
export default function ChangePassword() {
33+
const { user, setUser } = useAuth();
34+
const [oldUsername, setOldUsername] = useState<string>('');
35+
const [newUserName, setNewUserName] = useState<string>('');
36+
const router = useRouter();
37+
const { mutate, isLoading } = useFirebaseMutation();
38+
39+
const handleUpdateProfile = useCallback(async () => {
40+
const isCurrentUsernameCorrect = user?.displayName === oldUsername;
41+
const isSame = newUserName === oldUsername;
42+
const isValid = validateUserName(newUserName) && !isSame && isCurrentUsernameCorrect;
43+
if (!isValid) {
44+
showAlert({
45+
title: errorTitle,
46+
message: usernameErrorText,
47+
alertType: 'error',
48+
});
49+
return;
50+
}
51+
const userNameAlreadyExist = await usernameExists(newUserName);
52+
if (userNameAlreadyExist) {
53+
showAlert({
54+
title: errorTitle,
55+
message: usernameAlreadyExists,
56+
alertType: 'error',
57+
shouldHideAfterDelay: false,
58+
});
59+
return;
60+
}
61+
mutate(newUserName, async (name) => {
62+
if (!user) return;
63+
await updateProfile(user, { displayName: name as string });
64+
await update(ref(firebaseDatabase, `users/${user.uid}`), { username: name });
65+
await user.reload();
66+
setUser({ ...user });
67+
setNewUserName('');
68+
setOldUsername('');
69+
}).then(() => {
70+
showAlert({
71+
title: 'Success',
72+
message: successTitle,
73+
alertType: 'success',
74+
});
75+
router.back();
76+
}).catch((err) => {
77+
// eslint-disable-next-line no-console
78+
console.error(err);
79+
});
80+
}, [oldUsername, newUserName, user, setUser, mutate, router]);
81+
82+
return (
83+
<Page title="Change Username">
84+
<PageHeader heading="Change Username" />
85+
<BlockListView
86+
withPadding
87+
>
88+
<TextInput
89+
variant="normal"
90+
labelText="Current Username"
91+
value={oldUsername}
92+
onChangeText={setOldUsername}
93+
/>
94+
<TextInput
95+
variant="normal"
96+
labelText="New Username"
97+
value={newUserName}
98+
onChangeText={setNewUserName}
99+
maxLength={128}
100+
editable={!isLoading}
101+
/>
102+
<Button
103+
name="change-username"
104+
title={isLoading
105+
? ('Updating Username')
106+
: ('Confirm UserName Change')}
107+
disabled={
108+
isLoading
109+
|| (newUserName?.length ?? 0) < MIN_USERNAME_LENGTH
110+
}
111+
onPress={handleUpdateProfile}
112+
113+
/>
114+
</BlockListView>
115+
</Page>
116+
);
117+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import React, {
2+
useCallback,
3+
useMemo,
4+
useState,
5+
} from 'react';
6+
import {
7+
ActivityIndicator,
8+
ScrollView,
9+
StyleSheet,
10+
} from 'react-native';
11+
import { useRouter } from 'expo-router';
12+
import { query } from 'firebase/database';
13+
14+
import ClickableListItem from '@/components/ClickableListItems';
15+
import Page from '@/components/Page';
16+
import PageHeader from '@/components/PageHeader';
17+
import Text from '@/components/Text';
18+
import TextInput from '@/components/TextInput';
19+
import useFirebaseDatabaseList from '@/hooks/useFirebaseDatabaseList';
20+
import { rankedSearchOnList } from '@/utils/common';
21+
import { firebaseRef } from '@/utils/firebase';
22+
23+
const styles = StyleSheet.create({
24+
hintText: {
25+
textAlign: 'center',
26+
opacity: 0.5,
27+
marginTop: 20,
28+
},
29+
});
30+
31+
export interface UserGroupWithKey {
32+
key: string;
33+
name?: string;
34+
archivedAt?: string;
35+
archivedBy?: string;
36+
[key: string]: unknown;
37+
}
38+
39+
function ExploreGroups() {
40+
const [searchText, setSearchText] = useState<string>('');
41+
const router = useRouter();
42+
const userGroupsQuery = useMemo(() => (
43+
query(
44+
firebaseRef('/v2/userGroups/'),
45+
)
46+
), []);
47+
48+
const { list, pending } = useFirebaseDatabaseList<UserGroupWithKey>({
49+
query: userGroupsQuery,
50+
});
51+
52+
const nonArchivedUserGroups = list.filter(
53+
(ug) => !ug.archivedAt && !ug.archivedBy,
54+
);
55+
56+
const filteredGroups = useMemo(() => {
57+
if (searchText.length < 3) {
58+
return [];
59+
}
60+
61+
return rankedSearchOnList(
62+
nonArchivedUserGroups,
63+
searchText,
64+
(group) => group.name ?? '',
65+
);
66+
}, [searchText, nonArchivedUserGroups]);
67+
68+
const onHandleItemClick = useCallback((key?: string) => {
69+
router.push(`exploreGroup/${key}`);
70+
}, [router]);
71+
72+
return (
73+
<Page
74+
title="Explore Group"
75+
isScrollable={false}
76+
>
77+
<PageHeader heading="Explore Group" />
78+
{pending && <ActivityIndicator size="large" />}
79+
80+
<TextInput
81+
placeholder="Enter search Text"
82+
variant="normal"
83+
value={searchText}
84+
onChangeText={setSearchText}
85+
/>
86+
87+
{searchText.length < 3 && (
88+
<Text style={styles.hintText}>
89+
Start typing group name to begin the search! At least 3 characters
90+
</Text>
91+
)}
92+
93+
{searchText.length >= 3 && (
94+
<ScrollView>
95+
{filteredGroups.length > 0 ? (
96+
filteredGroups.map((group) => (
97+
<ClickableListItem
98+
key={group.key}
99+
name={group.key}
100+
title={group.name ?? ''}
101+
onPress={onHandleItemClick}
102+
/>
103+
))
104+
) : (
105+
<Text style={styles.hintText}>
106+
There are no groups matching your search text!
107+
</Text>
108+
)}
109+
</ScrollView>
110+
)}
111+
</Page>
112+
);
113+
}
114+
115+
export default ExploreGroups;

0 commit comments

Comments
 (0)