mirror of
https://github.com/urosran/cally.git
synced 2025-11-26 08:24:55 +00:00
Auth logic rework
This commit is contained in:
@ -1,44 +1,45 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import { Text, Button, TextInput } from "react-native";
|
import {Button, Text, TextInput} from "react-native";
|
||||||
import { View, TextField, Picker, Checkbox } from "react-native-ui-lib";
|
import {Checkbox, Picker, TextField, View} from "react-native-ui-lib";
|
||||||
import useAuth from "@/hooks/firebase/useAuth";
|
import useAuth from "@/hooks/firebase/useAuth";
|
||||||
import useChildren from "@/hooks/firebase/useChildren";
|
import useChildren from "@/hooks/firebase/useChildren";
|
||||||
import useCaregivers from "@/hooks/firebase/useCaregivers";
|
import useCaregivers from "@/hooks/firebase/useCaregivers";
|
||||||
import {useCreateSubUser} from "@/hooks/firebase/useCreateSubUser";
|
import {useCreateSubUser} from "@/hooks/firebase/useCreateSubUser";
|
||||||
import {email} from "@sideway/address";
|
|
||||||
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
||||||
import {uuidv4} from "@firebase/util";
|
import {uuidv4} from "@firebase/util";
|
||||||
|
import {useGetChildrenByParentId} from "@/hooks/firebase/useGetChildrenByParentId";
|
||||||
|
import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
|
||||||
|
|
||||||
const Screen: React.FC = () => {
|
const Screen: React.FC = () => {
|
||||||
const {
|
const {user, profileType, profileData} = useAuthContext()
|
||||||
user,
|
|
||||||
profileType,
|
|
||||||
email,
|
|
||||||
setEmail,
|
|
||||||
password,
|
|
||||||
setPassword,
|
|
||||||
handleLogin,
|
|
||||||
handleSignOut,
|
|
||||||
handleProfileTypeSelection,
|
|
||||||
handleRegister,
|
|
||||||
|
|
||||||
} = useAuth();
|
const {
|
||||||
|
email,
|
||||||
|
setEmail,
|
||||||
|
password,
|
||||||
|
setPassword,
|
||||||
|
handleLogin,
|
||||||
|
handleSignOut,
|
||||||
|
handleProfileTypeSelection,
|
||||||
|
handleRegister,
|
||||||
|
} = useAuth();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
child,
|
child,
|
||||||
setChild,
|
setChild,
|
||||||
fetchChildren,
|
handleNewChild,
|
||||||
handleNewChild,
|
} = useChildren(user);
|
||||||
} = useChildren(user);
|
|
||||||
|
|
||||||
const {
|
const {data: childrenByParentId} = useGetChildrenByParentId()
|
||||||
caregivers,
|
|
||||||
caregiver,
|
const {
|
||||||
setCaregiver,
|
caregivers,
|
||||||
fetchCaregivers,
|
caregiver,
|
||||||
handleNewCaregiver,
|
setCaregiver,
|
||||||
} = useCaregivers();
|
fetchCaregivers,
|
||||||
|
handleNewCaregiver,
|
||||||
|
} = useCaregivers();
|
||||||
|
|
||||||
const {mutateAsync: createSubUser} = useCreateSubUser()
|
const {mutateAsync: createSubUser} = useCreateSubUser()
|
||||||
|
|
||||||
@ -52,174 +53,172 @@ const Screen: React.FC = () => {
|
|||||||
// contact: "+381628334",
|
// contact: "+381628334",
|
||||||
// ...child
|
// ...child
|
||||||
// })
|
// })
|
||||||
await fetchChildren();
|
|
||||||
await fetchCaregivers();
|
await fetchCaregivers();
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (user) {
|
|
||||||
fetchChildren();
|
|
||||||
fetchCaregivers();
|
|
||||||
}
|
}
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
const [isParent, setIsParent] = useState<boolean>(false);
|
if (user) {
|
||||||
const [isChild, setIsChild] = useState<boolean>(false);
|
fetchCaregivers();
|
||||||
const [isCaregiver, setIsCaregiver] = useState<boolean>(false);
|
}
|
||||||
|
}, [user]);
|
||||||
useEffect(() => {
|
|
||||||
if (isParent) handleProfileTypeSelection("parent");
|
|
||||||
if (isChild) handleProfileTypeSelection("child");
|
|
||||||
if (isCaregiver) handleProfileTypeSelection("caregiver");
|
|
||||||
}, [isParent, isChild, isCaregiver]);
|
|
||||||
|
|
||||||
const renderLogin = () => (
|
|
||||||
<View marginH-20>
|
|
||||||
<TextInput placeholder="Email" value={email} onChangeText={setEmail} />
|
|
||||||
<TextInput
|
|
||||||
placeholder="Password"
|
|
||||||
value={password}
|
|
||||||
onChangeText={setPassword}
|
|
||||||
secureTextEntry
|
|
||||||
/>
|
|
||||||
<Button title="Login" onPress={handleLogin} />
|
|
||||||
<Button title="Register" onPress={handleRegister} />
|
|
||||||
<Text>Choose Profile Type:</Text>
|
|
||||||
<Checkbox
|
|
||||||
label="Parent"
|
|
||||||
value={isParent}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setIsParent(value);
|
|
||||||
if (value) {
|
|
||||||
setIsChild(false);
|
|
||||||
setIsCaregiver(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
label="Child"
|
|
||||||
value={isChild}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setIsChild(value);
|
|
||||||
if (value) {
|
|
||||||
setIsParent(false);
|
|
||||||
setIsCaregiver(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
label="Caregiver"
|
|
||||||
value={isCaregiver}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setIsCaregiver(value);
|
|
||||||
if (value) {
|
|
||||||
setIsParent(false);
|
|
||||||
setIsChild(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View>
|
const [isParent, setIsParent] = useState<boolean>(false);
|
||||||
{user ? (
|
const [isChild, setIsChild] = useState<boolean>(false);
|
||||||
<View paddingH-20>
|
const [isCaregiver, setIsCaregiver] = useState<boolean>(false);
|
||||||
{profileType === "parent" && <Text>Parent</Text>}
|
|
||||||
{profileType === "child" && <Text>Child</Text>}
|
useEffect(() => {
|
||||||
{profileType === "caregiver" && <Text>Caregiver</Text>}
|
if (isParent) handleProfileTypeSelection("parent");
|
||||||
<Button title="Sign Out" onPress={handleSignOut} />
|
if (isChild) handleProfileTypeSelection("child");
|
||||||
<TextField
|
if (isCaregiver) handleProfileTypeSelection("caregiver");
|
||||||
placeholder={"Child Name"}
|
}, [isParent, isChild, isCaregiver]);
|
||||||
floatingPlaceholder
|
|
||||||
value={child.name}
|
const renderLogin = () => (
|
||||||
onChangeText={(value) =>
|
<View marginH-20>
|
||||||
setChild((prevChild) => ({
|
<TextInput placeholder="Email" value={email} onChangeText={setEmail}/>
|
||||||
...prevChild,
|
<TextInput
|
||||||
name: value,
|
placeholder="Password"
|
||||||
}))
|
value={password}
|
||||||
}
|
onChangeText={setPassword}
|
||||||
enableErrors
|
secureTextEntry
|
||||||
validate={["required", (value: string) => value.length > 6]}
|
/>
|
||||||
validationMessage={[
|
<Button title="Login" onPress={handleLogin}/>
|
||||||
"Field is required",
|
<Button title="Register" onPress={handleRegister}/>
|
||||||
"Name is too short",
|
<Text>Choose Profile Type:</Text>
|
||||||
]}
|
<Checkbox
|
||||||
showCharCounter
|
label="Parent"
|
||||||
maxLength={30}
|
value={isParent}
|
||||||
/>
|
onValueChange={(value) => {
|
||||||
<Button
|
setIsParent(value);
|
||||||
title="Add Child"
|
if (value) {
|
||||||
onPress={() => {
|
setIsChild(false);
|
||||||
createNewSubUser(child);
|
setIsCaregiver(false);
|
||||||
}}
|
}
|
||||||
/>
|
}}
|
||||||
<TextField
|
/>
|
||||||
placeholder={"Caregiver Name"}
|
<Checkbox
|
||||||
floatingPlaceholder
|
label="Child"
|
||||||
value={caregiver.name}
|
value={isChild}
|
||||||
onChangeText={(value) =>
|
onValueChange={(value) => {
|
||||||
setCaregiver((prevCaregiver) => ({
|
setIsChild(value);
|
||||||
...prevCaregiver,
|
if (value) {
|
||||||
name: value,
|
setIsParent(false);
|
||||||
}))
|
setIsCaregiver(false);
|
||||||
}
|
}
|
||||||
enableErrors
|
}}
|
||||||
validate={["required", (value: string) => value.length > 6]}
|
/>
|
||||||
validationMessage={["Field is required"]}
|
<Checkbox
|
||||||
showCharCounter
|
label="Caregiver"
|
||||||
maxLength={30}
|
value={isCaregiver}
|
||||||
/>
|
onValueChange={(value) => {
|
||||||
<TextField
|
setIsCaregiver(value);
|
||||||
placeholder={"Caregiver Contact"}
|
if (value) {
|
||||||
floatingPlaceholder
|
setIsParent(false);
|
||||||
value={caregiver.contact}
|
setIsChild(false);
|
||||||
onChangeText={(value) =>
|
}
|
||||||
setCaregiver((prevCaregiver) => ({
|
}}
|
||||||
...prevCaregiver,
|
/>
|
||||||
contact: value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
enableErrors
|
|
||||||
validate={[
|
|
||||||
"number",
|
|
||||||
"required",
|
|
||||||
(value: string) => value.length > 9,
|
|
||||||
]}
|
|
||||||
validationMessage={["Field is required"]}
|
|
||||||
showCharCounter
|
|
||||||
maxLength={30}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
title="Add Caregiver"
|
|
||||||
onPress={() => {
|
|
||||||
createNewSubUser(caregiver);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<View margin-20>
|
|
||||||
<Text>Children:</Text>
|
|
||||||
{children.map((child) => (
|
|
||||||
<View key={child.name} row>
|
|
||||||
<Text>Name: {child.name} </Text>
|
|
||||||
<Picker label="Pick Caregiver">
|
|
||||||
{caregivers.map((item) => (
|
|
||||||
<Picker.Item
|
|
||||||
key={item.name}
|
|
||||||
label={item.name}
|
|
||||||
value={item.name}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Picker>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
) : (
|
);
|
||||||
renderLogin()
|
|
||||||
)}
|
return (
|
||||||
</View>
|
<View>
|
||||||
);
|
{user ? (
|
||||||
|
<View paddingH-20>
|
||||||
|
{profileType === ProfileType.parent && <Text>Parent</Text>}
|
||||||
|
{profileType === ProfileType.child && <Text>Child</Text>}
|
||||||
|
{profileType === ProfileType.caregiver && <Text>Caregiver</Text>}
|
||||||
|
<Button title="Sign Out" onPress={handleSignOut}/>
|
||||||
|
<TextField
|
||||||
|
placeholder={"Child Name"}
|
||||||
|
floatingPlaceholder
|
||||||
|
value={child.name}
|
||||||
|
onChangeText={(value) =>
|
||||||
|
setChild((prevChild) => ({
|
||||||
|
...prevChild,
|
||||||
|
name: value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
enableErrors
|
||||||
|
validate={["required", (value: string) => value.length > 6]}
|
||||||
|
validationMessage={[
|
||||||
|
"Field is required",
|
||||||
|
"Name is too short",
|
||||||
|
]}
|
||||||
|
showCharCounter
|
||||||
|
maxLength={30}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="Add Child"
|
||||||
|
onPress={() => {
|
||||||
|
createNewSubUser(child);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
placeholder={"Caregiver Name"}
|
||||||
|
floatingPlaceholder
|
||||||
|
value={caregiver.name}
|
||||||
|
onChangeText={(value) =>
|
||||||
|
setCaregiver((prevCaregiver) => ({
|
||||||
|
...prevCaregiver,
|
||||||
|
name: value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
enableErrors
|
||||||
|
validate={["required", (value: string) => value.length > 6]}
|
||||||
|
validationMessage={["Field is required"]}
|
||||||
|
showCharCounter
|
||||||
|
maxLength={30}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
placeholder={"Caregiver Contact"}
|
||||||
|
floatingPlaceholder
|
||||||
|
value={caregiver.contact}
|
||||||
|
onChangeText={(value) =>
|
||||||
|
setCaregiver((prevCaregiver) => ({
|
||||||
|
...prevCaregiver,
|
||||||
|
contact: value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
enableErrors
|
||||||
|
validate={[
|
||||||
|
"number",
|
||||||
|
"required",
|
||||||
|
(value: string) => value.length > 9,
|
||||||
|
]}
|
||||||
|
validationMessage={["Field is required"]}
|
||||||
|
showCharCounter
|
||||||
|
maxLength={30}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="Add Caregiver"
|
||||||
|
onPress={() => {
|
||||||
|
createNewSubUser(caregiver);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<View margin-20>
|
||||||
|
<Text>Children:</Text>
|
||||||
|
{children.map((child) => (
|
||||||
|
<View key={child.name} row>
|
||||||
|
<Text>Name: {child.name} </Text>
|
||||||
|
<Picker label="Pick Caregiver">
|
||||||
|
{caregivers.map((item) => (
|
||||||
|
<Picker.Item
|
||||||
|
key={item.name}
|
||||||
|
label={item.name}
|
||||||
|
value={item.name}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Picker>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
renderLogin()
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Screen;
|
export default Screen;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {useRouter} from "expo-router";
|
|||||||
import firestore from "@react-native-firebase/firestore";
|
import firestore from "@react-native-firebase/firestore";
|
||||||
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
||||||
|
|
||||||
type ProfileType = "parent" | "child" | "caregiver" | null;
|
export enum ProfileType { "parent", "child", "caregiver" }
|
||||||
|
|
||||||
interface IAuthContext {
|
interface IAuthContext {
|
||||||
user: FirebaseAuthTypes.User | null,
|
user: FirebaseAuthTypes.User | null,
|
||||||
@ -28,6 +28,8 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
|
|||||||
const onAuthStateChanged = async (user: FirebaseAuthTypes.User | null) => {
|
const onAuthStateChanged = async (user: FirebaseAuthTypes.User | null) => {
|
||||||
setUser(user);
|
setUser(user);
|
||||||
|
|
||||||
|
console.log(user)
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
try {
|
try {
|
||||||
const documentSnapshot = await firestore()
|
const documentSnapshot = await firestore()
|
||||||
@ -41,7 +43,7 @@ export const AuthContextProvider: FC<{ children: ReactNode }> = ({children}) =>
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching user profile type:", error);
|
console.error("Error fetching user profile type:", error);
|
||||||
setProfileType(null);
|
setProfileType(undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {useMutation} from "react-query";
|
|||||||
import auth from "@react-native-firebase/auth";
|
import auth from "@react-native-firebase/auth";
|
||||||
import firestore from "@react-native-firebase/firestore";
|
import firestore from "@react-native-firebase/firestore";
|
||||||
import {User,} from "./types/profileTypes";
|
import {User,} from "./types/profileTypes";
|
||||||
|
import {useSignUp} from "@/hooks/firebase/useSignUp";
|
||||||
|
|
||||||
type ProfileType = "parent" | "child" | "caregiver" | null;
|
type ProfileType = "parent" | "child" | "caregiver" | null;
|
||||||
|
|
||||||
@ -17,6 +18,8 @@ const useAuth = () => {
|
|||||||
auth().signInWithEmailAndPassword(email, password)
|
auth().signInWithEmailAndPassword(email, password)
|
||||||
);
|
);
|
||||||
const signOutMutation = useMutation(() => auth().signOut());
|
const signOutMutation = useMutation(() => auth().signOut());
|
||||||
|
const {mutateAsync: signUp } = useSignUp()
|
||||||
|
|
||||||
/*const setProfileDataMutation = useMutation((profileData) => {
|
/*const setProfileDataMutation = useMutation((profileData) => {
|
||||||
const currentUser = auth().currentUser;
|
const currentUser = auth().currentUser;
|
||||||
if (currentUser) {
|
if (currentUser) {
|
||||||
@ -35,11 +38,12 @@ const useAuth = () => {
|
|||||||
const handleRegister = async () => {
|
const handleRegister = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await createUserMutation.mutateAsync();
|
await signUp({email, password})
|
||||||
|
// await createUserMutation.mutateAsync();
|
||||||
console.log("User registered!");
|
console.log("User registered!");
|
||||||
|
|
||||||
await signInMutation.mutateAsync();
|
// await signInMutation.mutateAsync();
|
||||||
console.log("User signed in!");
|
// console.log("User signed in!");
|
||||||
|
|
||||||
/*let profileData: ParentProfile | ChildProfile | CaregiverProfile;
|
/*let profileData: ParentProfile | ChildProfile | CaregiverProfile;
|
||||||
switch (profileType) {
|
switch (profileType) {
|
||||||
|
|||||||
@ -1,12 +1,23 @@
|
|||||||
import {useMutation} from "react-query";
|
import {useMutation, useQueryClient} from "react-query";
|
||||||
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
||||||
import functions from '@react-native-firebase/functions';
|
import functions from '@react-native-firebase/functions';
|
||||||
|
import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
|
||||||
|
|
||||||
export const useCreateSubUser = () => {
|
export const useCreateSubUser = () => {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const { profileType } = useAuthContext()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["createSubUser"],
|
mutationKey: ["createSubUser"],
|
||||||
mutationFn: async ({email, password, ...userProfile}: { email: string, password: string } & UserProfile) => {
|
mutationFn: async ({email, password, ...userProfile}: { email: string, password: string } & UserProfile) => {
|
||||||
return await functions().httpsCallable("createSubUser")({email, password, ...userProfile})
|
if(profileType === ProfileType.parent) {
|
||||||
|
return await functions().httpsCallable("createSubUser")({email, password, ...userProfile})
|
||||||
|
} else {
|
||||||
|
throw Error("Can't create sub-users as a non-parent.")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({queryKey: ["getChildrenByParentId"]})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
34
hooks/firebase/useGetChildrenByParentId.ts
Normal file
34
hooks/firebase/useGetChildrenByParentId.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import {useQuery} from "react-query";
|
||||||
|
import {ChildProfile} from "@/hooks/firebase/types/profileTypes";
|
||||||
|
import firestore from "@react-native-firebase/firestore";
|
||||||
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
|
|
||||||
|
export const useGetChildrenByParentId = () => {
|
||||||
|
const {user} = useAuthContext()
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["getChildrenByParentId", user?.uid],
|
||||||
|
queryFn: async (): Promise<ChildProfile[]> => {
|
||||||
|
try {
|
||||||
|
const snapshot = await firestore()
|
||||||
|
.collection("Profiles")
|
||||||
|
.where("userType", "==", "child")
|
||||||
|
.where("parentId", "==", user?.uid!)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
return snapshot.docs.map((doc) => {
|
||||||
|
const data = doc.data();
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
birthday: data.birthday.toDate(),
|
||||||
|
} as ChildProfile;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error retrieving child users:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled: !!user?.uid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,11 +1,15 @@
|
|||||||
import {useMutation} from "react-query";
|
import {useMutation} from "react-query";
|
||||||
import auth from "@react-native-firebase/auth";
|
import auth from "@react-native-firebase/auth";
|
||||||
|
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||||
|
|
||||||
export const useSignUp = () => {
|
export const useSignUp = () => {
|
||||||
|
const { mutateAsync: updateUserData } = useUpdateUserData()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["signUp"],
|
mutationKey: ["signUp"],
|
||||||
mutationFn: async ({email, password}: { email: string, password: string }) => {
|
mutationFn: async ({email, password}: { email: string, password: string }) => {
|
||||||
await auth().createUserWithEmailAndPassword(email, password)
|
await auth().createUserWithEmailAndPassword(email, password)
|
||||||
|
await updateUserData({userType: "parent", email, password})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user