Skip to content

Commit

Permalink
ideas/ideaCreate: fix form - category dropdown & ImagePicker
Browse files Browse the repository at this point in the history
Shifting state into subcomponents to prevent rerendering of ideaCreate
and therefore all children of it. All values would be re-initialized
and that is why it got vanished if interacting with certain form fields.

ideas/ideaCreate: fix form - labels

I decided to go with a more specific formFields set for labels.
Meaning LabelListContainer, LabelList and LabelFieldForm.

The reusability of having a universal usable CustomCheckBox is awesome, but
at this point too complex.

Especially using in conjunction with Formik.

Note: I put all commits together because they were changing each other.
I could not separate them better, because previous changes were changed
again later. Sorry.
  • Loading branch information
Kha authored and philli-m committed Jan 26, 2022
1 parent 41a3aac commit 5cf5745
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 152 deletions.
85 changes: 67 additions & 18 deletions components/formFields.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { TextInput, View } from 'react-native';
import { CheckBox } from 'react-native-elements';
import DropDownPicker from 'react-native-dropdown-picker';
Expand All @@ -7,8 +7,8 @@ import { TextSourceSans } from './TextSourceSans';
import IconFA from 'react-native-vector-icons/FontAwesome';
import { COLORS } from '../theme/colors';

// Setting list mode to scrollview globally
// FIXME: to be checked if we want this
/* Setting list mode to scrollview globally
FIXME: to be checked if we want this */
DropDownPicker.setListMode('SCROLLVIEW');

export const TextInputFormField = (props) => {
Expand Down Expand Up @@ -38,34 +38,82 @@ export const CheckBoxFormField = (props) => {
);
};

export const CustomCheckBoxContainerParent = (props) => {
export const LabelListContainer = (props) => {
return (
<View style={styles.customCheckBoxContainerParent}>
<View style={styles.customCheckBoxContainer}>
<TextSourceSans style={styles.formLabel}>{props.field}</TextSourceSans>
{props.children}
{props.children.touched && <TextSourceSans style={styles.formError}>{props.error}</TextSourceSans>}
</View>
);
};

export const CustomCheckBoxFormField = (props) => {
const plusIcon = (
<IconFA name='plus' size={16} color={COLORS.text.main} />
);
export const LabelList = props => {
const [ selectedLabels, setSelectedLabels ] = useState(props.selectedLabels);

const handleItemPressed = selectedLabel => {
let selectedLabelsCopy = [...selectedLabels];
/* the following code might be hard to read. It is comparing if the newly
selected label was selected before or not. If yes, then it removes the label,
if not it adds to the list. Then updating the state and sending to its parent
Note: this has to be done separately because setting State does not immediately change
the actual state (FIXME). */
const labelIndex = selectedLabels.findIndex(sl => sl.id === selectedLabel.id);
(labelIndex === -1) && (selectedLabelsCopy = [...selectedLabels, selectedLabel]);
(labelIndex !== -1) && [...selectedLabelsCopy.splice(labelIndex, 1)];
setSelectedLabels([...selectedLabelsCopy]);
props.onIconPress(selectedLabelsCopy);
};

const getCheckState = (choice) => {
return !!props.selectedLabels.find(sl => sl.id === choice.id);
};

return props.labelChoices.map(choice => (
<LabelFormField
key={`customCheckBox_${choice.value}`}
label={choice}
checked={getCheckState(choice)}
onIconPress={selectedLabel => handleItemPressed(selectedLabel)}
/>
));
};

export const LabelFormField = props => {
const [ checkState, setCheckState ] = useState(props.checked);
const plusIcon = <IconFA name="plus" size={16} color={COLORS.text.main} />;
const checkIcon = (
<IconFA name='check' size={16} color={COLORS.text.inverted} />
);

const toggleCheckState = () => {
setCheckState(!checkState);
props.onIconPress(props.label);
};

return (
<CheckBox
center
checked={props.checked}
onPress={props.onIconPress}
title={props.title}
checked={checkState}
onPress={toggleCheckState}
title={props.label.name}
titleProps={{}}
textStyle={ (props.checked) ? styles.customCheckBoxTitleChecked : styles.customCheckBoxTitle }
textStyle={
checkState ? (
styles.customCheckBoxTitleChecked
) : (
styles.customCheckBoxTitle
)
}
checkedIcon={checkIcon}
uncheckedIcon={plusIcon}
containerStyle={ (props.checked) ? styles.customCheckBoxContainerChecked : styles.customCheckBoxContainer }
containerStyle={
checkState ? (
styles.customCheckBoxButtonChecked
) : (
styles.customCheckBoxButton
)
}
/>
);
};
Expand All @@ -80,18 +128,19 @@ export const DropdownFormFieldContainer = (props) => {
};

export const DropdownFormField = (props) => {
const [open, setOpen] = React.useState(false);
const [ open, setOpen ] = useState(false);
const [ selected, setSelected ] = useState(props.value);
return (
<View>
<DropDownPicker
style={styles.dropdownFormField}
open={open}
value={props.value}
value={selected}
items={props.items}
setOpen={setOpen}
setValue={props.setValue}
setItems={props.setItems}
onChangeValue={props.onChangeValue}
setValue={setSelected}
onChangeValue={value => props.onChangeValue(value)}
containerStyle={{}}
/>
</View>
Expand Down
6 changes: 3 additions & 3 deletions components/formFields.styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ export const styles = StyleSheet.create({
checkBoxContainer: {
backgroundColor: COLORS.text.inverted,
},
customCheckBoxContainer: {
customCheckBoxButton: {
backgroundColor: COLORS.text.inverted,
width: '30%',
flexDirection: 'row',
flexWrap: 'wrap',
marginLeft: 0,
borderRadius: BORDERRADIUS.lg
},
customCheckBoxContainerChecked: {
customCheckBoxButtonChecked: {
color: COLORS.text.inverted,
backgroundColor: COLORS.primary,
width: '30%',
Expand All @@ -48,7 +48,7 @@ export const styles = StyleSheet.create({
marginLeft: 0,
borderRadius: BORDERRADIUS.lg,
},
customCheckBoxContainerParent: {
customCheckBoxContainer: {
flexDirection: 'row',
flexWrap: 'wrap'
},
Expand Down
50 changes: 43 additions & 7 deletions components/imageFormField.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { View, Alert, Image, Platform } from 'react-native';
import { Button } from 'react-native-elements';
import { CheckBoxFormField } from './formFields';
import { styles } from './imageFormField.styles';
import IconSLI from 'react-native-vector-icons/SimpleLineIcons';
import * as ImagePicker from 'expo-image-picker';
Expand All @@ -9,7 +10,7 @@ import mime from 'mime';

export const ImagePickerFormField = (props) => {
const [capturedImage, setCapturedImage] = useState(null);
const [image, setImage] = useState(null);
const [image, setImage] = useState(props.initialImage);

useEffect(() => {
(async () => {
Expand All @@ -20,13 +21,13 @@ export const ImagePickerFormField = (props) => {
}
}
})();
}, []);
}, [capturedImage, image]);

const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [16, 9],
aspect: [3, 2],
quality: 1,
});

Expand All @@ -45,13 +46,13 @@ export const ImagePickerFormField = (props) => {
}
}
})();
}, []);
}, [capturedImage, image]);

const captureImageHandler = async () => {
let result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [16, 9],
aspect: [3, 2],
quality: 1,
});

Expand All @@ -62,7 +63,7 @@ export const ImagePickerFormField = (props) => {

const extRegex = /\w+$/;
const imgExt = imgName[0].match(extRegex);
imgExt ? (result.mimeType = mime.getType(imgExt[0])) : 'image/jpeg';
imgExt ? (result.type = mime.getType(imgExt[0])) : 'image/jpeg';

if (!result.cancelled) {
setCapturedImage(result.uri);
Expand Down Expand Up @@ -106,10 +107,45 @@ export const ImagePickerFormField = (props) => {
};

export const ImageChoiceFormFieldContainer = (props) => {
const cloudUploadIcon = (
<IconSLI name='cloud-upload' style={[styles.imageButtonIcon, styles.textLight]} />
);

const [clicked, setClicked] = useState(false);

return (
<View style={styles.formRow}>
<TextSourceSans style={styles.formLabel}>{props.field}</TextSourceSans>
{props.children}
{(!clicked && !props.image) &&
<Button
buttonStyle={styles.imageButton}
icon={cloudUploadIcon}
type='fill'
title='Add image'
titleStyle={styles.textLight}
clicked={props.clicked}
setClicked={props.setClicked}
onPress={setClicked}
/>
}
<TextSourceSans style={styles.imageInfo}>
Visualize your idea. It must be min. 600 pixel wide and 400 pixel tall. Allowed file formats are png, jpeg, gif. The file size should be max. 5 MB.
</TextSourceSans>
{(clicked || props.image) &&
<>
<ImagePickerFormField
onSetImage={props.onSetImage}
initialImage={props.image}
/>
<CheckBoxFormField
field='Image Copyright'
name='imageCopyrightChecked'
onIconPress={props.onIconPress}
checked={props.checked}
title='I hereby confirm that the copyrights for this photo are with me or that I have received rights of use from the author. I also confirm that the privacy rights of depicted third persons are not violated.'
/>
</>
}
</View>
);
};
18 changes: 18 additions & 0 deletions components/imageFormField.styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,23 @@ export const styles = StyleSheet.create({
},
textDark: {
color: COLORS.text.main,
},
imageButtonIcon: {
marginHorizontal: SPACINGS.multiplyBy(0.25),
fontSize: SIZES.md
},
imageButton: {
minWidth: '100%',
color: COLORS.text.inverted,
backgroundColor: COLORS.text.main,
paddingVertical: SPACINGS.multiplyBy(0.75),
borderColor: COLORS.text.main,
borderRadius: BORDERRADIUS.none
},
imageInfo: {
marginBottom: SPACINGS.multiplyBy(1)
},
textLight: {
color: COLORS.text.inverted
}
});
15 changes: 0 additions & 15 deletions containers/Ideas/Idea.styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,9 @@ export const styles = StyleSheet.create({
marginVertical: SPACINGS.multiplyBy(.5),
flexWrap: 'wrap',
},
imageButton: {
minWidth: '100%',
color: COLORS.text.inverted,
backgroundColor: COLORS.text.main,
paddingVertical: SPACINGS.multiplyBy(.75),
borderColor: COLORS.text.main,
borderRadius: BORDERRADIUS.none,
},
imageInfo: {
marginBottom: SPACINGS.multiplyBy(1),
},
textLight: {
color: COLORS.text.inverted,
},
iconButton: {
marginHorizontal: SPACINGS.multiplyBy(.25),
fontSize: SIZES.md
},
commentIcon: {
fontSize: SIZES.md,
marginVertical: SPACINGS.multiplyBy(.5),
Expand Down
Loading

0 comments on commit 5cf5745

Please sign in to comment.