Profile Cache and Profile Header Started
This commit is contained in:
@@ -4,22 +4,50 @@ import { View, ActivityIndicator, StyleSheet, SafeAreaView, FlatList } from 'rea
|
|||||||
import API from './../API.js';
|
import API from './../API.js';
|
||||||
import Post from './../components/Post.js';
|
import Post from './../components/Post.js';
|
||||||
import NewPost from "./../components/NewPost.js";
|
import NewPost from "./../components/NewPost.js";
|
||||||
|
import ProfileHeader from '../components/ProfileHeader.js';
|
||||||
|
|
||||||
|
const storeProfilePosts = async (profileid, value) => {
|
||||||
|
try {
|
||||||
|
const jsonValue = JSON.stringify(value)
|
||||||
|
await AsyncStorage.setItem('profile_' + profileid, jsonValue)
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProfilePosts = async (profileid) => {
|
||||||
|
try {
|
||||||
|
const value = await AsyncStorage.getItem('profile_' + profileid)
|
||||||
|
if (value !== null) {
|
||||||
|
return JSON.parse(value);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let Profile = ({ navigation, route }) => {
|
let Profile = ({ navigation, route }) => {
|
||||||
let [Me, setMeProfile] = useState({});
|
let [Me, setMeProfile] = useState({});
|
||||||
let [Posts, setPosts] = useState([]);
|
let [Posts, setPosts] = useState([]);
|
||||||
|
let [profile, setProfile] = useState({});
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(async () => {
|
||||||
setPosts([]);
|
setPosts([]);
|
||||||
let r = await API.getMe();
|
let r = await API.getMe();
|
||||||
setMeProfile(r);
|
setMeProfile(r);
|
||||||
if (route.params && route.params.profileid) {
|
if (route.params && route.params.profileid) {
|
||||||
API.getUserProfile(route.params.profileid).then(({ profile }) => {
|
console.log('Loading Cache Profile:' + route.params.profileid);
|
||||||
|
getProfilePosts(route.params.profileid).then(setPosts);
|
||||||
|
console.log('Loaded Cache Profile:' + route.params.profileid);
|
||||||
|
API.getUserProfile(route.params.profileid).then((profileObj) => {
|
||||||
|
let profile = profileObj.profile
|
||||||
|
setProfile(profileObj);
|
||||||
navigation.setOptions({ title: profile.firstName + " " + profile.lastName });
|
navigation.setOptions({ title: profile.firstName + " " + profile.lastName });
|
||||||
});
|
});
|
||||||
API.getPosts(route.params.profileid).then((data) => {
|
API.getPosts(route.params.profileid).then((data) => {
|
||||||
setPosts(data);
|
setPosts(data);
|
||||||
|
storeProfilePosts(route.params.profileid, data);
|
||||||
|
console.log('Store Cache Profile:' + route.params.profileid);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
navigation.navigate('Feed')
|
navigation.navigate('Feed')
|
||||||
@@ -30,6 +58,12 @@ let Profile = ({ navigation, route }) => {
|
|||||||
return (<></>);
|
return (<></>);
|
||||||
return (<Post post={item} viewer={Me} />);
|
return (<Post post={item} viewer={Me} />);
|
||||||
});
|
});
|
||||||
|
const header = (
|
||||||
|
<View>
|
||||||
|
<ProfileHeader profileObj={profile} key={profile._id} />
|
||||||
|
<NewPost newPostCB={(newPost) => setPosts([newPost, ...Posts])} />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
@@ -40,7 +74,7 @@ let Profile = ({ navigation, route }) => {
|
|||||||
data={Posts}
|
data={Posts}
|
||||||
renderItem={renderPost}
|
renderItem={renderPost}
|
||||||
keyExtractor={item => item._id || item.createdAt}
|
keyExtractor={item => item._id || item.createdAt}
|
||||||
ListHeaderComponent={<NewPost newPostCB={(newPost) => setPosts([newPost, ...Posts])} />}
|
ListHeaderComponent={header}
|
||||||
refreshing={Posts.length === 0}
|
refreshing={Posts.length === 0}
|
||||||
onRefresh={() => {
|
onRefresh={() => {
|
||||||
API.getPosts(route.params.profileid).then(setPosts);
|
API.getPosts(route.params.profileid).then(setPosts);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { Text, View, ScrollView, Image, StyleSheet } from 'react-native';
|
import { Text, View, ScrollView, Image, StyleSheet } from 'react-native';
|
||||||
import API from './../API.js';
|
import API from './../API.js';
|
||||||
import VideoPlayer from './VideoPlayer.js';
|
import VideoPlayer from './VideoPlayer.js';
|
||||||
|
import VimeoPlayer from './VimeoPlayer.js';
|
||||||
|
|
||||||
const videoIdF = (content) => {
|
const videoIdF = (content) => {
|
||||||
let vimeoTag = content.match(/@vimeo:[0-9]+/);
|
let vimeoTag = content.match(/@vimeo:[0-9]+/);
|
||||||
@@ -39,13 +40,13 @@ let Media = (props) => {
|
|||||||
const imageStyle = imagesTag.length == 1 ? styles.image : styles.multipleImage;
|
const imageStyle = imagesTag.length == 1 ? styles.image : styles.multipleImage;
|
||||||
const videosId = videoIdF(props.content);
|
const videosId = videoIdF(props.content);
|
||||||
let [videosFiles, setVideosFiles] = useState([]);
|
let [videosFiles, setVideosFiles] = useState([]);
|
||||||
useEffect(async ()=>{
|
useEffect(async () => {
|
||||||
if(!videosId[1]) return 0;
|
if (!videosId[1]) return 0;
|
||||||
let videoObj = await API.getVideo(videosId[1]);
|
let videoObj = await API.getVideo(videosId[1]);
|
||||||
setVideosFiles(videoObj.files);
|
setVideosFiles(videoObj.files || []);
|
||||||
}, [props.content])
|
}, [props.content])
|
||||||
//const vimeo = videosId.length ? <VimeoPlayer videoId={videosId[1]} /> : undefined;
|
const vimeo = videosFiles.length ? <VideoPlayer videosFiles={videosFiles} /> :
|
||||||
const vimeo = videosFiles.length ? <VideoPlayer videosFiles={videosFiles} /> : null;
|
(videosId.length ? <VimeoPlayer videoId={videosId[1]} /> : <></>);
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<View style={{ flexDirection: "row" }}>
|
<View style={{ flexDirection: "row" }}>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Text, ScrollView, FlatList, StyleSheet, View } from 'react-native';
|
import { Text, ScrollView, FlatList, StyleSheet, View, Linking } from 'react-native';
|
||||||
|
import Hyperlink from 'react-native-hyperlink'
|
||||||
import { Avatar, Button, Card, Title, Chip } from 'react-native-paper';
|
import { Avatar, Button, Card, Title, Chip } from 'react-native-paper';
|
||||||
import API from './../API.js';
|
import API from './../API.js';
|
||||||
import UserName from './UserName.js';
|
import UserName from './UserName.js';
|
||||||
@@ -8,7 +9,6 @@ import Comment from "./Comment";
|
|||||||
import NewComment from './NewComment.js';
|
import NewComment from './NewComment.js';
|
||||||
import Moment from 'moment';
|
import Moment from 'moment';
|
||||||
|
|
||||||
|
|
||||||
let Post = (props) => {
|
let Post = (props) => {
|
||||||
const viewer = props.viewer;
|
const viewer = props.viewer;
|
||||||
let [showCommentsB, changeshowCommentsB] = useState(false);
|
let [showCommentsB, changeshowCommentsB] = useState(false);
|
||||||
@@ -18,6 +18,7 @@ let Post = (props) => {
|
|||||||
let toProfileText = post.toProfile && post.toProfile !== post.profileid ?
|
let toProfileText = post.toProfile && post.toProfile !== post.profileid ?
|
||||||
<Text> {">"} <UserName profileid={post.toProfile} /></Text> : undefined;
|
<Text> {">"} <UserName profileid={post.toProfile} /></Text> : undefined;
|
||||||
let cleanContent = post.content.replace(/@[A-z]+:.+\w/g, '');
|
let cleanContent = post.content.replace(/@[A-z]+:.+\w/g, '');
|
||||||
|
//cleanContent = convertLinks(cleanContent);
|
||||||
const newComentAdded = (commentData) => {
|
const newComentAdded = (commentData) => {
|
||||||
let newPostObj = { ...post };
|
let newPostObj = { ...post };
|
||||||
newPostObj.comments.push(commentData);
|
newPostObj.comments.push(commentData);
|
||||||
@@ -54,6 +55,7 @@ let Post = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
|
<Hyperlink linkDefault={ true }>
|
||||||
{!post.nonOrganicType ?
|
{!post.nonOrganicType ?
|
||||||
<View>
|
<View>
|
||||||
<Text style={styles.userName}>
|
<Text style={styles.userName}>
|
||||||
@@ -71,6 +73,7 @@ let Post = (props) => {
|
|||||||
<Text style={{ fontSize: 18 }}>{cleanContent}</Text>
|
<Text style={{ fontSize: 18 }}>{cleanContent}</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
|
</Hyperlink>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
<Card.Actions style={{ flexDirection: "row", flow: 4, justifyContent: 'space-evenly', fontSize: 18 }}>
|
<Card.Actions style={{ flexDirection: "row", flow: 4, justifyContent: 'space-evenly', fontSize: 18 }}>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
35
components/ProfileHeader.js
Normal file
35
components/ProfileHeader.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import { Avatar, Button, Card, Title, Paragraph } from 'react-native-paper';
|
||||||
|
import UserName from './UserName';
|
||||||
|
|
||||||
|
const DefaultPhoto = "https://social.emmint.com/uploads/e6f9be6d665dc43417701bf16a90122c.png";
|
||||||
|
|
||||||
|
const ProfileHeader = ({ profileObj }) => {
|
||||||
|
let photoUrl = profileObj.profile.photo ? 'https://social.emmint.com/' + profileObj.profile.photo : DefaultPhoto;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Card elevation={3}>
|
||||||
|
<Card.Cover source={{ uri: photoUrl, cache: 'force-cache' }} />
|
||||||
|
<Card.Content>
|
||||||
|
<Title>
|
||||||
|
<UserName profileid={profileObj._id} />
|
||||||
|
</Title>
|
||||||
|
<Paragraph>{profileObj.profile.description}</Paragraph>
|
||||||
|
<Button mode='outlined'>Follow</Button>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
{/*
|
||||||
|
<Card.Title
|
||||||
|
title={<UserName profileid={profileObj._id} />}
|
||||||
|
subtitle={profileObj.profile.description}
|
||||||
|
left={(props) => <Avatar.Image {...props} source={{ uri: 'https://social.emmint.com/' + profileObj.profile.photo }} />}
|
||||||
|
right={(props) => <IconButton {...props} icon="more-vert" onPress={() => { }} />}
|
||||||
|
/>
|
||||||
|
*/}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(ProfileHeader);
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Text, View, ScrollView, Button, StyleSheet } from 'react-native';
|
import { Text, StyleSheet } from 'react-native';
|
||||||
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
import API from './../API.js';
|
import API from './../API.js';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
|
||||||
const storeName = async (key, value) => {
|
const storeName = async (key, value) => {
|
||||||
try {
|
try {
|
||||||
const jsonValue = JSON.stringify(value)
|
const jsonValue = JSON.stringify(value)
|
||||||
await AsyncStorage.setItem('Name_'+key, jsonValue)
|
await AsyncStorage.setItem('Name_' + key, jsonValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getName = async (key) => {
|
const getName = async (key) => {
|
||||||
try {
|
try {
|
||||||
const value = await AsyncStorage.getItem('Name_'+key)
|
const value = await AsyncStorage.getItem('Name_' + key)
|
||||||
if (value !== null) {
|
if (value !== null) {
|
||||||
return JSON.parse(value);
|
return JSON.parse(value);
|
||||||
}
|
}
|
||||||
@@ -24,25 +24,33 @@ const getName = async (key) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let UserName = ({profileid}) => {
|
let UserName = ({ profileid }) => {
|
||||||
let [profile, setProfile] = useState({});
|
let [profile, setProfile] = useState({});
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(async () => {
|
||||||
let cacheProfile = await getName(profileid);
|
let cacheProfile = await getName(profileid);
|
||||||
if(cacheProfile && cacheProfile.profile) setProfile(cacheProfile);
|
if (cacheProfile && cacheProfile.profile) setProfile(cacheProfile);
|
||||||
let p = await API.getUserProfile(profileid).catch(() => { return {} });
|
let p = await API.getUserProfile(profileid).catch(() => { return {} });
|
||||||
setProfile(p);
|
setProfile(p);
|
||||||
storeName(profileid, p)
|
storeName(profileid, p)
|
||||||
}, [profileid]);
|
}, [profileid]);
|
||||||
|
|
||||||
const onPress = ()=>{
|
let icon = profile._id ? (!profile.isGroup ? "person-outline" : "group") : '';
|
||||||
|
icon = icon === "person-outline" && profile.subscription && profile.subscription > (new Date() - 0) ? "assignment-ind" : icon;
|
||||||
|
icon = icon === "group" && profile.isCourse ? "subscriptions" : icon;
|
||||||
|
|
||||||
|
const onPress = () => {
|
||||||
return navigation.navigate('Profile', { profileid })
|
return navigation.navigate('Profile', { profileid })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text onPress={onPress}>
|
<Text onPress={onPress}>
|
||||||
{profile.profile && profile.profile.firstName} {profile.profile && profile.profile.lastName}
|
<Text style={{ paddingTop: 10 }}>
|
||||||
|
<Icon name={icon} size={18} />
|
||||||
|
</Text>
|
||||||
|
<Text> {profile.profile && profile.profile.firstName} {profile.profile && profile.profile.lastName}</Text>
|
||||||
|
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
27
package-lock.json
generated
27
package-lock.json
generated
@@ -5452,6 +5452,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||||
},
|
},
|
||||||
|
"linkify-it": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
|
||||||
|
"requires": {
|
||||||
|
"uc.micro": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"locate-path": {
|
"locate-path": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||||
@@ -5601,6 +5609,11 @@
|
|||||||
"buffer-alloc": "^1.1.0"
|
"buffer-alloc": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mdurl": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
|
||||||
|
},
|
||||||
"merge-options": {
|
"merge-options": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
|
||||||
@@ -7008,6 +7021,15 @@
|
|||||||
"nullthrows": "^1.1.1"
|
"nullthrows": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-native-hyperlink": {
|
||||||
|
"version": "0.0.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-hyperlink/-/react-native-hyperlink-0.0.19.tgz",
|
||||||
|
"integrity": "sha512-x4wuRGDMnnpWcRr5MCK1D2UcEuzD9IHK8lfjEhO/+QqXNaX31HdeD3ss3BXXZgHxpRYtLbTB0TuFcl1HHANp3w==",
|
||||||
|
"requires": {
|
||||||
|
"linkify-it": "^2.2.0",
|
||||||
|
"mdurl": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-native-iphone-x-helper": {
|
"react-native-iphone-x-helper": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
|
||||||
@@ -8305,6 +8327,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
|
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
|
||||||
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
|
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
|
||||||
},
|
},
|
||||||
|
"uc.micro": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
|
||||||
|
},
|
||||||
"uglify-es": {
|
"uglify-es": {
|
||||||
"version": "3.3.9",
|
"version": "3.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -10,28 +10,29 @@
|
|||||||
"eject": "expo eject"
|
"eject": "expo eject"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-native-async-storage/async-storage": "~1.15.0",
|
||||||
"@react-navigation/material-bottom-tabs": "^6.1.1",
|
"@react-navigation/material-bottom-tabs": "^6.1.1",
|
||||||
"@react-navigation/native": "^6.0.8",
|
"@react-navigation/native": "^6.0.8",
|
||||||
"@react-navigation/native-stack": "^6.5.0",
|
"@react-navigation/native-stack": "^6.5.0",
|
||||||
"expo": "~43.0.2",
|
"expo": "~43.0.2",
|
||||||
|
"expo-av": "~10.1.3",
|
||||||
|
"expo-device": "~4.0.3",
|
||||||
|
"expo-notifications": "~0.13.3",
|
||||||
"expo-status-bar": "~1.1.0",
|
"expo-status-bar": "~1.1.0",
|
||||||
|
"expo-updates": "~0.10.15",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-dom": "17.0.1",
|
"react-dom": "17.0.1",
|
||||||
"react-google-material-icons": "^1.0.4",
|
"react-google-material-icons": "^1.0.4",
|
||||||
"react-native": "0.64.3",
|
"react-native": "0.64.3",
|
||||||
"react-native-autoheight-webview": "^1.6.1",
|
"react-native-autoheight-webview": "^1.6.1",
|
||||||
|
"react-native-hyperlink": "0.0.19",
|
||||||
"react-native-paper": "^4.11.2",
|
"react-native-paper": "^4.11.2",
|
||||||
"react-native-safe-area-context": "^4.1.2",
|
"react-native-safe-area-context": "^4.1.2",
|
||||||
"react-native-screens": "^3.13.1",
|
"react-native-screens": "^3.13.1",
|
||||||
"react-native-vector-icons": "^9.1.0",
|
"react-native-vector-icons": "^9.1.0",
|
||||||
"react-native-web": "0.17.1",
|
"react-native-web": "0.17.1",
|
||||||
"react-native-webview": "^11.17.2",
|
"react-native-webview": "^11.17.2"
|
||||||
"@react-native-async-storage/async-storage": "~1.15.0",
|
|
||||||
"expo-av": "~10.1.3",
|
|
||||||
"expo-notifications": "~0.13.3",
|
|
||||||
"expo-device": "~4.0.3",
|
|
||||||
"expo-updates": "~0.10.15"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.9"
|
"@babel/core": "^7.12.9"
|
||||||
|
|||||||
Reference in New Issue
Block a user