React-admin : Multiple image upload to s3 at once - amazon-s3

I am trying to make a one-form creation page for an entity with an embedded array of other entities (which has another layer of depth). All of the entities and embedded entities are allowed (but not required) to have an image attached. The image input uses a custom api-platform endpoint to upload the image to AWS s3 and then returns a generated URL, which is being copied to according to text field. It works just the way I want when I am creating an entity with a single image field.
My Image Uploader:
(Feel free to criticize me about the way I check if the image has changed - I am happy to learn)
const ImageUploader = () => (
<>
<ImageInput source="imageUpload" label="image">
<ImageField source="imageUpload" title="title"/>
</ImageInput>
<ImageInput source="imageChecker" label="" options={{hidden: true}}>
</ImageInput>
<TextInput source="image" label="Image path"/>
<FormDataConsumer>
{
({formData}) => {
if (typeof formData === 'undefined' || typeof formData.imageUpload === 'undefined') {
return;
}
if (formData.imageChecker === null || formData.imageUpload !== formData.imageChecker) {
formData.imageChecker = formData.imageUpload;
formData.image = getImagePath(formData.imageUpload);
}
}
}
</FormDataConsumer>
</>
);
What is the problem:
The problem is whenever I have multiple ImageUploader fields in a Create field, uploading an image to one of the uploaders copies the effect for all the other uploaders and text inputs. The image itself is also uploaded to all of the image uploaders.
Example of my form with multiple image uploaders:
const FooBarCreate = props => (
<CreateGuesser {...props}>
...
<ImageUploader/>
<ArrayInput fullWidth={true} source="Foo">
<SimpleFormIterator>
...
<ImageUploader/>
<ArrayInput fullWidth={true} label="Bar" source="Bar">
<SimpleFormIterator>
...
<FormDataConsumer>
{
some logic unrelated to images
}
</FormDataConsumer>
</SimpleFormIterator>
</ArrayInput>
</SimpleFormIterator>
</ArrayInput>
</CreateGuesser>
);
My guess is that the program can't recognize which field I am trying to assign the value to, since their source names are all image and I have no idea how to solve this.
What I have tried:
surrounding the ImageUploader with a <SimpleForm> tag instead of empty tag with no luck, only generating save buttons all over the place.

The reason is that your multiple ImageInput share the same source "imageUpload". React-admin relies on source to read and modify form data. So when one ImageInput changes, the others all change along with it.
You gonna need to define different source for inside . The easiest way is to pass a source prefix property from outside.
like this:
<ImageUploader source="image1" />
const ImageUploader = ({source, ...props}) => (
<>
<ImageInput source=`${source}imageUpload` label="image">
<ImageField source="imageUpload" title="title"/>
</ImageInput>
<ImageInput source=`${source}imageChecker` label="" options={{hidden: true}}/>
<TextInput source=`${source}image` label="Image path"/>
<>
)

Related

Populte WYSIWYG editor after react native fetch

I am trying to incorporate this WYSIWYG package into my react native project (0.64.3). I built my project with a managed workflow via Expo (~44.0.0).
The problem I am noticing is that the editor will sometimes render with the text from my database and sometimes render without it.
Here is a snippet of the function that retrieves the information from firebase.
const [note, setNote] = useState("");
const getNote = () => {
const myDoc = doc(db,"/users/" + user.uid + "/Destinations/Trip-" + trip.tripID + '/itinerary/' + date);
getDoc(myDoc)
.then(data => {
setNote(data.data()[date]);
}).catch();
}
The above code and the editor component are nested within a large function
export default function ItineraryScreen({route}) {
// functions
return (
<RichEditor
onChange={newText => {
setNote(newText)
}}
scrollEnabled={false}
ref={text}
initialFocus={false}
placeholder={'What are you planning to do this day?'}
initialContentHTML={note}
/>
)
}
Here is what it should look like with the text rendered (screenshot of simulator):
But this is what I get most of the time (screenshot from physical device):
My assumption is that there is a very slight delay between when the data for the text editor is actually available vs. when the editor is being rendered. I believe my simulator renders correctly because it is able to process the getNote() function faster.
what I have tried is using a setTimeOut function to the display of the parent View but it does not address the issue.
What do you recommend?
I believe I have solved the issue. I needed to parse the response better before assigning a value to note and only show the editor and toolbar once a value was established.
Before firebase gets queried, I assigned a null value to note
const [note, setNote] = useState(null);
Below, I will always assign value to note regardless of the outcome.
if(data.data() !== undefined){
setNote(data.data()[date]);
} else {
setNote("");
}
The last step was to only show the editor once note no longer had a null value.
{
note !== null &&
<RichToolbar
style={{backgroundColor:"white", width:"114%", flex:1, position:"absolute", left:0, zIndex:4, bottom: (toolbarVisible) ? keyboardHeight * 1.11 : 0 , marginBottom:-40, display: toolbarVisible ? "flex" : "none"}}
editor={text}
actions={[ actions.undo, actions.setBold, actions.setItalic, actions.setUnderline,actions.insertLink, actions.insertBulletsList, actions.insertOrderedList, actions.keyboard ]}
iconMap={{ [actions.heading1]: ({tintColor}) => (<Text style={[{color: tintColor}]}>H1</Text>), }}
/>
<RichEditor
disabled={disableEditor}
initialFocus={false}
onChange={ descriptionText => { setNote(descriptionText) }}
scrollEnabled={true}
ref={text}
placeholder={'What are you planning to do?'}
initialContentHTML={note}
/>
}
It is working properly.

Validate source parameter with typescript in react-admin

I was wondering if it was possible (at the moment) to validate what is transmitted in the source property of inputs/fields.
I generate types (typescript) from my graphql introspection schema and I use it when I retrieve the controller so it would be awesome if typescript could validate that the source really exists in my record.
Example :
type Organisation {
id: number;
name: string;
}
const OrganisationShow = (props: ShowProps) => {
const { record, ...controllerProps } = useShowController<Organisation>(props);
return (
<ShowView
record={record}
{...controllerProps}
>
<TextField source="id" />
<TextField source="name" />
<TextField source="foo" /> // Typescript should deny it
</ShowView>
)
}
Is this something possible or an idea ?
Thanks for your help !
No, this is not possible for now. You would have to pass the type to all fields (<TextField<Organization> source="id" />, so I don't think it's very developer-friendly. And for nested source (e.g. author.name), I don't think it's feasible.

Unable to get renderStepIndicator working on react-native-step-indicator

I think I am not understanding the documentation correctly.
The documentation says: that is takes a Function that return a position: Number, or a stepStatus: which takes a String to render custom content inside step
My goal is to render a check mark instead of a number as it is by default.
I tried to return a string of 'test' and it does not work.
<StepIndicator
customStyles={customStyles}
currentPosition={this.state.currentPosition}
stepCount={this.state.stepCount}
renderStepIndicator={() => {
this.renderStepIndicator();
}}
labels={labels}
/>
and this is the function that return a string
renderStepIndicator() {
return 'test';
}
I am not sure what I am missing here. also I want to return an icon of checkmark. I have seen people doin git but I am not sure how if this only takes a string or an integer.
Here this function returns two parameters step position and step status. You can use this function like this,
<StepIndicator
customStyles={customStyles}
currentPosition={this.state.currentPosition}
stepCount={this.state.stepCount}
renderStepIndicator={(stepPosition,stepStatus) => {
this.renderStepIndicator(stepPosition,stepStatus);
}}
labels={labels}
/>
And the render function is like,
renderStepIndicator(stepPosition, stepStatus) {
return <Icon name={"check"} size={20} color={stepStatus === 'finished' ? "green" : "gray"} /> ;
}
This function render check icon. If you step was complete it show green check otherwise gray check.
For more details you can check this example,
https://github.com/24ark/react-native-step-indicator/blob/master/example/src/HorizontalStepIndicator.tsx
The above solution does not work for me so tried some changes and after some time I got this working solution if some are still facing this issue try using this code.
1. Put this code in your StepIndicator props
renderStepIndicator={({ position, stepStatus }) =>
renderStepIndicatorRender(position, stepStatus)
}
2. Create a function to render the icon
const renderStepIndicatorRender = (stepPosition, stepStatus) => {
return stepStatus === "finished" ? (
<Icon name="check" size={12} color="#fff" />
) : (
<Text>{stepPosition}</Text>
);

Add search to react-native firebase list

I have a react-native list populated from firebase. I want to add a search bar on top of the app. Once I start typing, I want that list to only show results related to the search term. Its similar to this: https://www.freecodecamp.org/news/how-to-build-a-react-native-flatlist-with-realtime-searching-ability-81ad100f6699/
Only problem is the way my data is typed after its retrieved from firebase, its impossible to use the tutorial above to retrieve that data.
My html code is:
<Toolbar title="Hello World"/>
<Button
title="Add New"
type = "outline"
onPress={this.addItem.bind(this)}
/>
<ListView
dataSource={this.state.itemDataSource}
renderRow={this.renderRow}
/>
My Firebase retrieval code is as follows:
getItems(itemsRef) {
itemsRef.on('value', (snap) => {
let items = [];
snap.forEach((child) => {
items.push({
title: child.val().DESCRIPTION,
text: child.val().BASE,
_key: child.key
});
});
this.setState({
itemDataSource: this.state.itemDataSource.cloneWithRows(items)
});
});
}
I just don't know how to add a search bar that searches the retrieved list and displays only relevant stuff on the list as someone types a word in.
Pease help. I'm guessing the type of the data is what is causing the problem.
Results should look like this App. I built this on AppSheet:
https://www.appsheet.com/start/ab7f8d5d-1adf-4107-bdd8-f7022a1a81f8

why react native can't require image currently?

<Image source={require(rowData.avatar)} />
error : Unknown name module ‘xxxxxx’
Why can print out the path but can't read pictures?
Try
<Image source={(rowData.avatar)} />
Images cannot use dynamically generated sources. Assuming what you're trying to do is to load an image as part of your package, your code must read:
const avatar = require('./path/to/avatar.jpg');
Only then you can use avatar as your source a follows:
rowData = {
avatar: avatar
}
<Image source={rowData.avatar} />
If you know before hands what images are going to be needed in your app, I suggest that you create an asset file in which you add all your hardcoded require, such as:
// assets.js
return {
avatar1: require('./path/to/file.jpg'),
avatar2: require('./path/to/file.jpg'),
avatar3: require('./path/to/file.jpg'),
}
And then you would construct your rowData as follows:
import avatars from './assets';
rowData = {
avatar: avatars['avatar1']
}
Where you would likely replace avatar1 with a variable containing the key pointing to the avatar you're interested in.
Here is an asset file I used for one of my projects.