React Native app data storage to Google drive and iCloud - react-native

I am trying to store my app data on Google Drive and iCloud based on user device.I don't want to use Async Storage ,redux state neither I want to store data on my server cloud i.e. AWS. Basically I like the way how WhatsApp takes data backup on google drive for android devices and iCloud for IOS devices.This way I want to store my encrypted data's private keys on drive or iCloud so that if user changes his device I can get these keys from drive or iCloud and proceed for decryption mechanism.I found https://github.com/manicakes/react-native-icloudstore which serves my purpose for iCloud.But I haven't found anything on same line for google drive.Can you please suggest me better approach for above requirement?

For Google Drive Implementation You can Check this Package : react-native-google-drive-api-wrapper . You Have To Use Google SignIn With this package In order Get the Access Token , Install This Package Also react-native-google-signin/google-signin .
A Quick Example :
import { GoogleSignin } from "#react-native-google-signin/google-signin";
import {
GDrive,
ListQueryBuilder,
MimeTypes
} from "#robinbobin/react-native-google-drive-api-wrapper";
import React, {
useCallback,
useEffect,
useState
} from "react";
import {
AppRegistry,
Button,
SafeAreaView,
StyleSheet
} from "react-native";
import { name } from './app.json';
function App() {
const [gdrive] = useState(() => new GDrive());
const [ui, setUi] = useState();
const invoker = useCallback(async cb => {
try {
return await cb();
} catch (error) {
console.log(error);
}
}, []);
const createBinaryFile = useCallback(async () => {
console.log(await invoker(async () => (
await gdrive.files.newMultipartUploader()
.setData([1, 2, 3, 4, 5], MimeTypes.BINARY)
.setRequestBody({
name: "bin",
//parents: ["folder_id"]
})
.execute()
)));
}, [invoker]);
const createIfNotExists = useCallback(async () => {
console.log(await invoker(async () => (
await gdrive.files.createIfNotExists(
{
q: new ListQueryBuilder()
.e("name", "condition_folder")
.and()
.e("mimeType", MimeTypes.FOLDER)
.and()
.in("root", "parents")
},
gdrive.files.newMetadataOnlyUploader()
.setRequestBody({
name: "condition_folder",
mimeType: MimeTypes.FOLDER,
parents: ["root"]
})
)
)));
}, [invoker]);
const createFolder = useCallback(async () => {
console.log(await invoker(async () => (
await gdrive.files.newMetadataOnlyUploader()
.setRequestBody({
name: "Folder",
mimeType: MimeTypes.FOLDER,
parents: ["root"]
})
.execute()
)));
}, [invoker]);
const createTextFile = useCallback(async () => {
console.log(await invoker(async () => {
return (await gdrive.files.newMultipartUploader()
.setData("cm9iaW4=", MimeTypes.TEXT)
.setIsBase64(true)
.setRequestBody({
name: "base64 text",
})
.execute()).id;
}));
}, [invoker]);
const emptyTrash = useCallback(async () => {
if (await invoker(async () => {
await gdrive.files.emptyTrash();
return true;
}))
{
console.log("Trash emptied");
};
}, [invoker]);
const getWebViewLink = useCallback(async () => {
console.log(await invoker(async () => (
await gdrive.files.getMetadata(
"some_id", {
fields: "webViewLink"
}
)
)));
}, [invoker]);
const readFiles = useCallback(async () => {
console.log(await invoker(async () => (
await gdrive.files.getText("text_file_id")
)));
console.log(await invoker(async () => (
await gdrive.files.getBinary("bin_file_id", null, "1-1")
)))
}, [invoker]);
useEffect(() => {
GoogleSignin.configure({
scopes: [
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/drive.appfolder"
]});
(async () => {
if (await invoker(async () => {
await GoogleSignin.signIn();
gdrive.accessToken = (await GoogleSignin.getTokens()).accessToken;
gdrive.files.fetchCoercesTypes = true;
gdrive.files.fetchRejectsOnHttpErrors = true;
gdrive.files.fetchTimeout = 1500;
return true;
}))
{
setUi([
["create bin file", createBinaryFile],
["create folder", createFolder],
["create if not exists", createIfNotExists],
["create text file", createTextFile],
["empty trash", emptyTrash],
["get webViewLink", getWebViewLink],
["read files", readFiles]
].map(([title, onPress], index) => (
<Button
key={index}
onPress={onPress}
title={title}
/>
)));
}
})();
}, [
createBinaryFile,
createFolder,
createIfNotExists,
createTextFile,
emptyTrash,
getWebViewLink,
readFiles,
invoker
]);
return (
<SafeAreaView
style={styles.container}
>
{ui}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: "cyan",
flex: 1,
justifyContent: "space-around",
padding: 25
}
});

Related

Multiple useEffect in react-native to achieve mentioned functionality

I need help with the async nature of Async storage and axios api. Here's the functionality that I am trying to achieve ->
send request to two separate api to get some data.
display that data on the screen with some additional text
api request are authenticated so a token is passed as Authentication Header
I have attached the current implementation, I am having the a number of errors in this
Errors:
Login_token not set in state after fetching from Async Storage.
Data not set in state after api call
both resulting in either failed api calls or undefined state errors on render
This is my code.
import React, { FunctionComponent, useEffect, useCallback, useState} from 'react';
import { StyleSheet, View} from 'react-native';
// chat
import { GiftedChat } from 'react-native-gifted-chat';
// navigation
import { RootStackParamList } from '../../navigators/RootStack';
import { StackScreenProps } from '#react-navigation/stack';
export type Props = StackScreenProps<RootStackParamList, "Chat">;
// api
import { Convo_details, Send_Msg, List_Msg, Expert_Public_Profile } from '../../api/UserApi';
import Spinner from 'react-native-loading-spinner-overlay';
import AsyncStorage from '#react-native-async-storage/async-storage';
import uuid from 'react-native-uuid';
const Chat: FunctionComponent<Props> = ({ navigation, route, ...props }) => {
// console.log(props.route.params);
const [login_token, setlogin_token] = useState('')
const [conversation_id, setconversation_id] = useState('')
const [conversation_details, setconversation_details] = useState({})
const [currentuser, setcurrentuser] = useState({})
const [loading, setLoading] = useState(false);
const [expertuid, setexpertuid] = useState('')
const [ExpertProfile, setExpertProfile] = useState({})
const [messages, setMessages] = useState([]);
useEffect(() => {
getlogintoken()
console.log("####################################","getlogintoken");
}, [])
/* conversationid */
useEffect(() => {
if (route.params != null) {
setconversation_id(route.params[0])
}
console.log("####################################","conversation id");
}, [])
/* expert uid */
useEffect(() => {
if (route.params != null) {
setexpertuid(route.params[1])
}
console.log("####################################","expert uid");
}, [])
/* expert public profile */
useEffect(() => {
getexpertpublicprofile()
getConvo_details()
console.log("####################################","convo_details");
}, [])
useEffect(() => {
// get current user
AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
console.log("####################################","current user");
}, [])
// set welcome msg
useEffect(() => {
if (Object.keys(conversation_details).length != 0 && Object.keys(ExpertProfile).length != 0)
setwelcomemsg()
}, [])
const onSend = useCallback(async (messages = []) => {
// console.log(messages[0].text);
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
const data = {
conversation_id: "f98d6851-a713-4f58-9118-77a779ff175f",//conversation_id,
message_type: "TEXT",
body: messages[0].text
}
const res: any = await Send_Msg(data, login_token)
.catch(error => {
alert(`Send_Msg -> ${error}`)
console.log(error);
return
})
if (res.status == 200) {
console.log(res.data);
} else console.log(res);
}, [])
const getexpertpublicprofile = async () => {
setLoading(true)
const res: any = await Expert_Public_Profile(expertuid, login_token)
.catch(error => {
setLoading(false)
console.log("Expert public profile ->");
alert(`Expert public profile ->${error.message}`)
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setExpertProfile(res.data)
else {
alert(`get expert public profile${res.data.message}`)
console.log("getexpertpublicprofile -->");
console.log(res.data);
}
}
const getlogintoken = () => {
AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getConvo_details = async () => {
setLoading(true)
const res: any = await Convo_details(conversation_id, login_token)
.catch(error => {
setLoading(false)
alert(`Convo_details-->${error.message}`)
console.log("Convo_details -->");
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setconversation_details(res.data)
else {
alert(`get convo details-> ${res.data.message}`)
console.log("getConvo_details -->");
console.log(res.data);
}
}
const setwelcomemsg = () => {
try {
let user = JSON.parse(currentuser)
let messages = [
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `About Me - ${ExpertProfile.bio}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `My name is ${conversation_details.recipient.name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `Hi ${user.full_name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
}]
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
} catch (error) {
console.log("try -> set welcome msg");
console.log(error);
return
}
}
return (
<View style={styles.maincontainer}>
<Spinner
visible={loading}
textContent={'Loading...'}
textStyle={{ color: '#FFF' }}
/>
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
user={{
_id: currentuser.user_uid,
}}
isTyping={false}
scrollToBottom={true}
showAvatarForEveryMessage={true}
renderAvatar={() => null}
/>
</View>
);
}
export default Chat;
const styles = StyleSheet.create({
maincontainer: {
flex: 1,
},
});
When axios returns, it usually give the response as res.data, so in your case, try either res.data or res.data.yourToken (I'm not sure how it's your object).
Gurav,
As far as your code above, The api call's will trigger even before you get currentuser or loginToken. You have to handle the api call after getting the currentuser and loginToken. This can be gracefully handled with async, await.
example code:
useEffect(() => {
getData()
}, [])
useEffect(() => {
if(login_token && currentuser) {
//The api call goes here after you get the logintoken andcurrentuser.
// The above condition is just an example but will vary based on your requirements
}
}, [login_token, currentuser])
const getData = async () => {
await getlogintoken()
await getcurrentuser()
}
const getlogintoken = async () => {
await AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getcurrentuser = async () => {
await AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
}

Secure random number generation is not supported by this browser

I am trying to establish a peer-to-peer video connection between a web frontend and a react-native android smart tv app. I want to display the user's webcam video on the smart tv. I am using an express server for signaling:
const app = require("express")();
const server = require("http").createServer(app);
const cors = require("cors");
const io = require("socket.io")(server, {
cors: {
origin: "*",
methods: [ "GET", "POST" ]
}
});
app.use(cors());
const PORT = process.env.PORT || 8082;
//here we define the behaviour of the API Endpoints
app.get('/', (req, res) => {
res.send('Runnin');
});
app.post('/',(req,res) => {
const body = req.body;
res.send(body);
});
io.on("connection", (socket) => {
socket.emit("me", socket.id);
console.log(socket.id);
socket.on("disconnect", () => {
socket.broadcast.emit("callEnded")
});
socket.on("callUser", ({ userToCall, signalData, from, name }) => {
io.to(userToCall).emit("callUser", { signal: signalData, from, name });
});
socket.on("answerCall", (data) => {
io.to(data.to).emit("callAccepted", data.signal)
});
});
server.listen(PORT, () => console.log(`Server is running on port ${PORT}`));
The signaling is working but as I am trying to display the video the following error is displayed:
[Link to screenshot of the error][1]
Code of the react-native Callscreen component:
import React, { useEffect, useState, useCallback, useContext } from 'react';
import { View, StyleSheet, Alert, Button, Text } from 'react-native';
import { RTCView } from 'react-native-webrtc';
import { SocketContext } from './Context';
import Callnotification from './Callnotifcation';
function CallScreen(props) {
const { callAccepted, userVideo, callEnded, me } = useContext(SocketContext);
return (
<>
{callAccepted && !callEnded && (
<View style={styles.root}>
<View style={[styles.videos, styles.remoteVideos]}>
<Text>Video of the caller</Text>
<RTCView streamURL={JSON.stringify(userVideo)} style={styles.remoteVideo} />
</View>
</View>
)
}
<View style={styles.root}>
<Callnotification />
<Text>{JSON.stringify(me)}</Text>
</View>
</>
);
}
Code of the Context.js connecting the react-native app to the signaling server:
import React, { createContext, useState, useRef, useEffect } from 'react';
import { io } from 'socket.io-client';
import Peer from 'simple-peer';
const SocketContext = createContext();
const socket = io('http://10.0.2.2:8082'); // use this to access via android emulator
//const socket = io('http://192.168.178.106:8082'); //use this to access via SmartTv Fire Tv Stick
const ContextProvider = ({ children }) => {
const [callAccepted, setCallAccepted] = useState(false);
const [callEnded, setCallEnded] = useState(false);
const [stream, setStream] = useState();
const [name, setName] = useState('');
const [call, setCall] = useState({});
const [me, setMe] = useState('');
const userVideo = useRef();
const connectionRef = useRef();
useEffect(() => {
socket.on('me', (id) => setMe(id));
socket.on('callUser', ({ from, name: callerName, signal }) => {
setCall({ isReceivingCall: true, from, name: callerName, signal });
});
}, []);
const answerCall = () => {
setCallAccepted(true);
const peer = new Peer({ initiator: false, trickle: false, stream });
peer.on('signal', (data) => {
socket.emit('answerCall', { signal: data, to: call.from });
});
peer.on('stream', (currentStream) => {
userVideo.current.srcObject = currentStream;
});
peer.signal(call.signal);
connectionRef.current = peer;
};
/* const callUser = (id) => {
const peer = new Peer({ initiator: true, trickle: false, stream });
peer.on('signal', (data) => {
socket.emit('callUser', { userToCall: id, signalData: data, from: me, name });
});
peer.on('stream', (currentStream) => {
userVideo.current.srcObject = currentStream;
});
socket.on('callAccepted', (signal) => {
setCallAccepted(true);
peer.signal(signal);
});
connectionRef.current = peer;
};*/
const leaveCall = () => {
setCallEnded(true);
connectionRef.current.destroy();
};
return (
<SocketContext.Provider value={{
call,
callAccepted,
setCallAccepted,
userVideo,
stream,
name,
setName,
callEnded,
me,
leaveCall,
answerCall,
}}
>
{children}
</SocketContext.Provider>
);
};
export { ContextProvider, SocketContext };
Code of the Context.js connecting the web react frontend to the signaling server:
import React, { createContext, useState, useRef, useEffect } from 'react';
import { io } from 'socket.io-client';
import Peer from 'simple-peer';
const SocketContext = createContext();
// const socket = io('http://localhost:5000');
const socket = io('http://localhost:8082');
const ContextProvider = ({ children }) => {
const [callAccepted, setCallAccepted] = useState(false);
const [callEnded, setCallEnded] = useState(false);
const [stream, setStream] = useState();
const [name, setName] = useState('');
const [call, setCall] = useState({});
const [me, setMe] = useState('');
const myVideo = useRef();
const userVideo = useRef();
const connectionRef = useRef();
useEffect(() => {
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then((currentStream) => {
setStream(currentStream);
myVideo.current.srcObject = currentStream;
});
socket.on('me', (id) => setMe(id));
socket.on('callUser', ({ from, name: callerName, signal }) => {
setCall({ isReceivingCall: true, from, name: callerName, signal });
});
}, []);
const answerCall = () => {
setCallAccepted(true);
const peer = new Peer({ initiator: false, trickle: false, stream });
peer.on('signal', (data) => {
socket.emit('answerCall', { signal: data, to: call.from });
});
peer.on('stream', (currentStream) => {
userVideo.current.srcObject = currentStream;
});
peer.signal(call.signal);
connectionRef.current = peer;
};
const callUser = (id) => {
const peer = new Peer({ initiator: true, trickle: false, stream });
peer.on('signal', (data) => {
socket.emit('callUser', { userToCall: id, signalData: data, from: me, name });
});
peer.on('stream', (currentStream) => {
userVideo.current.srcObject = currentStream;
});
socket.on('callAccepted', (signal) => {
setCallAccepted(true);
peer.signal(signal);
});
connectionRef.current = peer;
};
const leaveCall = () => {
setCallEnded(true);
connectionRef.current.destroy();
window.location.reload();
};
return (
<SocketContext.Provider value={{
call,
callAccepted,
myVideo,
userVideo,
stream,
name,
setName,
callEnded,
me,
callUser,
leaveCall,
answerCall,
}}
>
{children}
</SocketContext.Provider>
);
};
export { ContextProvider, SocketContext };
In my opinion the error lies in the RTCView of the Callscreen but i have no idea how to fix it. Your help is very much appreciated!
Thank you very much!
[1]: https://i.stack.imgur.com/YBh9P.jpg
The library that you are using is probably using the SubtleCrypto.generateKey function to generate shared secrets. This API is "only available in a secure context", which means that it can only be used if the page is served over HTTPS.
Serve your page over HTTPS, and the error should go away.

How to implement splash screen properly in a component which have hooks running?

Inside App.js I have auth validation (i am using useState, useMemo, useEffect) but when tried to impement splash screen and following Splas screen Dos I am getting Rendered more hooks than during the previous render. So following Rules of Hooks I put at top level useEffect and useState but now I am getting a new error Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function, in App I see I need to cancel async functions but I need them to request the server and validate users.
This is how my code was before implementing Splash screen:
export default function App() {
const [auth, setAuth] = useState(undefined);
useEffect(() => {
(async () => {
const token = await getTokenApi();
if (token) {
setAuth({
token,
idUser: jwtDecode(token).id,
});
} else {
setAuth(null);
}
})();
}, []);
const login = (user) => {
setTokenApi(user.jwt);
setAuth({
token: user.jwt,
idUser: user.user.id,
});
};
const logout = () => {
if (auth) {
removeTokenApi();
setAuth(null);
}
};
const authData = useMemo(
() => ({
auth,
login,
logout,
}),
[auth]
);
if (auth === undefined) return null;
return (
<AuthContext.Provider value={authData}>
<PaperProvider>{auth ? <AppNavigation /> : <Auth />}</PaperProvider>
</AuthContext.Provider>
);
This is how i got it now
export default function App() {
const [auth, setAuth] = useState(undefined);
useEffect(() => {
(async () => {
const token = await getTokenApi();
if (token) {
setAuth({
token,
idUser: jwtDecode(token).id,
});
} else {
setAuth(null);
}
})();
}, []);
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
await SplashScreen.preventAutoHideAsync();
await Font.loadAsync(Entypo.font);
await new Promise((resolve) => setTimeout(resolve, 4000));
} catch (e) {
console.warn(e);
} finally {
setAppIsReady(true);
}
}
prepare();
}, []);
const login = (user) => {
setTokenApi(user.jwt);
setAuth({
token: user.jwt,
idUser: user.user.id,
});
};
const logout = () => {
if (auth) {
removeTokenApi();
setAuth(null);
}
};
const authData = useMemo(
() => ({
auth,
login,
logout,
}),
[auth]
);
if (auth === undefined) return null;
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<View onLayout={onLayoutRootView}>
<AuthContext.Provider value={authData}>
<PaperProvider>{auth ? <AppNavigation /> : <Auth />}</PaperProvider>
</AuthContext.Provider>
</View>
);
}

Getting no response while using react-native-sqlite-storage ,

I am trying to execute update query but in response getting nothing.
const editData = async (no) => {
try {
db.transaction((tx) => {
tx.executeSql(
'UPDATE brief_History set para' +
no +
'=?,where id=?',
[value, 2],
(tx, results) => {
console.log('Results', results.rowsAffected);
if (results.rowsAffected > 0) {
Alert.alert('Record Updated Successfully...');
} else Alert.alert('Error');
},
);
});
} catch (err) {
console.log(err);
}
};
I was having the same problem, you need to verify if the Database was initialized in App.
Example using Expo
import React, { useEffect, useState } from 'react';
import AppLoading from 'expo-app-loading';
const App = () => {
const [InitializedDatabase, setInitializedDatabase] = useState(false);
useEffect(() => {
let tableValues = { table: "User", column: "Username TEXT, Password TEXT" };
createTable(tableValues).finally(() => setInitializedDatabase(true));
}, []);
if (!InitializedDatabase) {
return <AppLoading />;
}
return (
<View />
);
}
const createTable = (value) =>
new Promise((resolve, reject) =>
db.transaction((tx) =>
tx.executeSql(`CREATE TABLE IF NOT EXISTS ${value.table} (${value.column});`,
[],
(_, { rowsAffected, insertId }) => resolve(insertId),
(_, error) => reject(error)
)));

how do I make an array in state - reactnative

I'm taking the images I uploaded to cloud storage, but the problem is the variable is not an array, so it is only storing just one url. How do I make variables with state array?
My code:
const reference = storage().ref('images');
const [imageUrl, setImageUrl] = useState();
const refer = storage().ref('images');
useEffect(() => {
try {
listFilesAndDirectories(reference).then(() => {
console.log('Finished listing');
});
refer.list().then(result => {
result.items.forEach(element => {
element.getDownloadURL().then(downloadUrl => {
setImageUrl(downloadUrl)
console.log(imageUrl)
console.log("=================")
}).catch(error =>{
alert(error)
})
})
})
} catch (error) {
alert(error);
}
}, []);
Is that what you are looking for?
const [items, setItems] = useState([]);
const handleStateChange = () => {
setItems(state => [...state, 'someNewItem']);
}
With useCallback
const handleStateChange = useCallback(function () {
setItems(state => [...state, 'someNewItem']);
}, [])