Swipe card on button click using panresponder - javascript

I am trying to make a tinder-like app component to swipe cards, it is working fine when I swipe using a finger, however, I want to achieve the same on button click as well. I have used Panresponder so far for creating swipe action, below is my code
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1, justifyContent: 'center', alignContent: 'center', alignItems: 'center', backgroundColor: 'white' }}>
<View style={{
height: Dimensions.get('window').height * .75,
borderRadius: 5,
width: Dimensions.get('window').width * .75,
}}>
{
this.state.ARRAY.map((item, index) => {
this.position = new Animated.ValueXY()
this.rotate = this.position.x.interpolate({
inputRange: [-CONST_WIDTH / 2, 0, CONST_WIDTH / 2],
outputRange: ['-30deg', '0deg', '10deg'],
extrapolate: 'clamp'
})
this.rotateAndTranslate = {
transform: [{
rotate: this.rotate
},
...this.position.getTranslateTransform()
]
}
this.isPanDisabled = true
this.PanResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => this.isPanDisabled,
onPanResponderMove: (evt, gestureState) => {
this.position.setValue({ x: gestureState.dx, y: 0 })
},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dx > 120) {
this.isPanDisabled = true
// console.log('dxgreaterThan120', gestureState.dx)
Animated.spring(this.position, {
toValue: { x: CONST_WIDTH + 100, y: 0 },
useNativeDriver: Platform.OS === 'android'
}).start(() => {
let modified = this.state.ARRAY.filter((x, ind) => { return ind != index })
console.log('modified', modified)
this.setState({ ARRAY: modified })
})
}
else if (gestureState.dx < -120) {
this.isPanDisabled = true
Animated.spring(this.position, {
toValue: { x: -CONST_WIDTH - 150, y: 0 },
useNativeDriver: Platform.OS === 'android'
}).start(() => {
let modified = this.state.ARRAY.filter((x, ind) => { return ind != index })
console.log('modified', modified)
this.setState({ ARRAY: modified })
})
}
else if (gestureState.dy < -120 || gestureState.dy > 120) {
this.isPanDisabled = false
}
else {
this.isPanDisabled = true
Animated.spring(this.position, {
toValue: { x: 0, y: 0 },
friction: 4,
useNativeDriver: Platform.OS === 'android'
}).start()
}
}
})
return (
<Animated.View
key={index}
{...this.PanResponder.panHandlers}
style={[this.rotateAndTranslate, {
borderRadius: 5,
backgroundColor: color[index],
position: 'absolute',
top: 0, bottom: 0,
left: 0, right: 0,
}]}>
<Animated.ScrollView
onScrollEndDrag={() => {
this.isPanDisabled = true
}}>
<View style={{ flex: 1, flexGrow: 1 }}>
{
ARRAY_SUUMB.map((item, index) => {
return (
<Text style={{
padding: 16,
color: 'white'
}}>
Swiper
</Text>
)
})
}
</View>
</Animated.ScrollView>
</Animated.View>
)
})
}
</View>
<View style={{ justifyContent: 'center', flexDirection: 'row', alignContent: 'center', alignItems: 'center' }}>
<TouchableOpacity
onPress={() => this.swipeLeft()}
style={{
justifyContent: 'center',
height: 60,
width: 60,
borderRadius: 60 >> 1,
backgroundColor: 'gray',
alignContent: 'center',
alignItems: 'center'
}}>
<Text>Left</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.swipeRight()}
style={{
justifyContent: 'center',
height: 60, width: 60, borderRadius: 60 >> 1,
backgroundColor: 'gray',
alignContent: 'center', alignItems: 'center'
}}>
<Text>Right</Text>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
)
}
I want to make a function this.swipeRight() which I can swipe the card just like as if I am swiping using finger, I have tried creating a similar function but it just removes the top card instantly and afterwards it stops working
swipeRight = () => {
this.position.setValue({ x: Dimensions.get('screen').width * 1.5, y: 0 }, () => {
this.isPanDisabled = true
Animated.spring(this.position, {
toValue: { x: CONST_WIDTH + 100, y: 0 },
useNativeDriver: Platform.OS === 'android'
}).start()
let modified = this.state.ARRAY.splice(0, -1)
console.log('modified', modified)
this.setState({ ARRAY: modified })
})
}
Any help would be appreciated, I have the panresponder inside a map function

Related

React Native - Flatlist not scrolling in custom dropdown

I have Flatlist that doesn't scroll.
I can't find the problem why its not scrolling.
When I'm trying to scroll, It does nothing. It doesn't give me to scroll past the Produce item.
It looks like this (I have more items after Produce):
This is my code:
const Dropdown: FC<Props> = ({ label, data, onSelect, current, style }) => {
const [dropdownTop, setDropdownTop] = useState(0);
const [visible, setVisible] = useState(false);
const [selected, setSelected] = useState(current);
const DropdownButton = useRef();
const toggleDropdown = (): void => {
visible ? setVisible(false) : openDropdown();
};
const openDropdown = (): void => {
DropdownButton.current.measure((_fx, _fy, _w, h, _px, _py) => {
setDropdownTop(_py + h - 10);
});
setVisible(true);
};
const renderDropdown = (): ReactElement<any, any> => {
return (
<Modal visible={visible} transparent animationType='none'>
<TouchableOpacity
style={styles.overlay}
onPress={() => setVisible(false)}>
<View style={[styles.dropdown, { top: dropdownTop }]}>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
contentContainerStyle={{
flexGrow: 1,
flex: 1,
}}
/>
</View>
</TouchableOpacity>
</Modal>
)
};
const renderItem = ({ item }): ReactElement<any, any> => (
<TouchableOpacity style={styles.item} onPress={() => onItemPress(item)}>
<Text style={{ color: "#616161" }}>{item.label}</Text>
</TouchableOpacity>
)
const onItemPress = (item): void => {
setSelected(item);
onSelect(item);
setVisible(false);
};
return (
<TouchableOpacity
ref={DropdownButton}
style={[styles.button, style]}
activeOpacity={0.6}
onPress={toggleDropdown}>
{renderDropdown()}
<Text style={styles.buttonSecondText}>{label}</Text>
<Text style={styles.buttonText}>{(selected && selected.label) || null}</Text>
</TouchableOpacity>
)
}
export default Dropdown
const styles = StyleSheet.create({
item: {
paddingHorizontal: 10,
paddingVertical: 10,
// borderBottomWidth: 1,
borderWidth: 1,
},
button: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFF',
elevation: 10,
width: '100%',
paddingHorizontal: 13,
paddingVertical: 13,
borderRadius: 10,
zIndex: 1,
},
buttonSecondText: {
flex: 2,
color: '#000',
textAlign: 'left',
},
buttonText: {
flex: 2,
textAlign: 'right',
color: '#000',
},
dropdown: {
position: 'absolute',
backgroundColor: '#FFF',
paddingHorizontal: 20,
width: '100%',
shadowColor: '#000000',
shadowRadius: 4,
shadowOffset: { height: 4, width: 0 },
shadowOpacity: 0.5,
},
});
I can't understand where is my problem and why its not scrolling.
Maybe its because its inside a Modal? I'm first time trying to use Modal in React Native

Setting height in animated search bar react native

I have built an animated search bar in react native just like search bar of facebook mobile application. The search bar opens up when the search button is clicked on the header. It currently takes same height as the custom header that it is being rendered on. But i want to increase the height of the header to half the screen size when the search bar is on focus and set the height to default when the search bar is not focused. I have tried using my own logic but the problem was when i increased the height the contents of headers were shown below the search bar. And i dont want that. Can anyone help me how to resolve this?
/* eslint-disable #typescript-eslint/no-unused-vars */
import React, {useContext, useRef} from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Image,
SafeAreaView,
Animated,
TextInput,
Easing,
} from 'react-native';
import Logo from '#assets/images/NCLogo.png';
import Colors from '#constants/colors/colors';
import Search from '#assets/icons/Search.svg';
import Back from '#assets/icons/back-arrow.svg';
import {AuthContext} from '#components/ContextStore/AuthContext/AuthContext';
import DateAndDayGenerator from '#utils/DayGen';
import {HEIGHT, WIDTH} from '#utils/Dimensions';
import Metrics from '#constants/metrics/Metrics';
import Fonts from '#constants/fonts/fonts';
import SearchList from '#components/ListNews/SearchList';
interface Props {
news?: any;
}
const Header: React.FC<Props> = () => {
const date = new Date();
const dateAndDay = DateAndDayGenerator(date);
const {color} = useContext(AuthContext);
const [focused, setFocused] = React.useState(false);
const [value, setValue] = React.useState('');
const inputRef = useRef<TextInput>(null);
// animation initial value
const inputTranslateX = useRef(new Animated.Value(WIDTH)).current;
const backButtonOpacity = useRef(new Animated.Value(0)).current;
const contentTranslateY = useRef(new Animated.Value(HEIGHT)).current;
const contentOpacity = useRef(new Animated.Value(0)).current;
const handleClear = () => {
setValue('');
};
const handleBlur = () => {
setFocused(false);
const inputTranslateXConfig = {
toValue: WIDTH,
duration: 200,
useNativeDriver: true,
easing: Easing.inOut(Easing.ease),
};
const backButtonOpacityConfig = {
toValue: 0,
duration: 50,
useNativeDriver: true,
easing: Easing.inOut(Easing.ease),
};
const contentTranslateYConfig = {
toValue: HEIGHT,
duration: 0,
useNativeDriver: true,
easing: Easing.inOut(Easing.ease),
};
const contentOpacityConfig = {
toValue: 0,
duration: 200,
useNativeDriver: true,
easing: Easing.inOut(Easing.ease),
};
Animated.parallel([
Animated.timing(inputTranslateX, inputTranslateXConfig),
Animated.timing(backButtonOpacity, backButtonOpacityConfig),
Animated.timing(contentTranslateY, contentTranslateYConfig),
Animated.timing(contentOpacity, contentOpacityConfig),
]).start();
inputRef.current?.blur();
};
const handleFocus = () => {
setFocused(true);
const inputTranslateXConfig = {
toValue: 0,
duration: 200,
useNativeDriver: true,
easing: Easing.inOut(Easing.ease),
};
const backButtonOpacityConfig = {
toValue: 1,
duration: 200,
useNativeDriver: true,
easing: Easing.inOut(Easing.ease),
};
const contentTranslateYConfig = {
toValue: 0,
duration: 0,
useNativeDriver: true,
easing: Easing.inOut(Easing.ease),
};
const contentOpacityConfig = {
toValue: 1,
duration: 200,
useNativeDriver: true,
easing: Easing.inOut(Easing.ease),
};
Animated.parallel([
Animated.timing(inputTranslateX, inputTranslateXConfig),
Animated.timing(backButtonOpacity, backButtonOpacityConfig),
Animated.timing(contentTranslateY, contentTranslateYConfig),
Animated.timing(contentOpacity, contentOpacityConfig),
]).start();
inputRef.current?.focus();
};
return (
<>
<SafeAreaView style={styles.header_safe}>
<View style={styles.header}>
<View style={styles.view}>
<View style={styles.Logo}>
<Image source={Logo} style={styles.image} />
</View>
{color ? (
<View style={styles.texts}>
<Text style={[styles.text, {color}]}>Nepali Congress</Text>
<Text style={styles.date}>{`${dateAndDay.day}, ${
dateAndDay.month
} ${dateAndDay.date},${' '}${dateAndDay.year}`}</Text>
</View>
) : (
<View style={styles.texts}>
<Text style={styles.text}>Nepali Congress</Text>
<Text style={styles.date}>{`${dateAndDay.day}, ${
dateAndDay.month
} ${dateAndDay.date},${' '}${dateAndDay.year}`}</Text>
</View>
)}
<TouchableOpacity onPress={handleFocus}>
<View style={styles.search}>
<Search width={25} height={25} fill="none" />
</View>
</TouchableOpacity>
<Animated.View
style={[
styles.input,
{transform: [{translateX: inputTranslateX}]},
]}>
<Animated.View style={{opacity: backButtonOpacity}}>
<TouchableOpacity
activeOpacity={1}
style={styles.back_icon}
onPress={handleBlur}>
<Back width={28} height={28} fill="#000" />
</TouchableOpacity>
</Animated.View>
<TextInput
ref={inputRef}
placeholder="Enter title, description or date of news"
placeholderTextColor="#000"
clearButtonMode="while-editing"
onFocus={handleFocus}
value={value}
onChangeText={text => {
setValue(text);
}}
style={styles.input_text}
/>
<TouchableOpacity
onPress={handleClear}
style={styles.clearButton}>
<Text style={styles.clear}>X</Text>
</TouchableOpacity>
</Animated.View>
</View>
</View>
</SafeAreaView>
<Animated.View
style={[
styles.content,
{
opacity: contentOpacity,
transform: [{translateY: contentTranslateY}],
},
]}>
<SafeAreaView style={styles.content_safe}>
<View style={styles.content_inner}>
<View style={styles.separator} />
<SearchList searchPhrase={value} />
</View>
</SafeAreaView>
</Animated.View>
</>
);
};
const styles = StyleSheet.create({
header_safe: {
zIndex: 1000,
},
header: {
height: HEIGHT * 0.087,
paddingHorizontal: 16,
backgroundColor: Colors.white,
},
view: {
flex: 1,
overflow: 'hidden',
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
position: 'relative',
},
Logo: {
margin: 10,
alignItems: 'center',
flexDirection: 'row',
width: WIDTH * 0.15,
height: HEIGHT * 0.055,
},
image: {
width: '100%',
height: '100%',
resizeMode: 'stretch',
},
texts: {
marginLeft: 4,
marginRight: WIDTH * 0.155,
},
text: {
fontSize: Metrics.h3,
color: Colors.black,
fontFamily: Fonts.type.montBold,
width: WIDTH * 0.5,
},
date: {
marginTop: 4,
fontSize: Metrics.body7,
color: Colors.black,
fontFamily: 'Mont-Regular',
},
search: {
width: 35,
height: 35,
right: 20,
borderRadius: 80,
backgroundColor: '#f5f5f5',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
input: {
height: HEIGHT * 0.087,
flexDirection: 'row',
alignItems: 'center',
position: 'absolute',
top: 0,
left: 0,
backgroundColor: Colors.white,
width: WIDTH - 32,
},
back_icon: {
width: 40,
height: 40,
borderRadius: 40,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginRight: 5,
transform: [{rotate: '180deg'}],
},
input_text: {
flex: 1,
height: 37,
backgroundColor: '#e4e6e8',
borderRadius: 80,
paddingHorizontal: 16,
fontSize: 13,
color: Colors.black,
},
input_text1: {
flex: 1,
height: 37,
backgroundColor: '#e4e6e8',
borderRadius: 80,
paddingHorizontal: 16,
fontSize: 13,
},
content: {
position: 'absolute',
width: WIDTH,
height: HEIGHT,
left: 0,
bottom: 0,
zIndex: 999,
},
content_safe: {
flex: 1,
backgroundColor: Colors.white,
},
content_inner: {
flex: 1,
paddingTop: HEIGHT * 0.087,
},
separator: {
height: 1,
marginTop: 5,
backgroundColor: '#e6e4eb',
},
search_results: {
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#e6e4eb',
marginLeft: 16,
},
clearButton: {
width: 30,
height: 30,
borderRadius: 80,
marginLeft: 10,
backgroundColor: Colors.red,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
clear: {
fontSize: 14,
color: Colors.black,
fontWeight: 'bold',
},
});
export default Header;

Custom React Native list component is not handling pagination correctly

I have a custom list component called TableList. When a user clicks on an arrow it should increment to the next data set. However, when the first click in either direction is made, the component only updates the page number, not the data.
For example: when on page one, clicking next increments to page two, but the data remains the same. Clicking again, increments both the page number and the data. It will continue to paginate correctly, until the user starts clicking previous. Then it will give the same behavior as before for the first click, and continue normally for subsequent clicks.
The primary pagination function:
function changePage(direction) {
if (
(currentPage === 0 && direction === 'previous') ||
currentPage * pageSize > data
) {
return null;
}
if (direction === 'previous' && currentPage > 0) {
const newPage = currentPage - 1;
setDataSlice(data.slice(newPage * pageSize, currentPage * pageSize));
setCurrentPage(newPage);
}
if (direction === 'next') {
const newPage = currentPage + 1;
setDataSlice(data.slice(currentPage * pageSize, newPage * pageSize));
setCurrentPage(newPage);
}
}
Full code:
import {faArrowRight, faArrowLeft} from '#fortawesome/free-solid-svg-icons';
import {FontAwesomeIcon} from '#fortawesome/react-native-fontawesome';
import {func, number, string, shape} from 'prop-types';
import React, {useState} from 'react';
import {View, Text, FlatList} from 'react-native';
import EtherButton from '../components/EtherButton';
import {useTheme} from '../context/ThemeContext';
PageMenu.propTypes = {
onNext: func.isRequired,
onPrev: func.isRequired,
page: number.isRequired,
};
function PageMenu({onNext, onPrev, page}) {
const {style, values} = useTheme(getThemedStyles);
return (
<View style={style.pageMenuContainer}>
<EtherButton style={style.arrowButton} onPress={onPrev}>
<FontAwesomeIcon icon={faArrowLeft} color={values.BGSECOND} size={30} />
</EtherButton>
<View>
<Text style={style.counter}>{page}</Text>
</View>
<EtherButton style={style.arrowButton} onPress={onNext}>
<FontAwesomeIcon
icon={faArrowRight}
color={values.BGSECOND}
size={30}
/>
</EtherButton>
</View>
);
}
export default function TableList({
data,
pageSize,
style: overrides,
pageView = true,
}) {
const {style} = useTheme(getThemedStyles);
const [loadCount, setLoadCount] = useState(pageSize);
const [currentPage, setCurrentPage] = useState(0);
const [dataSlice, setDataSlice] = useState(data.slice(0, pageSize));
ListItem.proptypes = {
itemData: shape({
date: string.isRequired,
name: string.isRequired,
orderNumber: string.isRequired,
total: number.isRequired,
}),
};
function ListItem({orderData}) {
const [mouseHover, setMouseHover] = useState(false);
const textHighlight = [
style.valueText,
mouseHover ? style.textHighlighted : null,
];
return (
<View
onMouseOver={() => setMouseHover(true)}
onMouseLeave={() => setMouseHover(false)}
style={[style.listRow, mouseHover ? style.rowHighlighted : null]}
>
<View style={style.amountCell}>
<Text style={textHighlight}>{orderData.total} </Text>
</View>
<View style={style.nameCell}>
<Text style={textHighlight}>{orderData.name}</Text>
</View>
<View style={style.tokenCell}>
<Text style={textHighlight}>{orderData.orderNumber}</Text>
</View>
<View style={style.dateCell}>
<Text style={textHighlight}>
{new Date(orderData.date).toLocaleString()}
</Text>
</View>
</View>
);
}
function loadMore() {
const newCount = loadCount + pageSize;
setLoadCount(newCount);
setDataSlice(data.slice(0, newCount));
}
function changePage(direction) {
if (
(currentPage === 0 && direction === 'previous') ||
currentPage * pageSize > data
) {
return null;
}
if (direction === 'previous' && currentPage > 0) {
const newPage = currentPage - 1;
setDataSlice(data.slice(newPage * pageSize, currentPage * pageSize));
setCurrentPage(newPage);
}
if (direction === 'next') {
const newPage = currentPage + 1;
setDataSlice(data.slice(currentPage * pageSize, newPage * pageSize));
setCurrentPage(newPage);
}
}
return (
<View style={[style.mainContainer, overrides]}>
<View style={style.topRow}>
<View style={style.amountCell}>
<Text style={style.headerText}>Price</Text>
</View>
<View style={style.nameCell}>
<Text style={style.headerText}>Description</Text>
</View>
<View style={style.tokenCell}>
<Text style={style.headerText}>Order ID</Text>
</View>
<View style={style.dateCell}>
<Text style={style.headerText}>Date</Text>
</View>
</View>
<FlatList
data={dataSlice}
key={dataSlice}
renderItem={({item}) => <ListItem orderData={item} />}
keyExtractor={(item) => item.orderNumber}
style={style.flatList}
/>
<Text style={style.timeZone}>
Time shown in {new Date().toString().match(/([A-Z]+[-][0-9]+.*)/)[0]}
</Text>
<View style={style.bottomBar}>
{pageView ? (
<PageMenu
onPrev={() => changePage('previous')}
onNext={() => changePage('next')}
page={currentPage + 1}
/>
) : (
<EtherButton onPress={loadMore} style={style.loadMoreButton}>
<Text style={style.buttonText}>Load More</Text>
</EtherButton>
)}
</View>
</View>
);
}
const getThemedStyles = (theme, fontSize) => ({
mainContainer: {
backgroundColor: theme.BGFIRST,
borderColor: theme.FIRST,
borderWidth: 2,
borderRadius: 5,
overflowX: 'auto',
},
topRow: {
backgroundColor: theme.SECOND,
height: 40,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
borderTopLeftRadius: 2,
borderTopRightRadius: 2,
},
headerText: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.body,
color: theme.LIGHT,
alignSelf: 'center',
},
flatList: {
paddingVertical: 20,
},
pageMenuContainer: {
flexDirection: 'row',
alignItems: 'center',
},
counter: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.body,
backgroundColor: theme.BGSECOND,
color: theme.DARK,
padding: 5,
borderRadius: 5,
borderColor: theme.FIRST,
borderWidth: 2,
},
arrowButton: {
borderColor: theme.SECOND,
},
bottomBar: {
alignItems: 'center',
backgroundColor: theme.SECOND,
},
loadMoreButton: {
justifyContent: 'center',
alignItems: 'center',
height: 40,
marginVertical: 5,
width: '15%',
borderRadius: 5,
borderColor: theme.FIRST,
borderWidth: 2,
backgroundColor: theme.BGSECOND,
},
loadMoreText: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.subheader,
},
buttonText: {
fontFamily: 'NotoSans_Bold',
fontSize: fontSize.legal,
color: theme.FIRST,
textAlign: 'center',
},
// Table
listRow: {
alignItems: 'center',
flexDirection: 'row',
height: 33,
justifyContent: 'space-around',
},
rowHighlighted: {
backgroundColor: theme.SECOND,
},
valueText: {
alignSelf: 'center',
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.legal,
},
textHighlighted: {
color: theme.LIGHT,
},
amountCell: {
width: 80,
minWidth: 60,
},
nameCell: {
width: 220,
minWidth: 210,
},
tokenCell: {
width: 200,
minWidth: 150,
},
dateCell: {
width: 140,
minWidth: 115,
},
//
timeZone: {
alignSelf: 'center',
fontFamily: 'NotoSans_Bold',
fontSize: fontSize.legal,
color: theme.DARK,
marginBottom: 20,
},
});
Could you please try putting changePage into a useCallback and providing dataSlice as a dependency? It looks like a data leak to me.
Unrelated: you shouldn't use key if you are using the keyExtractor. I would remove the key prop from the FlatList.
I'm embarrassed that it took me this long to figure it out, but I found the answer. I was basing the data set off the wrong variables. Changing the setState in the if (direction === "next") section fixed it. Here's what I changed it to:
setDataSlice(data.slice(newPage * pageSize, (newPage + 1) * pageSize));

how to keep moving my circular progress bar after 30 seconds

import { Observer } from "mobx-react";
import React, { Component } from "react";
import {
StyleSheet,
Text,
View,
ImageBackground,
TouchableOpacity,
Image,
Platform,
Dimensions,
Clipboard,
} from "react-native";
import * as Progress from "react-native-progress";
import AppStore from "../../stores/AppStore";
import Iconpack from "../../utils/Iconpack";
import Theme from "../../utils/Theme";
import DeviceInfo from "react-native-device-info";
import CircularProgressBar from "./CircularProgressBar";
import { Shadow } from "react-native-shadow-2";
import Toast from "react-native-simple-toast";
import { BoxShadow } from "react-native-shadow";
import FastImage from "react-native-fast-image";
// import Clipboard from "#react-native-community/clipboard";
const hRem = AppStore.screenHeight / 812;
const wRem = AppStore.screenWidth / 375;
export class OtpDetails extends Component {
constructor() {
super();
this.state = {
progress: 0,
indeterminate: true,
clipboardText: "",
textInputText: "",
};
}
componentDidMount() {
this.animate();
}
animate() {
let progress = 0;
this.setState({ progress });
setTimeout(() => {
this.setState({ indeterminate: false });
const interval = setInterval(() => {
progress += Math.random() / 30;
if (progress > 1) {
progress = 1;
clearInterval(interval);
}
this.setState({ progress });
}, 600);
}, 600);
}
render() {
const shadowOpt = {
width: 160,
height: 170,
color: "#fff",
border: 2,
radius: 70,
opacity: 6,
x: 0,
y: 3,
};
const { myTitle } = this.props.route.params;
const setTextIntoClipboard = async () => {
await Clipboard.setString(myTitle);
Toast.show("OTP copied successfully!", 200);
};
const deviceName = DeviceInfo.getModel();
return (
<Observer>
{() => (
<View style={{ flex: 1 }}>
<FastImage
style={styles.container}
source={Iconpack.GLOBAL_BG}
resizeMode={FastImage.resizeMode.cover}
/>
<View style={styles.headerStyle}>
<TouchableOpacity
onPress={() => {
this.props.navigation.navigate("OtpScreen");
}}
>
<Image
style={styles.backButton}
source={Iconpack.GLOBAL_BACK_BUTTON}
/>
</TouchableOpacity>
<Text style={styles.headerTitle}>{myTitle}</Text>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity>
<Image
style={[styles.editTrashButton, { marginRight: 19 }]}
source={Iconpack.EDIT_ICON}
/>
</TouchableOpacity>
<TouchableOpacity>
<Image
style={styles.editTrashButton}
source={Iconpack.DELETE_ICON}
/>
</TouchableOpacity>
</View>
</View>
<View style={styles.circleOuter}>
{Platform.OS === "ios" ? (
<View style={styles.circleContainer}>
<Progress.Circle
style={{ alignItems: "center" }}
progress={this.state.progress}
indeterminate={this.state.indeterminate}
size={Dimensions.get("window").width * 0.561}
thickness={10}
borderWidth={0}
endAngle={10}
strokeCap="round"
color={"#354659"}
indeterminateAnimationDuration={2000}
unfilledColor={"#031830"}
/>
</View>
) : (
<Shadow
distance={20}
startColor={"rgba(27, 100, 206, 0.5)"}
radius={210}
safeRender={true}
>
<View style={styles.circleContainer}>
<Progress.Circle
style={{ alignItems: "center", overflow: "hidden" }}
progress={this.state.progress}
indeterminate={this.state.indeterminate}
size={Dimensions.get("window").width * 0.561}
thickness={10}
borderWidth={0}
endAngle={10}
strokeCap="round"
color={"#354659"}
indeterminateAnimationDuration={2000}
unfilledColor={"#031830"}
/>
</View>
</Shadow>
)}
<Text style={[styles.circleItem]}>{myTitle}</Text>
<TouchableOpacity
onPress={() => {
setTextIntoClipboard();
}}
style={styles.copyItem}
>
<Text style={styles.copyText}>TAP TO COPY</Text>
</TouchableOpacity>
</View>
{/* <View style={styles.circleOuter}>
<View style={styles.circleContainer}>
<Text style={styles.circleItem}>title</Text>
</View>
<TouchableOpacity
onPress={() => {
copyToClipboard();
}}
style={styles.copyItem}
>
<Text style={styles.copyText}>TAP TO COPY</Text>
</TouchableOpacity> */}
{/* </View> */}
</View>
)}
</Observer>
);
}
}
export default OtpDetails;
const styles = StyleSheet.create({
container: {
flex: 1,
position: "absolute",
top: 0,
right: 0,
left: 0,
bottom: 0,
backgroundColor: "#021831",
},
headerStyle: {
flexDirection: "row",
marginTop: hRem * 56,
marginHorizontal: wRem * 26.66,
justifyContent: "space-between",
alignItems: "center",
},
backButton: {
width: wRem * 12,
height: hRem * 21,
},
editTrashButton: {
width: wRem * 23,
height: hRem * 23,
},
headerTitle: {
...Theme.encodeSansMed2,
lineHeight: hRem * 16,
color: "#FFFFFF",
fontWeight: "600",
marginLeft: wRem * 50,
},
circleOuter: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
circleContainer: {
borderRadius:
Math.round(
Dimensions.get("window").width + Dimensions.get("window").height
) / 2,
width: Dimensions.get("window").width * 0.56,
height: Dimensions.get("window").width * 0.56,
justifyContent: "center",
backgroundColor: "black",
shadowColor: "rgba(27, 100, 206, 0.5)",
shadowOffset: {
width: 0,
height: 0,
},
shadowOpacity: 30,
shadowRadius: 30,
},
circleItem: {
color: "white",
...Theme.encodeSansMed7,
textAlign: "center",
position: "absolute",
width: "100%",
paddingBottom: 100,
},
copyItem: {
justifyContent: "center",
alignItems: "center",
marginTop: hRem * 64,
},
copyText: {
color: "#3D4756",
...Theme.encodeSansMed4,
},
});
I have added circular progress by using the react-native-progress library. I have implemented logic for moving bar for 30 sec but the thing is after 30 sec it stops, so I need to keep moving after 30 sec here my whole code.
Here is the logical part which I have added here...
constructor() {
super();
this.state = {
progress: 0,
indeterminate: true,
clipboardText: "",
textInputText: "",
};
}
componentDidMount() {
this.animate();
}
animate() {
let progress = 0;
this.setState({ progress });
setTimeout(() => {
this.setState({ indeterminate: false });
const interval = setInterval(() => {
progress += Math.random() / 30;
if (progress > 1) {
progress = 1;
clearInterval(interval);
}
this.setState({ progress });
}, 600);
}, 600);
}
I have tried several ways but nothing is happening also function will be welcome for this question. thanks in advance

why is the timer not getting back to zero when doing console in handlebackpress method even after doing clearInterval also how to avoid losing frames

I m trying to automatically start the video but before that implementing timer and clearing the timer was my goal. The countdown is getting cleared but the timer in the state in which my setInterval is assigned keeps on increasing even after been cleared. Also, there are a lot of frames dropped and stutters on each toggle even my code has become too messy I would really appreciate any kind of help that would help me to improve my messy code and avoid those dropped frames.
Thank you.
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
BackHandler,
TouchableOpacity,
View,
Alert,
PermissionsAndroid,
CameraRoll,
} from 'react-native';
import { RNCamera } from 'react-native-camera';
import RNFetchBlob from 'rn-fetch-blob';
const url = 'MY API';
export default class App extends Component {
constructor() {
super();
this.state = {
showCamera: false,
cameraType: RNCamera.Constants.Type.front,
recording: false,
timer: null,
count: null
};
}
componentDidMount() {
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
this.permissionRequest();
}
componentWillUnmount() {
this.backHandler.remove();
}
async permissionRequest() {
try {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
'title': 'Require Permission for Storage',
'message': 'Storage permisson is required to store your photos in the gallery'
});
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('granted', PermissionsAndroid.RESULTS);
} else {
console.warn('denied', PermissionsAndroid.RESULTS);
}
} catch (err) {
console.warn('err', err);
}
}
handleBackPress = async () => {
if (this.state.showCamera === true) {
await this.setState({ showCamera: false });
clearInterval(this.state.timer);
console.log('this.state.timer', this.state.timer);
// this.setState({ count: null });
console.log('this.state.timer after', this.state.timer, this.state.count);
} else {
BackHandler.exitApp();
}
}
async toggleCamera() {
// console.log(this,'this');
await this.setState({ showCamera: !this.state.showCamera });
console.warn(this.state.showCamera, 'checking status');
if (this.state.showCamera === true) {
this.setState({ count: 10 });
let timer = setInterval(this.tickingTimer, 1000);
this.setState({ timer });
}
}
tickingTimer = () => {
if (this.state.count > 0) {
this.setState({ count: this.state.count -1 });
console.log(this.state.count);
}
}
async takePicture() {
if (this.camera) {
const options = { quality: 0.9, base64: true };
const data = await this.camera.takePictureAsync(options);
CameraRoll.saveToCameraRoll(data.uri)
.then(resp => {
console.log('resp', resp);
})
.catch(err => {
console.log('err', err);
});
console.log(data);
}
}
async recordVideo() {
if (this.camera) {
this.setState({ recording: true });
const options = { quality: 'RNCamera.Constants.VideoQuality.480p', maxDuration: 2 };
const {uri, codec = 'mp4' } = await this.camera.recordAsync(options);
this.setState({ recording: false });
const type = `video/${codec}`;
CameraRoll.saveToCameraRoll(uri)
.then(resp => {
console.log('video resp', resp)
})
.catch(err => {
console.log('err Video', err);
})
RNFetchBlob.fetch('POST', url + 'upload', {
},
[
{
name: 'file',
filename: 'filename.mp4',
data: RNFetchBlob.wrap(uri),
type
}
]
)
.uploadProgress({ interval : 200 }, (written, total) => {
console.log('progress', written, total, written / total)
})
.then(res => JSON.parse(res.data))
.then((res) => {
console.log(' server', res.file.URL);
})
.catch((err) => {
console.log('error ', err);
});
}
}
async stopVideo() {
if (this.camera) {
await this.camera.stopRecording();
this.setState({ recording: false });
}
}
swapCamera() {
const { cameraType } = this.state;
let newType;
if (this.camera) {
cameraType === RNCamera.Constants.Type.front ? newType = RNCamera.Constants.Type.back : newType = RNCamera.Constants.Type.front;
}
this.setState({ cameraType: newType });
}
render() {
const { showCamera, recording, cameraType, count } = this.state;
return (
<View style={styles.container}>
{
showCamera === false ?
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<TouchableOpacity style={{ height: 50, width: 80, marginVertical: 5, justifyContent: 'center', alignItems: 'center', borderRadius: 5, backgroundColor: 'rgba(52,130,116,0.9)' }} onPress={() => this.toggleCamera()}>
<Text style={{ color: 'white', textAlign: 'center' }}>Open Camera</Text>
</TouchableOpacity>
<TouchableOpacity style={{ height: 50, width: 80, marginVertical: 5, justifyContent: 'center', alignItems: 'center', borderRadius: 5, backgroundColor: 'rgba(52,130,116,0.9)' }} onPress={() => this.toggleCamera()}>
<Text style={{ color: 'white', textAlign: 'center' }}>Record Video</Text>
</TouchableOpacity>
</View>
:
<RNCamera style={{ flex: 1, alignItems: 'center', justifyContent: 'flex-end' }} ref={ref => { this.camera = ref }} flashMode={RNCamera.Constants.FlashMode.auto} captureAudio = {true} type={cameraType}>
<View style={{ flexDirection: 'row', marginVertical: 20 }}>
<View style={{ flex: 0.3, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'white' }}>starts in <Text style={{color: 'red', fontWeight: 'bold', fontSize: 16 }}>{count}</Text></Text>
</View>
<View style={{ flex: 0.3, justifyContent: 'center', alignItems: 'center' }}>
<View style={{ height: 60, width: 60, marginHorizontal: 5, borderRadius: 30, padding: 5, borderWidth: 2, borderColor: 'white' }}>
{recording === false ?
<TouchableOpacity disabled={this.state.count !== 0} onPress={() => this.recordVideo()} style={{ flex: 1, borderRadius: 30, backgroundColor: 'white', alignItems: 'center', justifyContent: 'center' }} /> :
<TouchableOpacity onPress={() => this.stopVideo()} style={{ flex: 1, borderRadius: 30, backgroundColor: 'red', alignItems: 'center', justifyContent: 'flex-end' }} />
}
</View>
</View>
<View style={{ flex: 0.4 }}>
<TouchableOpacity onPress={() => this.swapCamera()} style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'white' }}>Swap</Text>
</TouchableOpacity>
</View>
</View>
</RNCamera>
}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
padding: 15,
paddingHorizontal: 20,
alignSelf: 'center',
margin: 20
}
});
// This is my package.json file
{
"name": "newApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"react": "16.3.1",
"react-native": "0.55.4",
"react-native-camera": "git+https://git#github.com/react-native-community/react-native-camera",
"react-native-easy-toast": "^1.1.0",
"react-native-progress-circle": "^2.0.0",
"rn-fetch-blob": "^0.10.13"
},
"devDependencies": {
"babel-jest": "22.4.4",
"babel-preset-react-native": "4.0.0",
"jest": "22.4.4",
"react-test-renderer": "16.3.1"
},
"jest": {
"preset": "react-native"
}
}
Keep timer as class object and not a state variable, try this
this.setState({ showCamera: false }, () => {
clearInterval(this.timer);
});

Categories