How to pause and play video when display - react-native

I am trying to pause and play video using scrollView but i am unable to do that I wrote a code but it have some condition issue, can anybody please tell me what is wrong in my code, I want to play the video when it is visible and pause all other videos in the list than when we scroll the video show on screen it start playing automatically and the previous video pause or stops automatically.
handleVideoLayout = (e:any) => {
const {height} = Dimensions.get("window");
console.log(e.nativeEvent.layout.y,e.nativeEvent.layout.height);
this.position.start = -(e.nativeEvent.layout.y - height + 100);
this.position.end = e.nativeEvent.layout.y + e.nativeEvent.layout.height + 100;
}
handleScroll = (e:any) => {
const scrollPosition = e.nativeEvent.contentOffset.y;
const paused = this.state.paused;
const {start,end} = this.position;
console.log("========================================");
console.log(start,end,scrollPosition);
console.log("========================================");
if(scrollPosition > start && scrollPosition < end && paused){
this.setState({paused:false});
} else if((scrollPosition > end || scrollPosition < start) && !paused){
this.setState({paused:true});
}
My render function:
<ScrollView
onScroll={(event) => {
this.handleScroll(event)
}}
onLayout={this.handleVideoLayout}
>
{data.map((item,index)=> {
return (
<VideoComponent/>
)
})}
</ScrollView>

I don’t have idea about doing this using scroll view but you have an option in flatlist. In that, you will get the visible items on the screen. and then based on your requirements you can apply conditions.
see my below boilerplate.
const onViewableItemsChanged = ({viewableItems, changed}) => {
//here 'viewableItems' is an item that is currently visible on screen.
};
const viewabilityConfigCallbackPairs = useRef([{onViewableItemsChanged}]);
<FlatList
data={data}
renderItem={renderPost}
showsVerticalScrollIndicator={false}
viewabilityConfigCallbackPairs={
viewabilityConfigCallbackPairs.current
}
/>

find the current index of your list and also the total index's of your list and then put like this
`
const [paused, setPaused] = useState(false);
<Video
videoRef={videoRef}
source={{uri: userData?.Video?.path}}
poster={VideoToImageUrlCoverter(userData?.Video?.path)}
posterResizeMode={'cover'}
pauseOnPress={paused}
shouldPlay={currentIndex === index}
repeat={true}
autoplay
resizeMode="cover"
paused={
currentIndex == index
? paused
: !paused && currentIndex == index
? false
: true
}
/>
`

Related

Undefined is not an object (evaluating 'prevDeps.length') using react-native-skia

I created a donut chart components (DonutChart) that takes an array of values representing percentages. The values are derived from data gathered from a firestore db. When running this:
Warning: React has detected a change in the order of Hooks called by DonutChart. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks
Previous render Next render
-
Previous render Next render
------------------------------------------------------
1. useMemo useMemo
2. useEffect useMemo
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
along with:
TypeError: undefined is not an object (evaluating 'prevDeps.length')
I am assuming the SKIA library uses 'useMemo' for it's animation computations.
Here is some of the code and then I'll explain what I have tried:
DonutChart
const DonutChart = ({ dataValues }: DonutChartProps) => {
const sortedValues = dataValues.sort((n1, n2) => n1 - n2);
const total = sortedValues.reduce((sum, n) => sum + n, 0);
const createRollingTotal = () => {
var rollingTotal = 0;
return sortedValues.map((val, index) => {
rollingTotal = index !== 0 ? rollingTotal + sortedValues[index - 1] : rollingTotal;
return index !== 0 ? total - rollingTotal : total;
});
};
const progress = createRollingTotal();
const progressValues = progress.map(useValue);
React.useEffect(() => {
progress.map((val, index) => animateChart(val, progressValues[index]));
return () => {};
}, []);
const animateChart = (value: number, state: SkiaMutableValue) => {
state.current = 0;
runTiming(state, value, {
duration: 1250,
easing: Easing.inOut(Easing.cubic),
});
};
return (
<Canvas style={{ height: OUTER_RADIUS + 200, width: OUTER_RADIUS + 200 }} mode="continuous">
<DonutChartSkia progress={progressValues} />
</Canvas>
);
};
export default DonutChart;
DonutChartSkia
const fromCircle = (cx: number, cy: number, r: number) =>
rrect(rect(cx - r, cy - r, 2 * r, 2 * r), r, r);
interface ProgressBarProps {
progress: SkiaValue<number>[];
}
interface DonutChartProps {
dataValues: number[];
}
const BLUR = 1;
const SHADOW_WIDTH = 1;
const INNER_RADIUS = 35;
const OUTER_RADIUS = 55;
const PATH_RADIUS = (INNER_RADIUS + OUTER_RADIUS) / 2;
const DonutChartSkia = ({ progress }: ProgressBarProps) => {
const font = useFont(require('../../assets/fonts/SF-Pro-Text-Semibold.otf'), 18);
const text = useComputedValue(() => `${Math.round(progress[0]?.current * 100)}%`, [progress[0]]);
if (font === null) {
return null;
}
const textWidth = font.getTextWidth('00°C');
const path = Skia.Path.Make();
path.addCircle(OUTER_RADIUS, OUTER_RADIUS, PATH_RADIUS);
const c = vec(PATH_RADIUS + 12, PATH_RADIUS + 12);
const renderPath = (val: SkiaValue<number>, index: number) => {
const color = CHART_COLORS[index];
return (
<Group key={index}>
<RadialGradient
colors={[color.secondary, color.primary]}
c={vec(OUTER_RADIUS, OUTER_RADIUS)}
r={70}
/>
<Path
path={path}
style="stroke"
strokeWidth={OUTER_RADIUS - INNER_RADIUS}
end={val}
strokeCap="round"
antiAlias={true}
/>
</Group>
);
};
return (
<Group transform={translate({ x: 70, y: 70 })}>
<Group>
<LinearGradient
start={vec(22, 22)}
end={vec(200, 200)}
colors={[primaryColor, secondaryColor]}
/>
<Box box={fromCircle(OUTER_RADIUS, OUTER_RADIUS, OUTER_RADIUS)}>
<BoxShadow dx={SHADOW_WIDTH} dy={SHADOW_WIDTH} blur={BLUR} color={shadowColor} />
<BoxShadow dx={-SHADOW_WIDTH} dy={-SHADOW_WIDTH} blur={BLUR} color={glareColor} />
</Box>
</Group>
<Group>
<RadialGradient colors={[shadowColor, primaryColor]} c={vec(128, 128)} r={128} />
<Box box={fromCircle(OUTER_RADIUS, OUTER_RADIUS, INNER_RADIUS)}>
<BoxShadow dx={SHADOW_WIDTH} dy={SHADOW_WIDTH} blur={BLUR} color={shadowColor} />
<BoxShadow dx={-SHADOW_WIDTH} dy={-SHADOW_WIDTH} blur={BLUR} color={glareColor} />
</Box>
</Group>
<Text
x={c.x - textWidth / 2}
y={c.y + font.getSize() / Math.PI}
font={font}
text={text}
color={textColor}
/>
<Group>{progress.map((val, index) => renderPath(val, index))}</Group>
</Group>
);
};
I have tried removing the 'useEffect' and instead use 'useMemo'.
Changed how I handle the API call, but still need to use 'useEffect' for that.
Added (and removed) umount functions to the 'useEffect' hook.
I expect the DonutChart to rerender with each data change since it uses values from a redux store that is updated from firestore. I keep running into this error, and I can only assume it is caused by the SKIA library using 'useMemo'. Still, I am not sure and only having less than a year of self-taught React-native dev experience I am not sure if it is just me or some issue I should log on GitHub. I know this is all over the place but I am hoping someone might notice something I might be doing wrong and I might also learn from that.

How to map data which returns when making Api call in react-native

I am making an Api call where I will get the list of address.
const [address_line1, setAddress_line1] = React.useState([]);
I am able to get the result in the console.
Below is the snippet of my code after api is called:
if (result.status === 200 && result.data.status === "success") {
for (let index = 0; index < 3; index++) {
console.log(result.data.data[index].city);
setAddress_line1(result.data.data[index].city);
}
}
CONSOLE OUTPUT:
delhi
Mumbai
Pune
And I am trying to map this output in code :
<ScrollView showsVerticalScrollIndicator={false}>
{address_line1.map((item, index) => (
<TouchableOpacity
index={index}
onPress={() => setIndexSelect(index)}
key={index}
>
<Text>{item}</Text>
</TouchableOpacity>
))}
</ScrollView>;
But I am getting error:
address_line1.map is not a function
I know there is something wrong at the place where I am mapping my output, But I am not able to figure it out. I have been trying this for hours. I need some help!!
Try this way
if (result.status === 200 && result.data.status === "success") {
const data = [];
for (let index = 0; index < 3; index++) {
console.log(result.data.data[index].city);
data.push(result.data.data[index].city);
}
setAddress_line1(data);
}
You can also use reduce or map
useEffect(() => {
if (result.status === 200 && result.data.status === "success") {
const addresses = result.data.data.map(elem=>elem.city)
setAddress_line1(addresses);
}
});
This way you loop all data and return the city

React double execution

I'm working on a infinite scroll on scrollview, to do this I use Refrescontrol and that function:
handleOnScroll(e){
let yOffset = e.nativeEvent.contentOffset.y;
if (this.state.hWall && this.state.hWall - this.state.hScreen - yOffset <= 0 && !this.state.isRefreshing){
this.setState({ isRefreshing : true }, () => this.getPosts('more') );
}
}
Now the problem is that Alway I have two this.getPosts('more') calls, how to prevents double call before the getPosts action are completed?
The problem is that setState is asyncronous. Try using this.isRefresing to control whether there is a refresing ongoing or not.
handleOnScroll(e){
let yOffset = e.nativeEvent.contentOffset.y;
if (this.state.hWall && this.state.hWall - this.state.hScreen - yOffset <= 0 && !this.isRefreshing){
this.isRefresing = true; //this is syncronous
//setState is still required if you want to update your UI
this.setState({ isRefreshing : true }, () => this.getPosts('more') );
}
}
Furthermore, if you trade your scroll view for a flatList, you can use the onEndReached callback to fetch more data when the list is about to end.

render views's order in react native android and iOS?

I have a view in react native witch contains some child views. I need to perform some functions right after each views rendered.
my views are in this order :
Base Parent view ---> parent view ---> child view
so I need to calculate some layout dimention in parent view but exactly after child view is created.
in Android I dont have any problem
bu in iOS these views created in no order so I cant do my calculation.
How can I be sure to run a function exactly after all childs are created?
this is my base parent view :
<View onLayout={this.getGridExactPos} style={{width: '100%' , aspectRatio: 1, backgroundColor: 'red'}} {...this._panResponder.panHandlers}>
{this.constructViews()}
</View>
this is my childs in above container:
constructViews = () => {
let strings = 'a,b,c,d|s,d,f,g|h,b,a,u|m,l,k,o';
let answers = '01,12,23';
let arrayST = strings.split("|");
for(i=0;i<arrayST.length;i++){
arrayST[i] = arrayST[i].split(",");
}
let rows = [];
let currentIndex = 0
let currentRowIndex = 0
for (let i = 1; i <=arrayST.length; i++) {
let row = [];
for (let j = 1; j <=arrayST.length; j++) {
const boxIndex = currentIndex++
let stateKey = `${i}${j}`;
row.push(
<View onLayout={ (e) => this.getExactPos(e, stateKey, boxIndex)} style={[styles.box, {backgroundColor : this.state.boxes[boxIndex].bgColor}]} key={stateKey}><Text>{this.state.boxes[boxIndex].title}</Text></View>
);
}
const rowIndex = currentRowIndex++
rows.push(
<View onLayout={ (e) => this.fixPos(e, rowIndex)} style={styles.row} key={i}>{row}</View>
);
}
return rows;
}
so I need to run 'this.fixPos' exactly after 'this.getExactPos' and finally after this loop finished I need to run 'this.getGridExactPos'.
in android I dont have problem but in iOS there is no order so makes me mistake.
thanks for your advices.

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
)
}
}