Unable to clear AbortController.abort() after called - react-native

Good afternoon folks,
Here's an issue that's currently driving me up the wall and I cannot seem to see the wood for the trees.
I have a React Native app with a built in file uploader, part of this uploader is the use of the abortcontroller which allows the code to send a signal to the fetch request to stop the call in-flight
This works perfectly as one would expect the problem is if the user then selects another file or tries to upload the file the cancelled previously my promise returns instantly with the abort error still in place preventing any further uploads and for love nor money can I seem to find a way to stop this from occurring.
Here is the reduced version of my screen (urls removed, some data points removed, etc) to protect the privacy of my system
import React from 'react';
import {StyleSheet,View,ScrollView,Alert,} from 'react-native';
import AppSettings from '../../constants/AppSettings'
import Colours from '../../constants/Colours';
import CustomHeader from '../../components/CustomHeader';
import CustomButton from '../../components/CustomButton';
import TextContent from '../../components/TextContent';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import Constants from 'expo-constants';
import * as DocumentPicker from 'expo-document-picker';
import * as Permissions from 'expo-permissions';
import CustomInput from '../../components/CustomInput';
import Progressbar from '../../components/ProgressBar';
import * as Network from 'expo-network';
export default class UploadScreen extends React.Component {
state = {
file: null,
filetype: null,
fileExtention:null,
uploading: false,
pickResult: null,
mainDataLoaded: false,
docDesc:null,
enteredPassword:'',
currentUploadPercent:0,
uploadTime: 0,
startPick: false,
};
render() {
return (
<View style={styles.screenContainer}>
<LinearGradient start={[0, 1]} end={[0,0.9]} colors={['rgba(163, 163, 163,1)', 'rgba(163, 163, 163,0)']} style={{flex:1}} >
<ScrollView style={styles.scrollContainer} contentContainerStyle={styles.defaultContainer}>
<View style={{flex:1, alignItems: 'center', justifyContent: 'center', width:'100%' }}>
<TextContent>Upload file containing: {this.state.docDesc}</TextContent>
{this._maybeRenderFile()}
{this._maybeRenderControls()}
{this._maybeRenderUploadingIndicator()}
</View>
</ScrollView>
</LinearGradient>
</View>
);
}
_maybeRenderUploadingIndicator = () => {
if (this.state.uploading) {
return (
<View style={{width:'80%',alignItems:'center'}}>
<Progressbar progress={this.state.currentUploadPercent}/>
<CustomButton style={{width:'100%'}} onPress={()=>{AbortUpload(this)}} title='Cancel Upload'></CustomButton>
</View>
);
}
};
_maybeRenderControls = () => {
if (!this.state.uploading) {
return (
<View style={{width:'100%',alignItems:'center'}}>
<CustomButton style={{width:'80%'}} onPress={this._pickImage} title='Select file to upload'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="folder-open" size={30}/></CustomButton>
</View>
);
}
};
_maybeRenderFile = () => {
if (this.state.file) {
switch (this.state.filetype) {
case 'application/pdf':
const passwordHandler = enteredText => {
this.setState({enteredPassword: enteredText});
};
return (
<View style={{alignItems:'center'}}>
<MaterialCommunityIcons style={{color:Colours.PrimaryText}} name="file-pdf" size={100}/>
<TextContent style={{textAlign:'center'}}>File to upload: {this.state.file}</TextContent>
<TextContent>If this file requires a password to access please type it below or leave blank if not required.</TextContent>
{!this.state.uploading && (
<View>
<CustomInput placeholder='PDF Password (if applicable)' autoCapitalize='characters' autoCompleteType='off' autoCorrect={false} textContentType='none' onChangeText={passwordHandler} value={this.state.enteredPassword}/>
<CustomButton style={{width:'100%'}} onPress={()=>{this._handleImagePicked(this.state.pickResult)}} title='Upload this file'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="file-upload-outline" size={30}/></CustomButton>
<TextContent style={{textAlign:'center'}}>Or</TextContent>
</View>
)}
</View>
);
break;
case 'image/jpg':
case 'image/png':
case 'image/gif':
return (
<View style={{alignItems:'center'}}>
<MaterialCommunityIcons style={{color:Colours.PrimaryText}} name="file-image" size={100}/>
<TextContent style={{textAlign:'center'}}>File to upload: {this.state.file}</TextContent>
{!this.state.uploading && (
<View>
<CustomButton style={{minWidth:'80%'}} onPress={()=>{this._handleImagePicked(this.state.pickResult)}} title='Upload this file'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="file-upload-outline" size={30}/></CustomButton>
<TextContent style={{textAlign:'center'}}>Or</TextContent>
</View>
)}
</View>
);
break;
default:
break;
}
}
};
_askPermission = async (type, failureMessage) => {
const { status, permissions } = await Permissions.askAsync(type);
if (status === 'denied') {
alert(failureMessage);
}
};
_pickImage = async () => {
await this._askPermission(
Permissions.CAMERA_ROLL,
'We need the file permission to access files from your phone...'
);
if(!this.state.startPick){
this.setState({startPick: true})
let pickerResult = await DocumentPicker.getDocumentAsync({});
if(pickerResult.type == 'success'){
this.setState({startPick: false})
//Get file extention
var splitAt = pickerResult.name.lastIndexOf(".")
var fileExt = pickerResult.name.slice(splitAt,pickerResult.name.length).toLowerCase()
switch (fileExt) {
case '.pdf':
this.setState({file: pickerResult.name, filetype: 'application/pdf', pickResult: pickerResult, fileExtention: fileExt})
break;
case '.jpg':
this.setState({file: pickerResult.name, filetype: 'image/jpg', pickResult: pickerResult, fileExtention: fileExt})
break;
case '.jpeg':
this.setState({file: pickerResult.name, filetype: 'image/jpg', pickResult: pickerResult, fileExtention: fileExt})
break;
case '.png':
this.setState({file: pickerResult.name, filetype: 'image/png', pickResult: pickerResult, fileExtention: fileExt})
break;
case '.gif':
this.setState({file: pickerResult.name, filetype: 'image/gif', pickResult: pickerResult, fileExtention: fileExt})
break;
default:
this.setState({file: null, filetype: null, pickResult: null})
Alert.alert('Unsupported filetype','For security reasons you may only select images or PDF files to upload.')
break;
}
}else{
//No file selected
this.setState({file: null, filetype: null, pickResult: null})
this.setState({startPick: false})
}
if(__DEV__){console.log('Result:', pickerResult)}
}else{
if(__DEV__){console.log('Pick already started')}
}
};
_StatusCheck = async() =>{
return fetch('Url for server side upload status response')
.then((response) => response.json())
.then((responseJson) => {
return responseJson
})
.catch((error) =>{
console.error(error);
});
}
_handleImagePicked = async pickerResult => {
try {
if (!pickerResult.cancelled) {
var thisTime = Date.now()
this.setState({uploadTime: thisTime})
var myPromise = new Promise(function(){})
myPromise = MakeQuerablePromise(new uploadFileAsync(
pickerResult.uri,
this.state.docType + '-' + Date.now() + this.state.fileExtention,
this.state.filetype,
this.state.docType,
this.state.docDesc,
this.state.enteredPassword,
this.state.quoteID,
this.state.uploading,
thisTime,
controller.signal
));
this.setState({ uploading: true });
if(__DEV__){
console.log("Initial fulfilled:", myPromise.isFulfilled());//false
console.log("Initial rejected:", myPromise.isRejected());//false
console.log("Initial pending:", myPromise.isPending());//true
}
for (let index = 0; index < 1;) {
var currentStatus = await this._StatusCheck()
var curTime = new Date()
if(__DEV__){
console.log('Time:',curTime.getHours(),':',curTime.getMinutes(),':',curTime.getSeconds())
console.log('Status:',currentStatus)
console.log("Promise status- fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
console.log('Promise Content:',myPromise)
}
if(!myPromise.isRejected()){
if(currentStatus.percent != undefined){
if(currentStatus.percent < 100){
this.setState({currentUploadPercent:currentStatus.percent})
if(__DEV__){
console.log('Upload progess ' + currentStatus.percent)
console.log("Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
}
}else{
this.setState({currentUploadPercent:currentStatus.percent})
if(__DEV__){
console.log('Upload progess 100%')
console.log("Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
}
}
}
}
if(myPromise.isFulfilled() == true){
if(__DEV__){
console.log("Entered Fulfilled State - Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
}
index++
}
if(myPromise.isRejected() == true){
if(__DEV__){
console.log("Entered Rejected State - Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
}
index++
}
}
if(myPromise.isRejected() == false){
myPromise.then(response => response.json()).then((responseJson)=>{
if(__DEV__){
console.log('Promise Json:',responseJson)
console.log("Final fulfilled:", myPromise.isFulfilled());//true
console.log("Final rejected:", myPromise.isRejected());//false
console.log("Final pending:", myPromise.isPending());//false
}
if(responseJson.datapage.result.gitUploadStatus.successful == true){
//Successful upload
this.props.navigation.navigate('CaptureThanks')
}else{
//Upload had a issue
Alert.alert('Upload error','There was an issue with the upload, this maybe a temporary issue with your connection or with the server please ensure you have a good steady signal and try again, if the problem persists please contact us. Error Code: '+responseJson.datapage.gitUploadStatus.errorMessage)
}
})
}else{
//Rejected promise handle failure
if(myPromise.rejectReason() == 'AbortError'){
myPromise = MakeQuerablePromise(new Promise(function(resolve, reject){
resolve('AbortError')
}))
}else{
Alert.alert('Upload error','There was an issue with the upload, this maybe a temporary issue with your connection or with the server please ensure you have a good steady signal and try again, if the problem persists please contact us. Error Code: '+responseJson.datapage.gitUploadStatus.errorMessage)
}
}
}
} catch (e) {
if(__DEV__){
console.log('Error Name:',e.name)
console.log('Catch Error:',{ e });
}
return;
} finally {
if(__DEV__){
console.log('Reached Final')
}
myPromise = MakeQuerablePromise(new Promise(function(resolve, reject){
resolve('Finished')
}))
console.log(myPromise)
this.setState({ uploading: false, currentUploadPercent:0 });
}
};
}
function MakeQuerablePromise(promise) {
// Don't modify any promise that has been already modified.
if (promise.isResolved){
return promise
};
// Set initial state
var isPending = true;
var isRejected = false;
var isFulfilled = false;
var rejectReason = '';
// Observe the promise, saving the fulfillment in a closure scope.
var result = promise.then(
function(v) {
isFulfilled = true;
isPending = false;
rejectReason = '';
return v;
},
function(e) {
isRejected = true;
isPending = false;
rejectReason = e.name;
return e;
}
);
result.isFulfilled = function() { return isFulfilled; };
result.isPending = function() { return isPending; };
result.isRejected = function() { return isRejected; };
result.rejectReason = function() {return rejectReason; };
return result;
}
const controller = new AbortController;
async function uploadFileAsync(uri,name,type,docType,docDesc,password,quoteid,isUploading,uploadTime,abortSignal) {
if(!isUploading){
if(__DEV__){console.log('Making upload request for ',docType,' Document description:', docDesc)}
let apiUrl = 'Url to push the upload to';
let formData = new FormData();
//(method) FormData.append(name: string, value: string | Blob, fileName?: string): void
formData.append('filename', {
uri,
name: name,
type: type,
documentType:docType,
description:docDesc,
password:password,
quoteid:quoteid,
});
let options = {
method: 'POST',
body: formData,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
signal: abortSignal,
};
if(__DEV__){console.log('Options:', options)}
return fetch(apiUrl, options);
}else{
return null
}
}
async function AbortUpload(stateObject){
controller.abort()
stateObject.setState({isUploading: false})
}
UploadScreen.navigationOptions = {
header: () => <CustomHeader goback={true} title='Document Upload'/>,
title: AppSettings.AppName + ' ',
headerTitleStyle:{
fontFamily: AppSettings.HeaderFont,
},
headerStyle: {
backgroundColor: Colours.HeaderBackground
},
headerTintColor: Colours.HeaderText
};
const styles = StyleSheet.create({
screenContainer:{
flex:1,
backgroundColor: Colours.PrimaryBackgroud,
},
scrollContainer:{
flex: 1,
height:'100%'
},
defaultContainer: {
alignItems: 'center',
},
});
Forgive the nature of my code as I'm still pretty new to react native and only been doing it for a few months now so still getting my head around a lot of the features and systems and only just updated to the latest version of expo (36) yesterday so I could enable the fetch abort.
But it anyone has any clues as to why it seems that after the signal is called once to abort every future request then seems to be getting that same signal regardless that the user has not clicked it again and I am not storing it in state so I can't seem to understand why its persisting to the degree that I even went to the extreme of rebuilding the promises at start and end to ensure they were cleaned every time the upload is fired.

You shared way too much code, but I'll have a try at this.
First of all, I'm surprised it even worked in the first place. You're creating your abort controller like so:
const controller = new AbortController;
While it should be:
const controller = new AbortController();
Now, when it comes to the logic, I believe it's displaying an error because you're using that same controller, which was already canceled.
The trick would be to turn it into a state and update it with a new AbortController as soon as it gets aborted (probably in your AbortUpload method, right after calling controller.abort()).

Related

Cant receive data from postMessage in react native webview

here is my injected js:
window.ReactNativeWebView.postMessage(JSON.stringify({
external_url_open: null,
height: Math.max(document.body.offsetHeight, document.body.scrollHeight, document.documentElement.clientHeight)
}));
(function(){
var attachEvent = function(elem, event, callback)
{
...
}
var all_links = document.querySelectorAll('a[href]');
if ( all_links ) {
for ( var i in all_links ) {
if ( all_links.hasOwnProperty(i) ) {
attachEvent(all_links[i], 'onclick', function(e){
if ( new RegExp( '^https?:\/\/www.dropbox\.com.*', 'gi' ).test( this.href ) ) {
// handle external URL
e.preventDefault();
window.ReactNativeWebView.postMessage(JSON.stringify({
external_url_open: this.href,
}));
}
else { window.location.href = this.href; }
});
}
}
}
})();
I want to receive the pages height on react native side with this code:
<WebView
...
javascriptEnabled={true}
onMessage={(event) => {
// retrieve event data
var data = event.nativeEvent.data; // maybe parse stringified JSON
try {
data = JSON.parse(data)
} catch ( e ) { }
// check if this message concerns us
if ( 'object' == typeof data && data.external_url_open ) {
if (data.external_url_open != null){
// proceed with URL open request
Linking.openURL(data.external_url_open);
}
else {
height = data.height;
console.log(data.page_height);
}
}
}}
injectedJavaScript={jsCode}
...
/>
the link handling part works but the height receive part doesn't and I dont see anything in console when refreshing the page. Can anyone help?
I just needed to relocate this else to the outer if:
else {
height = data.height;
console.log(data.page_height);
}

How to change the width of NormalPeoplePicker dropdown

I'm using default example of NormalPeoplePicker from https://developer.microsoft.com/en-us/fluentui#/controls/web/peoplepicker#IPeoplePickerProps.
When the dropdown displays it cuts off longer items (example: 'Anny Lundqvist, Junior Manager of Soft..'). How do I make it wider, so that the full item's text displays?
import * as React from 'react';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
import { IPersonaProps } from 'office-ui-fabric-react/lib/Persona';
import { IBasePickerSuggestionsProps, NormalPeoplePicker, ValidationState } from 'office-ui-fabric-react/lib/Pickers';
import { people, mru } from '#uifabric/example-data';
const suggestionProps: IBasePickerSuggestionsProps = {
suggestionsHeaderText: 'Suggested People',
mostRecentlyUsedHeaderText: 'Suggested Contacts',
noResultsFoundText: 'No results found',
loadingText: 'Loading',
showRemoveButtons: true,
suggestionsAvailableAlertText: 'People Picker Suggestions available',
suggestionsContainerAriaLabel: 'Suggested contacts',
};
const checkboxStyles = {
root: {
marginTop: 10,
},
};
export const PeoplePickerNormalExample: React.FunctionComponent = () => {
const [delayResults, setDelayResults] = React.useState(false);
const [isPickerDisabled, setIsPickerDisabled] = React.useState(false);
const [mostRecentlyUsed, setMostRecentlyUsed] = React.useState<IPersonaProps[]>(mru);
const [peopleList, setPeopleList] = React.useState<IPersonaProps[]>(people);
const picker = React.useRef(null);
const onFilterChanged = (
filterText: string,
currentPersonas: IPersonaProps[],
limitResults?: number,
): IPersonaProps[] | Promise<IPersonaProps[]> => {
if (filterText) {
let filteredPersonas: IPersonaProps[] = filterPersonasByText(filterText);
filteredPersonas = removeDuplicates(filteredPersonas, currentPersonas);
filteredPersonas = limitResults ? filteredPersonas.slice(0, limitResults) : filteredPersonas;
return filterPromise(filteredPersonas);
} else {
return [];
}
};
const filterPersonasByText = (filterText: string): IPersonaProps[] => {
return peopleList.filter(item => doesTextStartWith(item.text as string, filterText));
};
const filterPromise = (personasToReturn: IPersonaProps[]): IPersonaProps[] | Promise<IPersonaProps[]> => {
if (delayResults) {
return convertResultsToPromise(personasToReturn);
} else {
return personasToReturn;
}
};
const returnMostRecentlyUsed = (currentPersonas: IPersonaProps[]): IPersonaProps[] | Promise<IPersonaProps[]> => {
return filterPromise(removeDuplicates(mostRecentlyUsed, currentPersonas));
};
const onRemoveSuggestion = (item: IPersonaProps): void => {
const indexPeopleList: number = peopleList.indexOf(item);
const indexMostRecentlyUsed: number = mostRecentlyUsed.indexOf(item);
if (indexPeopleList >= 0) {
const newPeople: IPersonaProps[] = peopleList
.slice(0, indexPeopleList)
.concat(peopleList.slice(indexPeopleList + 1));
setPeopleList(newPeople);
}
if (indexMostRecentlyUsed >= 0) {
const newSuggestedPeople: IPersonaProps[] = mostRecentlyUsed
.slice(0, indexMostRecentlyUsed)
.concat(mostRecentlyUsed.slice(indexMostRecentlyUsed + 1));
setMostRecentlyUsed(newSuggestedPeople);
}
};
const onDisabledButtonClick = (): void => {
setIsPickerDisabled(!isPickerDisabled);
};
const onToggleDelayResultsChange = (): void => {
setDelayResults(!delayResults);
};
return (
<div>
<NormalPeoplePicker
// eslint-disable-next-line react/jsx-no-bind
onResolveSuggestions={onFilterChanged}
// eslint-disable-next-line react/jsx-no-bind
onEmptyInputFocus={returnMostRecentlyUsed}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={suggestionProps}
className={'ms-PeoplePicker'}
key={'normal'}
// eslint-disable-next-line react/jsx-no-bind
onRemoveSuggestion={onRemoveSuggestion}
onValidateInput={validateInput}
removeButtonAriaLabel={'Remove'}
inputProps={{
onBlur: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onBlur called'),
onFocus: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onFocus called'),
'aria-label': 'People Picker',
}}
componentRef={picker}
onInputChange={onInputChange}
resolveDelay={300}
disabled={isPickerDisabled}
/>
<Checkbox
label="Disable People Picker"
checked={isPickerDisabled}
// eslint-disable-next-line react/jsx-no-bind
onChange={onDisabledButtonClick}
styles={checkboxStyles}
/>
<Checkbox
label="Delay Suggestion Results"
defaultChecked={delayResults}
// eslint-disable-next-line react/jsx-no-bind
onChange={onToggleDelayResultsChange}
styles={checkboxStyles}
/>
</div>
);
};
function doesTextStartWith(text: string, filterText: string): boolean {
return text.toLowerCase().indexOf(filterText.toLowerCase()) === 0;
}
function removeDuplicates(personas: IPersonaProps[], possibleDupes: IPersonaProps[]) {
return personas.filter(persona => !listContainsPersona(persona, possibleDupes));
}
function listContainsPersona(persona: IPersonaProps, personas: IPersonaProps[]) {
if (!personas || !personas.length || personas.length === 0) {
return false;
}
return personas.filter(item => item.text === persona.text).length > 0;
}
function convertResultsToPromise(results: IPersonaProps[]): Promise<IPersonaProps[]> {
return new Promise<IPersonaProps[]>((resolve, reject) => setTimeout(() => resolve(results), 2000));
}
function getTextFromItem(persona: IPersonaProps): string {
return persona.text as string;
}
function validateInput(input: string): ValidationState {
if (input.indexOf('#') !== -1) {
return ValidationState.valid;
} else if (input.length > 1) {
return ValidationState.warning;
} else {
return ValidationState.invalid;
}
}
/**
* Takes in the picker input and modifies it in whichever way
* the caller wants, i.e. parsing entries copied from Outlook (sample
* input: "Aaron Reid <aaron>").
*
* #param input The text entered into the picker.
*/
function onInputChange(input: string): string {
const outlookRegEx = /<.*>/g;
const emailAddress = outlookRegEx.exec(input);
if (emailAddress && emailAddress[0]) {
return emailAddress[0].substring(1, emailAddress[0].length - 1);
}
return input;
}
Component which renders suggestion list have fixed width of 180px. Take a look at PeoplePickerItemSuggestion.styles.ts.
What you can do is to modify this class .ms-PeoplePicker-Persona:
.ms-PeoplePicker-Persona {
width: 260px; // Or what ever you want
}
UPDATE - Solution from comments
Change width trough styles property of PeoplePickerItemSuggestion Component
const onRenderSuggestionsItem = (personaProps, suggestionsProps) => (
<PeoplePickerItemSuggestion
personaProps={personaProps}
suggestionsProps={suggestionsProps}
styles={{ personaWrapper: { width: '100%' }}}
/>
);
<NormalPeoplePicker
onRenderSuggestionsItem={onRenderSuggestionsItem}
pickerCalloutProps={{ calloutWidth: 500 }}
...restProps
/>
Working Codepen example
For more information how to customize components read Component Styling.

react-native-numeric-input change event is not triggered

I'm currently working on a react project using multiple libraries including react-native-numeric-input which for some reason have a lot of unexpected behaviours.
Last night I had everything working as it should be but when I can to take another look the numeric input + and - buttons are not responding to any of the clicks, I even added a console.log to the onChange prop to test if the value is not changing or the event is not triggered and i didn't get anything.
this the numeric input:
<NumericInput
onChange={num => {
console.log(num);
this.setState({ updateItem: num}, this.saveChangedConfigs);
console.log('input', this.state);
}}
value={this.state.updateItem}
totalWidth={configValues.inw}
totalHeight={configValues.inh}
initValue={this.state.updateItem}
inputStyle={{ backgroundColor: '#f4f4f4'}}
containerStyle={styles.updateValue}
minValue={1}
/>
these are the two methods that change the state:
saveChangedConfigs = async () => {
try {
this.props.saveConfigs({
stockpile: this.state.updateItem.toString(),
data: this.state.updateData.toString(),
delete: this.state.emptyFiles.toString(),
autoUpdate: this.state.autoUpdate.toString(),
language: this.state.language
})
await this.initConfigs();
} catch (error) {
console.log('save err', error);
}
}
and
initConfigs = async () => {
const configs = await AsyncStorage.multiGet([
'updateStockpile',
'updateData',
'emptyPhone',
'language',
'autoUpdate'
])
console.log("configs", configs)
const confs = {
stockpile: this.props.config.stockpile,
autoUpdate: this.props.config.autoUpdate,
language: this.props.config.language,
data: this.props.config.data,
delete: this.props.config.delete
}
await configs.map(conf => {
switch(conf[0]){
case 'updateStockpile': if(conf[1] !== null) confs.stockpile = conf[1]; break;
case 'updateData': if(conf[1] !== null) confs.data = conf[1]; break;
case 'emptyPhone': if(conf[1] !== null) confs.delete = conf[1]; break;
case 'language': if(conf[1] !== null) confs.language = conf[1]; break;
case 'autoUpdate': if(conf[1] !== null) confs.autoUpdate = conf[1]; break;
}
})
await this.props.initConfigs(confs);
await this.setState({
language: confs.language,
updateItem: Number(confs.stockpile),
updateData: Number(confs.data),
emptyFiles: Number(confs.delete),
autoUpdate: confs.autoUpdate
});
}
this is my initial state:
this.state = {
language: this.props.config.language,
updateItem: Number(this.props.config.stockpile),
updateData: Number(this.props.config.data),
emptyFiles: Number(this.props.config.delete),
autoUpdate: this.props.config.autoUpdate
}
Hope you guys can help me out with this or maybe suggest an alternative if you know about one.

Get size Image and Video with RNFetchBlobStat

I want when pick photo from device if size of photo > 10 MB or video >100 MB it will not pick. So I use RNFetchBlobStat to get size of photo and video.
When I get detail info of photo from device it show size = 1.42MB but when RNFetchBlobStat return size is 3466195 B. (ImagePicker fileSize have same result)
Here is my code
const MAX_SIZE_PHOTO = 10485760 //10*1024*1024
const MAX_SIZE_VIDEO = 104857600//100*1024*1024
getSizeImg = (type, source) => {
RNFetchBlob.fs.stat(source.uri.replace('file:///', '').replace('file://', '').replace('file:/', ''))
.then((stats) => {
console.log("file size", stats.size)
if (type === 'video') {
if (stats.size > MAX_SIZE_VIDEO) {
Alert.alert(null, "This video is too large to be uploaded")
error()
return
}
uploadVideo(source, completeHandler, progressHandler, error, diary_id, token)
} else {
if (stats.size > MAX_SIZE_PHOTO) {
Alert.alert(null, "This image is too large to be uploaded")
error()
return
}
uploadImage(source)
}
})
.catch((err) => {
console.log("getSize", err)
error(err)
})
}
export let choiceImage = (openLibrary, completeHandler, progressHandler, error, closeLibrary, diary_id, token) => {
console.log("choiceImage", diary_id, token)
ImagePicker.showImagePicker(optionsImage, (response) => {
if (response.didCancel) {
closeLibrary();
}
else if (response.error) {
error()
}
else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
// openLibrary(response.uri, 'image');
let source = {
uri: response.uri,
name: response.fileName ? response.fileName : 'image.png',
type: 'image/*',
};
getSizeImg('image', source);
}
});
}
const isIOS = Platform.OS === 'ios';
/**
* FUNCTION CHOICE VIDEO FROM LIBRARY
* #param func
*/
export let choiceVideo = (openLibrary, completeHandler, progressHandler, error, closeLibrary, diary_id, token) => {
ImagePicker.showImagePicker(optionsVideo, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
closeLibrary();
}
else if (response.error) {
error();
}
else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
// openLibrary(isIOS ? response.uri : 'file://' + response.path, 'video');
let source = {
uri: response.uri,
name: response.fileName ? response.fileName : "video.mp4",
type: 'video/*',
};
getSizeImg('video', source);
}
});
}
Why RNFetchBlobStat return size photo unlike size when get detail info photo from device?
How I can fix it?
You're right. tat, it seems it is not meant to return the size of file recursively. fs.stat does not return correct images size. The issue still open under react-native-fetch-blob lib.
Basically, you can do this by checking the files' size recursively but if we're going to do this in native it might take some time. Here is some code line may help you :
How can I calculate the size of a folder?
https://github.com/kfiroo/react-native-cached-image/blob/master/ImageCacheProvider.js

Render the component only after getting data from server

I have to show options menu using conditional check on data coming from the server. Here is my code:
function renderMoreOptionsOnNavbar(groupInfo){
console.log('hey check this out', groupInfo)
if (groupInfo.user_joined) {
var formData = new FormData();
formData.append('user_id', global.currentUser.USER_ID);
formData.append('slug', groupInfo.id);
var adminsIdsArray = []
ajaxPost({
url: 'community',
basePath: 'lumen',
params: formData
}).then(res => {
if(!res.err_code) {
console.log('This is what you need', res)
const communityAdminsArray = res.community_admins
adminsIdsArray = communityAdminsArray.map(function(user) {
return user.user_id
})
console.log('AdminsArray', adminsIdsArray.includes(global.currentUser.USER_ID))
}
})
const menuOptions = adminsIdsArray.includes(global.currentUser.USER_ID) ? ['Switch off notifications', 'Edit group information', 'Quit group'] : ['Switch off notifications', 'Quit group']
const menuHeight = menuOptions.length*40
console.log('options', menuOptions)
return(
<ModalDropdown options={menuOptions} style={styles.dropDownView} dropdownStyle = {styles.dropdownStyle, {height: menuHeight, width: 175}} adjustFrame = {adjustDropDownFrame}
renderSeparator = {renderDropDownSeparator} dropdownTextStyle = {styles.dropDownOptionText}
onSelect = {onSelectingDropDownOption.bind(this, groupInfo)}
showsVerticalScrollIndicator = {false}
>
<View style={styles.dropDownButtonView}>
<Image
source={require('../../Assets/vertical_dots_menu.png')}
resizeMode = {Image.resizeMode.center}
style = {styles.dropDownBaseButton}
/>
</View>
</ModalDropdown>
)
}
else {
return null
}
}
The options in menu loading before data. Can some one tell me, how to pause rendering until data loads.
You should follow this pattern to achieve what you want:
state = {requiredData: null}
fetchDataFunction().then((resp)=>{
...
this.setState({requiredData: resp}) // I assume the response of fetch is what you need
})
render() {
if (this.state.requiredData === null) {
return null // you can use a spinner instead null
} else {
return (
... // your main view
)
}
}