expo camera show black screen for first time : React-Native - javascript

I have Created Project with expo-camera which on click of button opens camera. However first time when i click that button only black screen is render, after reopening the screen, and clicking the same button, the Camera UI is render on Screen.
Problem
I don't know why it show black screen first. The Problem only exist in android devices.
Link to Expo Snack Try this example on android devices.
import React, { useState, useEffect, useContext } from 'react';
import {
SafeAreaView,
View,
Text,
Dimensions,
ImageBackground,
Image,
Alert,
} from 'react-native';
import { Camera } from 'expo-camera';
import { Entypo, Ionicons, FontAwesome } from '#expo/vector-icons';
import { Surface, Button, TextInput } from 'react-native-paper';
const { width, height } = Dimensions.get('window');
let cameraRef;
const PickImage = ({ navigation }) => {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const [isCameraUiOn, setIsCameraUiOn] = useState(false);
const [isCapturing, setIsCapturing] = useState(false);
const [flashMode, setFlashMode] = useState(true);
const [capturePhoto, setCapturePhoto] = useState(null);
const snap = async () => {
if (cameraRef) {
let photo = await cameraRef.takePictureAsync({
quality: 0.5,
});
setCapturePhoto(photo.uri);
setIsCapturing(false);
setIsCameraUiOn(false);
}
};
const getCameraPermission = async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
};
useEffect(() => {
getCameraPermission();
}, []);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
if (isCameraUiOn) {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1 }}
type={type}
ref={(ref) => (cameraRef = ref)}
flashMode={
flashMode
? Camera.Constants.FlashMode.on
: Camera.Constants.FlashMode.off
}>
<Entypo
name="cross"
color="#FFF"
size={50}
onPress={() => setIsCameraUiOn(false)}
/>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
display: 'flex',
justifyContent: 'space-evenly',
alignItems: 'flex-end',
flexDirection: 'row',
zIndex: 9999,
}}>
<Ionicons
name="md-reverse-camera"
color="#FFF"
size={35}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}
/>
{isCapturing ? (
<FontAwesome name="circle" color="#FFF" size={60} />
) : (
<FontAwesome
name="circle-o"
color="#FFF"
size={60}
onPress={() => {
setIsCapturing(true);
snap();
}}
/>
)}
<Ionicons
name="ios-flash"
color={flashMode ? 'gold' : '#FFF'}
size={35}
onPress={() => setFlashMode(!flashMode)}
/>
</View>
</Camera>
</View>
</SafeAreaView>
);
} else {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ margin: 20 }}>
<Text
style={{
fontSize: 25,
fontWeight: '600',
}}>
Report Cattles with Ease !!
</Text>
<Text
style={{
marginVertical: 10,
}}>
Our System uses, cloud based fancy image detection algorithm to
process your image.
</Text>
</View>
<View
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}>
<Button
style={{
width: width * 0.5,
}}
icon="camera"
mode="contained"
onPress={() => setIsCameraUiOn(true)}>
Press me
</Button>
</View>
</SafeAreaView>
);
}
};
export default PickImage;
Link to snack Expo

im with the same problem rn, some people suggested asking for permission before rendering

Related

Displaying a Image from the Camera

I am relatively new to React-Native and mobile app development. I am trying to create a clone of Instagram. And in the post screen what I do is I have a button Camera and once I click on it, that should take me to CameraView screen. And in the CameraView screen I will take a picture using expo-camera and save the picture in a variable. So here, I want to export the picture and then import it in my Post screen, so that the user can have a preview of what the picture looks like before uploading. And another thing to note is that, the Post screen will by default have a Placeholder image and once the image is taken the placeholder image has to be replaced with the image.
I tried this code but it didnt work.
My Post screen :-
import { View, Text, Image, TextInput } from 'react-native';
import React, { useState, useEffect } from 'react'
import * as Yup from 'yup';
import { Formik } from 'formik'
import { Button, Divider } from 'react-native-elements';
import { useNavigation } from '#react-navigation/native';
import CameraView, { takePicture } from '../../screens/CameraView';
const PLACEHOLDER_IMG = require('../../assets/defImg.png')
const uploadPostSchema = Yup.object().shape({
caption: Yup.string().max(2200, 'Caption cannot exceed character limit.')
})
const FormikPostUploader = ({route}) => {
const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
const navigation = useNavigation()
const handleCameraPress = () => {
UserImage = takePicture()
setThumbnailUrl(UserImage)
navigation.navigate('CameraView')
}
return (
<Formik
initialValues={{ imageUrl: '', caption:'' }}
onSubmit={(values) => console.log(values)}
validationSchema={uploadPostSchema}
validateOnMount={true}>
{({
handleBlur,
handleChange,
handleSubmit,
values,
errors,
isvalid
}) => (
<>
<View style={{ margin: 20, justifyContent: 'space-between', flexDirection: 'row' }}>
<Image source={thumbnailUrl} />
<View style={{ flex: 1, margin: 15 }}>
<TextInput
placeholder='Write a caption...'
placeholderTextColor='gray'
multiline={true}
style={{ color: 'white', fontSize: 17 }}
onChangeText={handleChange('caption')}
onBlur={handleBlur('caption')}
value={values.caption}
/>
</View>
</View>
<Divider width={0.5} orientation='vertical'/>
<View style={{ alignItems: 'center', marginTop: 15 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', color: 'white' }}>Choose Image</Text>
</View>
<View style={{ justifyContent: 'space-between', flexDirection: 'column', margin: 20 }}>
<View style={{ justifyContent: 'space-between', flexDirection: 'row', margin: 30 }}>
<Button title='Camera' onPress={handleCameraPress}/>
<Button title='Gallery' onPress={() => navigation.navigate('GalleryView')} />
</View>
<Button onPress={handleSubmit} title='Post' disabled={isvalid} />
</View>
</>
)}
</Formik>
)
}
export default FormikPostUploader
And my CameraView screen :-
import { View, Text, Image, StyleSheet, Button } from 'react-native'
import React, {useState, useEffect} from 'react'
import { Camera } from 'expo-camera'
import { TouchableOpacity } from 'react-native'
import { useNavigation } from '#react-navigation/native'
import { PLACEHOLDER_IMG } from '../components/newPost/FormikPostUploader'
export const takePicture = async(camera) => {
const data = await camera.takePictureAsync(null)
return data.uri
}
const Header = () =>{
const navigation = useNavigation()
return (
<View style = {styles.headerContainer}>
<TouchableOpacity style = {{marginBottom:15}} onPress={() => navigation.goBack()}>
<Image source = {require('../assets/back.png')} style = {{width:35, height:35, margin:20}}/>
</TouchableOpacity>
<Text style={{color:'white', fontWeight: '700', fontSize:20, marginRight: 25, marginTop:35, marginBottom:15}}>New Post</Text>
<Text> </Text>
</View>
)
}
const CameraView = () => {
const UserImage = takePicture()
const navigation = useNavigation()
const [hasCameraPermission, setHasCameraPermission] = useState(null)
const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
const [camera, setCamera] = useState(null)
const [type, setType] = useState(Camera.Constants.Type.back)
const captureImage = async () => {
if (camera) {
const UserImage = await takePicture(camera)
setThumbnailUrl(UserImage)
}
}
useEffect(() => {
(async() => {
const cameraStatus = await Camera.requestCameraPermissionsAsync()
setHasCameraPermission(cameraStatus.status === 'granted')})()},
[])
if (hasCameraPermission === null) {
return <View/>
}
if (hasCameraPermission === false){
return <Text>Enable access for Camera to proceed</Text>
}
return (
<View style={{ flex: 1, backgroundColor: 'black'}}>
<Header/>
<Camera
ref = {ref => setCamera(ref)}
style={styles.fixedRatio}
type={type}
ratio={'1:1'}/>
<View style = {styles.cameraContainer}>
<TouchableOpacity onPress={() => {setType(type === Camera.Constants.Type.back ? Camera.Constants.Type.front : Camera.Constants.Type.back)}}>
<Image source={require('../assets/turn-camera.png')} style = {{width:70, height:70, margin: 15}}/>
</TouchableOpacity>
<TouchableOpacity onPress={() => {captureImage(), navigation.goBack(), console.log(UserImage)}}>
<Image source={require('../assets/camera-shutter.png')} style = {{width:70, height:70, marginRight:60}}/>
</TouchableOpacity>
</View>
</View>
)
}
const styles = StyleSheet.create({
cameraContainer: {
flex:0.15,
backgroundColor: 'black',
flexDirection:'row',
justifyContent: 'space-between',
alignItems: 'center',
},
fixedRatio: {
flex:0.99,
aspectRatio:0.9
},
headerContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor:'black'
},
icon: {
width:25,
height: 25,
marginHorizontal:15,
marginTop:30,
},
iconContainer: {
flexDirection:'row',
justifyContent: 'space-between',
height:50,
},
}
)
export default CameraView
Am I doing anything wrong. Can you help me fix this. Thanks in advance!

Components display perfectly on web view but aren't being displayed on android view in React Native

I'm building an instagram clone using React Native with Homescreen consisting of custom Header and Imagepost components,but it isn't showing up properly on android although it displays perfectly on web view(images below).
I only added the Text and the Test Button for testing purposes
HomeScreen.js:
import React, { useEffect, useState } from "react";
import { View, Text, TouchableOpacity, Button } from "react-native";
import axios from "axios";
import ImagePost from "../../components/ImagePost";
import Header from "../../components/Header";
import Ionicons from "react-native-vector-icons/Ionicons";
import useStore from "../../store";
const HomeScreen = ({ navigation }) => {
const [posts, setposts] = useState([]);
const [current, setcurrent] = useState(null);
const user = useStore((state) => state.currentUser);
const setcurrentUser = useStore((state) => state.setcurrentUser);
useEffect(() => {
axios.get("http://localhost:5000/posts").then((response) => {
console.log(response.data.posts);
setposts(response.data.posts);
});
}, []);
return (
<View>
<Header navigation={navigation} />
<Text>huidhoasodhiohdp</Text>
<Button title="test" />
{console.log("now user", user)}
{console.log("posts", posts)}
{posts &&
posts.map((post) => {
return (
<View key={post._id}>
<ImagePost post={post} navigation={navigation} />
{console.log("This post is", post)}
</View>
);
})}
</View>
);
};
export default HomeScreen;
Header.js:
import React from "react";
import { View, TouchableOpacity, Text } from "react-native";
import Ionicons from "react-native-vector-icons/Ionicons";
import useStore from "../store";
const Header = ({ navigation }) => {
console.log("header navigation", navigation);
const user = useStore((state) => state.currentUser);
return (
<View
style={{
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
backgroundColor: "white",
}}
>
<TouchableOpacity
onPress={() => navigation.navigate("AddScreen")}
style={{ margin: "5%" }}
>
<Ionicons name="add-outline" size={25} color="black" />
</TouchableOpacity>
{user && <Text>{user.username}</Text>}
{user && console.log(user.username)}
<TouchableOpacity style={{ margin: "5%" }}>
<Ionicons name="chatbubble-ellipses-outline" size={25} color="black" />
</TouchableOpacity>
</View>
);
};
export default Header;
ImagePost.js :
import React from "react";
import { View, Text, Image, TouchableOpacity, Button } from "react-native";
import Ionicons from "react-native-vector-icons/Ionicons";
const ImagePost = ({ post, navigation }) => {
return (
<View>
<View style={{ flex: 1, flexDirection: "row", alignItems: "center" }}>
<Image
style={{
width: 50,
height: 50,
borderRadius: 50,
marginRight: "3%",
}}
source={post.creator.pic}
/>
<Text style={{ fontWeight: "bold" }}>User</Text>
</View>
<View style={{ width: "100%", height: 450 }}>
<Image
style={{ resizeMode: "cover", height: "100%" }}
source={post.file}
/>
<View style={{ flex: 1, flexDirection: "row", marginBottom: "6%" }}>
<TouchableOpacity style={{ marginRight: "1%" }}>
<Ionicons name="heart-outline" size={25} color="black" />
</TouchableOpacity>
<TouchableOpacity
style={{ marginRight: "1%" }}
onPress={() =>
navigation.navigate("CommentsScreen", { postId: post._id })
}
>
<Ionicons name="chatbubble-outline" size={25} color="black" />
</TouchableOpacity>
</View>
<Text>
<Text style={{ fontWeight: "bold", marginRight: "5%" }}>
{post.creator.userName}
</Text>
{post.description}
</Text>
</View>
</View>
);
};
export default ImagePost;
Android view
Web view

React-Native expo modal with textinput, Keyboard and modal flicker after every text entry unable to input text

I have an issue with modal not being stable when text input receives entry. I have struggled with this and read most post on this site but when I try it, no results. One of them is Keyboard closes after every keypress in TextInput react native. I have taken most of the irrelevant code out so you have an idea of what I want to achieve. It's quiet lengthy but I tried.
import React, { useEffect, useState } from "react";
/*
loads of imports
**/
const ReplyFeedback = (props) => {
const [dataset, setdataset] = useState([]);
const [replyset, setreplyset] = useState([]);
const [visible, setvisible] = useState(null);
const [messagereply, setmessagereply] = useState("");
const [msgid, setmsgid] = useState(null);
const dispatch = useDispatch();
/**
* useEffects and functions
*/
const handleSaveReply = (id) => {
/**
* Saving the reply from text input for firebase
*/
};
const handleCaseupdate = async () => {
/**
* Some firebase events
*/
};
function processreply(id) {
setmsgid(id);
setvisible(true);
}
const renderfeedback = (id, message, date, index) => (
/**
* reder some jsx
*/
);
const renderreply = (item, index) => (
/**
* render some jsx
*/
);
const ModalPopup = ({ visible, children }) => {
const [showmodal, setshowmodal] = useState(visible);
const togglemodal = () => {
if (visible) {
setshowmodal(true);
} else {
setshowmodal(false);
}
};
return (
<Modal transparent visible={showmodal}>
<View
style={{
flex: 1,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "center",
alignItems: "center",
}}
>
<View style={styles.modalContainer}>{children}</View>
</View>
</Modal>
);
};
return (
<React.Fragment>
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView behavior="position">
<ModalPopup visible={visible}>
<View
style={{
width: "90%",
height: "72%",
backgroundColor: "rgb(255,255,255)",
}}
>
<View
style={{
flexDirection: "row",
justifyContent: "center",
}}
>
<TextInput
placeholder="Enter Reply"
multiline
autoCorrect
scrollEnabled
numberOfLines={8}
autoCapitalize="sentences"
placeholderTextColor="rgb(0,191,255)"
onChangeText={(text) => setmessagereply(text)}
value={messagereply}
blurOnSubmit={false}
style={{
width: "100%",
height: 250,
fontSize: 20,
padding: 10,
textAlignVertical: "top",
backgroundColor: "rgb(245,245,245)",
}}
/>
</View>
<View
style={{
marginTop: 15,
flexDirection: "row",
justifyContent: "space-around",
}}
>
<TouchableOpacity
style={{
width: 65,
height: 65,
borderRadius: 30,
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgb(109, 123, 175)",
}}
onPress={() => setvisible(false)}
>
<MaterialIcons
name="cancel"
size={50}
color="rgb(225,225,225)"
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => handleSaveReply(msgid)}
style={{
width: 65,
height: 65,
borderRadius: 30,
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgb(109, 123, 175)",
}}
>
<Ionicons name="send" size={40} color="rgb(255,255,255)" />
</TouchableOpacity>
</View>
</View>
</ModalPopup>
</KeyboardAvoidingView>
<View style={styles.feedbackreply}>
<View style={styles.replydetails}>
{/**
* some jsx
*/}
</View>
{/**
* somejsx
*/}
</View>
<View
style={{
alignSelf: "flex-end",
marginHorizontal: 5,
marginTop: 5,
}}
>
{/**
* some jsx
*/}
</View>
<ScrollView
automaticallyAdjustContentInsets={false}
keyboardShouldPersistTaps="handled"
>
{dataset &&
dataset
.sort(
(a, b) =>
new moment(new Date(a.date)).format("YYYYMMDD HHmmss") -
new moment(new Date(b.date)).format("YYYYMMDD HHmmss")
)
.map(({ id, message, date }, index) =>
renderfeedback(id, message, date, index)
)}
<ScrollView keyboardShouldPersistTaps="handled">
{replyset &&
replyset
.sort(
(a, b) =>
new moment(new Date(a.date)).format("YYYYMMDD HHmmss") -
new moment(new Date(b.date)).format("YYYYMMDD HHmmss")
)
.filter((item) => item.id !== id)
.map((item, index) => renderreply(item, index))}
</ScrollView>
</ScrollView>
</SafeAreaView>
</React.Fragment>
);
};
export { ReplyFeedback };
const window = Dimensions.get("window");
const styles = StyleSheet.create({
/**
* Tones of styling
*/
});
So you could see from the container I rendered some other views including a scrollview that all appears under the modal when it is shown. Please the issue here is that the modal flickers along with the keyboard making it impossible to add text
This is the image

How to save route.params with asyncstorage?

Srry if the title makes no sense. Don't know a better title.
How can I save route.params items that I pass to my second screen using AsyncStorage?
In my first screen i have a bunch of data in a FlatList that can be opened with a Modal. Inside that Modal I have a TouchableOpacity that can send the data thats inside the Modal to my second screen. The data that has been passed to the second screen is passed to a FlatList. The data in the FlatList should be saved to AsyncStorage. Tried alot of things getting this to work, but only getting warning message
undefined. Code below is the most recent progress.
Using React Navigation V5.
FIRST SCREEN
const [masterDataSource, setMasterDataSource] = useState(DataBase);
const [details, setDetails] = useState('');
<TouchableOpacity
onPress={() => {
const updated = [...masterDataSource];
updated.find(
(item) => item.id === details.id,
).selected = true;
setMasterDataSource(updated);
navigation.navigate('cart', {
screen: 'cart',
params: {
items: updated.filter((item) => item.selected),
},
});
setModalVisible(false);
}}>
<Text>Add to cart</Text>
</TouchableOpacity>
SECOND SCREEN
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { useTheme } from '../Data/ThemeContext';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default function ShoppingList({ route, navigation }) {
const [shoppingList, setShoppingList] = useState([]);
const { colors } = useTheme();
const todo = () => {
alert('Todo');
};
useEffect(() => {
restoreShoppingListAsync();
}, []);
const shoppingListAsync = () => {
const shoppingList = route.params && route.params.items;
setShoppingList(list);
storeShoppingList(list);
};
const asyncStorageKey = '#ShoppingList';
const storeShoppingListAsync = (list) => {
const stringifiedList = JSON.stringify(list);
AsyncStorage.setItem(asyncStorageKey, stringifiedList).catch((err) => {
console.warn(err);
});
};
const restoreShoppingListAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then((stringifiedList) => {
console.log(stringifiedList);
const parsedShoppingList = JSON.parse(stringifiedList);
if (!parsedShoppingList || typeof parsedShoppingList !== 'object')
return;
setShoppingList(parsedShoppingList);
})
.then((err) => {
console.warn(err);
});
};
const RenderItem = ({ item }) => {
return (
<View>
<TouchableOpacity
style={{
marginLeft: 20,
marginRight: 20,
elevation: 3,
backgroundColor: colors.card,
borderRadius: 10,
}}>
<View style={{ margin: 10 }}>
<Text style={{ color: colors.text, fontWeight: '700' }}>
{item.name}
</Text>
<Text style={{ color: colors.text }}>{item.gluten}</Text>
<Text style={{ color: colors.text }}>{item.id}</Text>
</View>
</TouchableOpacity>
</View>
);
};
const emptyComponent = () => {
return (
<View style={{ alignItems: 'center' }}>
<Text style={{ color: colors.text }}>Listan är tom</Text>
</View>
);
};
const itemSeparatorComponent = () => {
return (
<View
style={{
margin: 3,
}}></View>
);
};
return (
<View
style={{
flex: 1,
}}>
<View
style={{
padding: 30,
backgroundColor: colors.Textinput,
elevation: 12,
}}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Ionicons name="arrow-back-outline" size={25} color="#fff" />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20 }}>Inköpslista</Text>
<TouchableOpacity>
<Ionicons
name="trash-outline"
size={25}
color="#fff"
onPress={() => todo()}
/>
</TouchableOpacity>
</View>
</View>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
data={shoppingList}
renderItem={RenderItem}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={itemSeparatorComponent}
initialNumToRender={4}
maxToRenderPerBatch={5}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
showsVerticalScrollIndicator={true}
contentContainerStyle={{ paddingBottom: 20 }}
/>
</View>
</View>
);
}
As you are using async storage to maintain the cart.
I would suggest an approach as below
Update the asyn storage when new items are added to or removed from the cart
Retrieve the items from the cart screen and show the items there
Before you navigate store the items like below
AsyncStorage.setItem(
'Items',
JSON.stringify(updated.filter((item) => item.selected))
).then(() => {
navigation.navigate('Cart', {
items: updated.filter((item) => item.selected),
});
});
The cart screen would be something like below
function Cart({ navigation, route }) {
const [data,setData]=useState([]);
React.useEffect(() => {
async function fetchMyAPI() {
const value = await AsyncStorage.getItem('Items');
setData(JSON.parse(value));
}
fetchMyAPI();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Go back" onPress={() => navigation.goBack()} />
<FlatList
data={data}
renderItem={RenderItem}
/>
</View>
);
}
Working Example
https://snack.expo.io/#guruparan/cartexample

I can't wrap a FlatList, it makes the FlatList height is over the wrapper

Hello Everyone!
I want to display my notification data inside a Modal and using a FlatList, I am using react-native-modal for the Modal component, inside a Modal I have 3 main component like header, body, and footer.
header is just a title with the bottom line of it
body is a FlatList
and footer is a just Button for clear the notification
These 3 main components I wrap again with View component and I set the maxHeight for this (View) component
The body is the main issue here because the FlatList height is over the View height that I set the maxHeight, it's hard to explain and sorry, but you can see the picture at the bottom
And the important one is that I can't use flex:1 inside a Modal it's because a module that I use (react-native-modal) if I use flex: 1 it makes FlatList disappear
Picture
enter image description here
Code of ModalNotificationShowcase.jsx
import React, {
forwardRef,
memo,
useContext,
useEffect,
useImperativeHandle,
useState,
} from 'react';
import {View, Dimensions, Pressable, FlatList} from 'react-native';
import {Divider, Button, Icon, List} from '#ui-kitten/components';
import {IconHelper, Text, Modal} from '../Helper';
import {useRealmInstance, useRealmObjects} from '../../hooks';
import moment from 'moment';
import util from '../../utils';
import ThemeContext from '../../themes/ThemeContext';
const NotificationItem = memo(props => {
const {currentTheme} = props;
const [bgColor, setBgColor] = useState(
currentTheme === 'light' ? '#EDF1F7' : '#1A2138',
);
const notif = props.data;
const realm = props.realm;
return (
<Pressable
onPressIn={() =>
setBgColor(currentTheme === 'light' ? '#E4E9F2' : '#151A30')
}
style={{
padding: 12,
marginBottom: 12,
backgroundColor: bgColor,
borderRadius: 4,
}}
onPress={props.onPress}
onPressOut={() =>
setBgColor(currentTheme === 'light' ? '#EDF1F7' : '#1A2138')
}
>
<View style={{flexDirection: 'row'}}>
<IconHelper
color={notif.name === 'reminder' ? '#FF3D71' : '#0095FF'}
name={
notif.name === 'reminder'
? 'alert-triangle-outline'
: 'info-outline'
}
style={{marginRight: 8}}
onPress={() => console.log('Pressed')}
/>
<Text size={14} style={{flex: 1, marginBottom: 4}}>
{notif.message}
</Text>
</View>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<Text size={12} hint>
{moment(notif.createdAt).fromNow()}
</Text>
<View style={{flex: 1, alignItems: 'flex-end'}}>
<IconHelper
size={20}
color='#FF3D71'
name='trash-2-outline'
underlayColor='#E4E9F2'
onPress={() => {
realm.write(() => {
realm.delete(notif);
});
}}
/>
</View>
</View>
</Pressable>
);
});
const NotificationShowcase = props => {
const realm = useRealmInstance();
const notifications = useRealmObjects('notifications');
const {theme} = useContext(ThemeContext);
return (
<View
style={{
width: Dimensions.get('window').width - 48,
maxHeight: Dimensions.get('window').height - 48,
borderRadius: 4,
}}
>
// =====
// Header
// =====
<Text size={14} bold align='center' style={{padding: 12}}>
Notifikasi
</Text>
<Divider />
// =====
// Body
// =====
<View style={{padding: 12, flexGrow: 1, overflow: 'hidden'}}>
{notifications.isEmpty() ? (
<Text
size={14}
align='center'
hint
style={{
padding: 8,
marginBottom: 12,
backgroundColor: theme === 'light' ? '#EDF1F7' : '#1A2138',
borderRadius: 4,
}}
>
Belum ada notifikasi untuk saat ini
</Text>
) : (
// ======================
// The main issue is here
// ======================
<FlatList
data={notifications}
initialNumToRender={0}
maxToRenderPerBatch={1}
updateCellsBatchingPeriod={120}
numColumns={1}
keyExtractor={item => item._id.toString()}
renderItem={items => {
const notif = items.item;
return (
<NotificationItem
data={notif}
realm={realm}
currentTheme={theme}
/>
);
}}
/>
)}
// =====
// Footer
// =====
{!notifications.isEmpty() && (
<View
style={{
justifyContent: 'center',
alignItems: 'center',
}}
>
<Button
size='small'
appearance='outline'
accessoryLeft={props => (
<Icon {...props} name='trash-2-outline' />
)}
onPress={() => {
realm.write(() => {
realm.delete(notifications);
});
}}
>
Hapus semua
</Button>
</View>
)}
</View>
</View>
);
};
const ModalNotificationShowcase = forwardRef((props, ref) => {
let Modal_ref;
useImperativeHandle(ref, () => ({
show: () => Modal_ref.show(),
hide: () => Modal_ref.hide(),
}));
return (
<Modal
backdropStyle={{backgroundColor: 'rgba(9, 44, 76, .32)'}}
onBackdropPress={() => Modal_ref.hide()}
ref={refs => (Modal_ref = refs)}
>
<NotificationShowcase
navigate={props.navigate}
onPressNotifItem={() => Modal_ref.hide()}
/>
</Modal>
);
});
export default ModalNotificationShowcase;
Updated
const NotificationShowcase = props => {
return (
<View
style={{
flex: 1,
margin: 24,
borderRadius: 4,
}}
>
...
// =====
// Body
// =====
<View style={{flex: 1}}>
// ...
</View>
</View>
);
};

Categories

Resources