Files
EMI-ExpoAPP/Views/NewGroup.js
T
2026-02-20 21:35:54 -05:00

228 lines
7.6 KiB
JavaScript

import React from "react";
import { StyleSheet, SafeAreaView, ImageBackground, View, ScrollView, Alert, Image, Platform } from 'react-native';
import { Title, TextInput, Button, Text, Switch } from 'react-native-paper';
import API from "../API";
import * as ImagePicker from 'expo-image-picker';
const DefaultPhoto = "https://social.emmint.com/uploads/e6f9be6d665dc43417701bf16a90122c.png";
const NewGroup = ({ navigation }) => {
const [title, setTitle] = React.useState('');
const [subtitle, setSubtitle] = React.useState('');
const [description, setDescription] = React.useState('');
const [isPrivate, setIsPrivate] = React.useState(false);
const [isSubmitting, setIsSubmitting] = React.useState(false);
const [selectedPhoto, setSelectedPhoto] = React.useState(null);
const pickImage = async () => {
if (isSubmitting) return;
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 0.6,
});
if (!result.canceled && result.assets?.[0]) {
setSelectedPhoto(result.assets[0]);
}
};
const handleUploadPhoto = async (photo) => {
if (!photo) return '';
const uri = Platform.OS === "android" ? photo.uri : photo.uri.replace("file://", "");
const filename = photo.uri.split("/").pop() || "image.jpg";
const match = /\.(\w+)$/.exec(filename);
const ext = match?.[1] || "jpg";
const type = match ? `image/${match[1]}` : "image/jpeg";
const formData = new FormData();
formData.append("banner", {
uri,
name: `image.${ext}`,
type,
});
try {
const uploaded = await fetch("https://social.emmint.com/upload.php", {
method: "POST",
body: formData,
headers: { "Content-Type": "multipart/form-data" }
})
.then((res) => res.json())
.then((data) => data?.fileName || '');
return uploaded;
} catch (error) {
return '';
}
};
const createGroup = async () => {
const cleanTitle = title.trim();
const cleanSubtitle = subtitle.trim();
const cleanDescription = description.trim();
if (!cleanTitle) {
return Alert.alert("Missing name", "Please add a group name.");
}
if (!cleanDescription) {
return Alert.alert("Missing description", "Please add a short group description.");
}
setIsSubmitting(true);
try {
const photoPath = selectedPhoto ? await handleUploadPhoto(selectedPhoto) : '';
if (selectedPhoto && !photoPath) {
return Alert.alert("Could not upload image", "Please try again.");
}
const data = await API.newGroup(cleanTitle, cleanSubtitle, cleanDescription, isPrivate, false, photoPath);
if (data?.status !== "ok") {
return Alert.alert("Could not create group", data?.status || "Please try again.");
}
setTitle('');
setSubtitle('');
setDescription('');
setIsPrivate(false);
setSelectedPhoto(null);
if (data?._id) {
return navigation.replace("Profile", { profileid: data._id });
}
Alert.alert(
"Group created",
"Your new group is ready. You can switch to it from the menu.",
[{ text: "OK", onPress: () => navigation.goBack() }]
);
} catch (error) {
Alert.alert("Could not create group", "Please try again.");
} finally {
setIsSubmitting(false);
}
};
return (
<SafeAreaView style={styles.safeArea}>
<ImageBackground
source={require("../assets/settings.png")}
style={styles.background}
imageStyle={styles.backgroundImage}
>
<ScrollView contentContainerStyle={styles.container}>
<Title style={styles.title}>New Group</Title>
<View style={styles.photoRow}>
<Image
source={{ uri: selectedPhoto?.uri || DefaultPhoto }}
style={styles.photoPreview}
/>
<Button mode="outlined" icon="photo" onPress={pickImage} disabled={isSubmitting}>
{selectedPhoto ? "Change image" : "Add group image"}
</Button>
</View>
<TextInput
mode="outlined"
label="Group name"
value={title}
onChangeText={setTitle}
style={styles.input}
/>
<TextInput
mode="outlined"
label="Subtitle (optional)"
value={subtitle}
onChangeText={setSubtitle}
style={styles.input}
/>
<TextInput
mode="outlined"
label="Description"
value={description}
onChangeText={setDescription}
multiline
numberOfLines={4}
style={styles.input}
/>
<View style={styles.switchRow}>
<View style={styles.switchTextWrap}>
<Text style={styles.switchTitle}>Private group</Text>
<Text style={styles.switchSubtitle}>Require approval before people can join.</Text>
</View>
<Switch value={isPrivate} onValueChange={setIsPrivate} />
</View>
<Button
mode="contained"
onPress={createGroup}
loading={isSubmitting}
disabled={isSubmitting}
style={styles.button}
>
Create Group
</Button>
</ScrollView>
</ImageBackground>
</SafeAreaView>
);
};
export default NewGroup;
const styles = StyleSheet.create({
safeArea: {
backgroundColor: "#edf2f7",
flex: 1,
},
background: {
flex: 1,
},
backgroundImage: {
resizeMode: "contain",
opacity: 0.05,
},
container: {
padding: 14,
paddingBottom: 24,
},
photoRow: {
alignItems: "center",
marginTop: 10,
marginBottom: 4,
},
photoPreview: {
width: 96,
height: 96,
borderRadius: 48,
marginBottom: 10,
backgroundColor: "#ddd",
},
title: {
paddingBottom: 8,
fontSize: 30,
marginTop: 8,
fontWeight: "bold",
color: "#777",
},
input: {
marginTop: 10,
backgroundColor: "#fff",
},
switchRow: {
marginTop: 16,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
switchTextWrap: {
flex: 1,
},
switchTitle: {
fontSize: 16,
fontWeight: "600",
},
switchSubtitle: {
color: "#666",
marginTop: 2,
},
button: {
marginTop: 22,
},
});