Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add profile settings menu #265

Merged
merged 9 commits into from
Sep 13, 2024
171 changes: 171 additions & 0 deletions client/app/components/settings/TextInputs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React, {useState} from "react";
import {
Button,
Modal,
SafeAreaView,
Text,
TextInput,
View,
StyleSheet,
Pressable,
TouchableWithoutFeedback
} from "react-native";

type GenericTextInputProps = {
defaultValue: string;
isVisible: boolean;
visibleSetter: Function;
outputSetter: Function;
headerText: string;
errorMessage: string;
maxLength: number;
inputValidator: Function;

}

const GenericTextInput = ({
defaultValue,
isVisible,
visibleSetter,
outputSetter,
headerText,
errorMessage,
maxLength,
inputValidator,
}: GenericTextInputProps) => {
const[textInput, setTextInput] = useState('');
const[error, setError] = useState('');

return(
<Modal
animationType="fade"
transparent={true}
visible={isVisible}
onRequestClose={() => {
visibleSetter(false);
setError('');
}}
>
<Pressable onPress={() => {
visibleSetter(false);
setError('');
}}>
<SafeAreaView style={styles.centeredView}>
<TouchableWithoutFeedback>
<View style={styles.inputModal}>
<Text style={styles.inputHeader}>{headerText}</Text>
<Text style={error==='' ? {display:"none"} : {color: "red"}}>{error}</Text>
<TextInput
defaultValue={defaultValue}
maxLength={maxLength}
style={styles.textInput}
onChangeText={text => {setTextInput(text); setError('');}}
/>
<View style={styles.buttonContainer}>
<Button title="Cancel"
onPress={() => {
visibleSetter(false);
setError('');
}}
/>
<Button title="Save" onPress={() => {
if (inputValidator(textInput)) {
visibleSetter(false);
outputSetter(textInput);
defaultValue=textInput;
setError('');
} else
setError(errorMessage);
}}
/>
</View>
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</Pressable>
</Modal>
)};

type InputProps = {
defaultValue: string;
isVisible: boolean;
visibleSetter: Function;
outputSetter: Function;
};

export const DisplayNameInput = ({
defaultValue,
isVisible,
visibleSetter,
outputSetter,
}: InputProps) => GenericTextInput({
defaultValue: defaultValue,
isVisible: isVisible,
visibleSetter: visibleSetter,
outputSetter: outputSetter,
headerText: "Edit Display Name",
errorMessage: "Please enter a display name.",
maxLength: 12,
inputValidator: (input: string) => input.length > 0,
});

export const ColorInput = ({
defaultValue,
isVisible,
visibleSetter,
outputSetter,
}: InputProps) => GenericTextInput({
defaultValue: defaultValue,
isVisible: isVisible,
visibleSetter: visibleSetter,
outputSetter: outputSetter,
headerText: "Edit Profile Color",
errorMessage: "Please enter a valid hex code.",
maxLength: 7,
inputValidator: (input: string) => (/^#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3}$/).exec(input),
});

const styles = StyleSheet.create({
inputHeader: {
fontSize: 14,
fontWeight: "600",
color: "#a7a7a7",
textTransform: "uppercase",
letterSpacing: 1.2,
},
centeredView: {
height: "100%",
justifyContent: "flex-start",
alignItems: "center",
backgroundColor: "rgba(54, 54, 54, 0.5)",
},
inputModal: {
alignItems: "center",
backgroundColor: "#cccccc",
marginTop: "50%",
width: "70%",
borderRadius: 20,
padding: "5%",
shadowColor: "black",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
buttonContainer: {
paddingHorizontal: "5%",
flexDirection: 'row',
justifyContent: 'space-between',
width: "90%",
},
textInput: {
marginVertical: "5%",
width: "75%",
textAlign: "center",
borderBottomWidth: 2,
fontSize: 20,
},
});
138 changes: 137 additions & 1 deletion client/app/screens/settings/SettingsScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import React, { useState } from "react";
import { SafeAreaView, Text, StyleSheet, View, ScrollView } from "react-native";
import {
SafeAreaView,
Text,
StyleSheet,
View,
ScrollView,
Image,
Pressable,
Modal,
Button,
FlatList,
TouchableWithoutFeedback
} from "react-native";

import { SettingsItem } from "../../components/settings/SettingsItem";
import {ColorInput, DisplayNameInput} from "@app/components/settings/TextInputs";

// List of settings items
// toggle type: a switch
Expand Down Expand Up @@ -33,18 +46,122 @@ const Sections = [
const SettingsScreen: React.FC = () => {
// settings values (will be changed later to reflect the actual settings)
const [data, setData] = useState({
displayName: "Display Name",
profilePicIndex: 0, // index for icons array
profileColor: "#1199ff",
notifyNewMessage: true,
darkMode: false,
language: "English",
deleteMessages: false,
});

const[profileVisible, setProfileVisible] = useState(false);
const[inputVisible, setInputVisible] = useState({
displayName: false,
profileColor: false,
});


const iconStyle = [styles.icon, {backgroundColor: data.profileColor}]

const icons = [
require("../../../assets/icons/user/face_01.png"),
require("../../../assets/icons/user/face_02.png"),
require("../../../assets/icons/user/face_03.png"),
require("../../../assets/icons/user/face_04.png"),
require("../../../assets/icons/user/face_05.png"),
require("../../../assets/icons/user/face_06.png"),
require("../../../assets/icons/user/face_07.png"),
require("../../../assets/icons/user/fake_pfp.jpg"),
];

return (
<SafeAreaView style={styles.safeAreaStyle}>
<ScrollView style={styles.container}>

{/* User Settings Menu */}
<Modal
dyland88 marked this conversation as resolved.
Show resolved Hide resolved
animationType="fade"
transparent={true}
visible={profileVisible}
onRequestClose={() => setProfileVisible(false)}
>

<Pressable onPress={() => setProfileVisible(false)}>
<SafeAreaView style={{height:"100%"}}>
<TouchableWithoutFeedback>
<View style={styles.userModal}>
<View style={styles.header}>
<Text style={styles.headerText}>Hi {data.displayName}!</Text>
<Text> </Text>
<Pressable onPress={() => setProfileVisible(false)}>
<Image
style={iconStyle}
source={icons[data.profilePicIndex]}
/>
</Pressable>
</View>

<DisplayNameInput
defaultValue={data.displayName}
isVisible={inputVisible.displayName}
visibleSetter={(value: boolean) => setInputVisible({...inputVisible, ["displayName"]: value})}
outputSetter={(output: string) => setData({...data, ["displayName"]: output})}
/>
<ColorInput
defaultValue={data.profileColor}
isVisible={inputVisible.profileColor}
visibleSetter={(value: boolean) => setInputVisible({...inputVisible, ["profileColor"]: value})}
outputSetter={(output: string) => setData({...data, ["profileColor"]: output})}
/>

{/* User Settings */}
<View style={[styles.section, {height:"100%"}]}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionHeaderText}>Edit Profile</Text>
</View>
<View style={styles.sectionContent}>
<Button
title="Edit Display Name"
onPress={() => setInputVisible({...inputVisible, ["displayName"]: true})}
/>
<Button
title="Edit Profile Color"
onPress={() => setInputVisible({...inputVisible, ["profileColor"]: true})}
/>
</View>
<View style={styles.sectionHeader}>
<Text style={styles.sectionHeaderText}>Change profile picture</Text>
</View>
<View style={[styles.sectionContent, {alignItems: "center"}]}>
<FlatList data={icons}
numColumns={6}
renderItem={icon => (
<Pressable onPress={() => setData({ ...data, ["profilePicIndex"]: icon.index })}>
<Image style={[iconStyle, icon.index === data.profilePicIndex ? styles.selected:{margin: 5}]}
source={icon.item}/>
</Pressable>
)}>
</FlatList>
</View>
</View>
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</Pressable>
</Modal>

{/* Settings Screen */}
<View style={styles.header}>
<Text style={styles.headerText}>Settings</Text>
<Pressable onPress={() => setProfileVisible(true)}>
<Image
style={iconStyle}
source={icons[data.profilePicIndex]}
/>
</Pressable>
</View>

{Sections.map(({ header, items }) => (
<View style={styles.section} key={header}>
<View style={styles.sectionHeader}>
Expand Down Expand Up @@ -79,6 +196,8 @@ const styles = StyleSheet.create({
paddingVertical: 24,
},
header: {
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 24,
paddingRight: 24,
},
Expand Down Expand Up @@ -106,6 +225,23 @@ const styles = StyleSheet.create({
borderBottomWidth: 1,
borderColor: "#e3e3e3",
},
icon: {
width: 50,
height: 50,
borderRadius: 20,
},
selected: {
borderWidth: 3,
borderColor: "yellow",
margin: 5,
},
userModal: {
backgroundColor: "#d4d4d4",
borderRadius: 10,
paddingVertical: 24,
marginLeft: "auto",
height: "50%", // For some reason I can't get the view to auto fit height to children so just set to 50% for now
},
});

export default SettingsScreen;
Loading