Compare commits
10 Commits
fce958fbfc
...
fe9fc8e3e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe9fc8e3e4 | ||
|
|
8cdcfefa0d | ||
|
|
f5c7ff38dd | ||
|
|
733c1fd793 | ||
|
|
91e9740159 | ||
|
|
a1b2143337 | ||
|
|
ad394a6f18 | ||
|
|
0ae13c9ffe | ||
|
|
13406f2774 | ||
|
|
8f62e4b953 |
18
API.js
18
API.js
@@ -1,4 +1,5 @@
|
|||||||
const baseUrl = "https://api.emmint.com";
|
|
||||||
|
const baseUrl = "https://emiapi.reynafamily.com";
|
||||||
//const baseUrl = "http://localhost:3000";
|
//const baseUrl = "http://localhost:3000";
|
||||||
|
|
||||||
let getCall = async (path = "", params = {}) => {
|
let getCall = async (path = "", params = {}) => {
|
||||||
@@ -6,7 +7,8 @@ let getCall = async (path = "", params = {}) => {
|
|||||||
Object.keys(params).forEach(p => {
|
Object.keys(params).forEach(p => {
|
||||||
queryParams += p + "=" + params[p] + "&"
|
queryParams += p + "=" + params[p] + "&"
|
||||||
});
|
});
|
||||||
return fetch(baseUrl + path + queryParams, {
|
let localBaseUrl = global.baseUrl ?? baseUrl;
|
||||||
|
return fetch(localBaseUrl + path + queryParams, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@@ -15,6 +17,7 @@ let getCall = async (path = "", params = {}) => {
|
|||||||
}
|
}
|
||||||
}).then(response => response.json()).catch((error) => {
|
}).then(response => response.json()).catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
console.trace();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +26,8 @@ let deleteCall = async (path = "", params = {}) => {
|
|||||||
Object.keys(params).forEach(p => {
|
Object.keys(params).forEach(p => {
|
||||||
queryParams += p + "=" + params[p] + "&"
|
queryParams += p + "=" + params[p] + "&"
|
||||||
});
|
});
|
||||||
return fetch(baseUrl + path + queryParams, {
|
let localBaseUrl = global.baseUrl ?? baseUrl;
|
||||||
|
return fetch(localBaseUrl + path + queryParams, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@@ -32,11 +36,13 @@ let deleteCall = async (path = "", params = {}) => {
|
|||||||
}
|
}
|
||||||
}).then(response => response.json()).catch((error) => {
|
}).then(response => response.json()).catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
console.trace();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let postCall = async (path, params) => {
|
let postCall = async (path, params) => {
|
||||||
return fetch(baseUrl + path, {
|
let localBaseUrl = global.baseUrl ?? baseUrl;
|
||||||
|
return fetch(localBaseUrl + path, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@@ -46,6 +52,7 @@ let postCall = async (path, params) => {
|
|||||||
}
|
}
|
||||||
}).then(response => response.json()).catch((error) => {
|
}).then(response => response.json()).catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
console.trace();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +143,9 @@ const API = {
|
|||||||
if (userid) return getCall("/post/usr/" + userid);
|
if (userid) return getCall("/post/usr/" + userid);
|
||||||
return getCall("/post/");
|
return getCall("/post/");
|
||||||
},
|
},
|
||||||
|
getPostsByTag(tag) {
|
||||||
|
return getCall("/post/tag/" + tag);
|
||||||
|
},
|
||||||
getPostsWithTag(userid, tag = "images") {
|
getPostsWithTag(userid, tag = "images") {
|
||||||
if (userid) return getCall("/post/usr/" + userid + "/" + tag);
|
if (userid) return getCall("/post/usr/" + userid + "/" + tag);
|
||||||
return getCall("/post/" + tag);
|
return getCall("/post/" + tag);
|
||||||
|
|||||||
26
App.js
26
App.js
@@ -8,6 +8,7 @@ import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
|||||||
import Login from "./Views/Login.js"
|
import Login from "./Views/Login.js"
|
||||||
import Feed from "./Views/Feed.js"
|
import Feed from "./Views/Feed.js"
|
||||||
import Profile from "./Views/Profile.js"
|
import Profile from "./Views/Profile.js"
|
||||||
|
import Tags from "./Views/Tags.js"
|
||||||
import Search from './Views/Search.js';
|
import Search from './Views/Search.js';
|
||||||
import Groups from './Views/Groups.js';
|
import Groups from './Views/Groups.js';
|
||||||
import Courses from './Views/Courses.js';
|
import Courses from './Views/Courses.js';
|
||||||
@@ -31,6 +32,7 @@ import SongPlayer from './Views/SongPlayer.js';
|
|||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
import { PostHogProvider } from 'posthog-react-native'
|
import { PostHogProvider } from 'posthog-react-native'
|
||||||
import * as Updates from 'expo-updates';
|
import * as Updates from 'expo-updates';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
|
||||||
|
|
||||||
const Tab = createBottomTabNavigator();
|
const Tab = createBottomTabNavigator();
|
||||||
@@ -95,6 +97,7 @@ const MainNavigation = ({ route }) => {
|
|||||||
const [notification, setNotification] = useState(false);
|
const [notification, setNotification] = useState(false);
|
||||||
const notificationListener = useRef();
|
const notificationListener = useRef();
|
||||||
const responseListener = useRef();
|
const responseListener = useRef();
|
||||||
|
const mainNavigation = useNavigation();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
registerForPushNotificationsAsync().then(async (token) => {
|
registerForPushNotificationsAsync().then(async (token) => {
|
||||||
let isLoggedIn = await API.isLoggedIn();
|
let isLoggedIn = await API.isLoggedIn();
|
||||||
@@ -105,14 +108,27 @@ const MainNavigation = ({ route }) => {
|
|||||||
|
|
||||||
// This listener is fired whenever a notification is received while the app is foregrounded
|
// This listener is fired whenever a notification is received while the app is foregrounded
|
||||||
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
|
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
|
||||||
//console.log("got notif", notification)
|
//console.log("got notif", notification);
|
||||||
setNotification(notification);
|
setNotification(notification);
|
||||||
});
|
});
|
||||||
|
|
||||||
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
|
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
|
||||||
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
|
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
|
||||||
//console.log("got notif click", notification)
|
const data = response.notification.request.content.data;
|
||||||
console.log(response);
|
if (data && Object.keys(data).length > 0) {
|
||||||
|
try {
|
||||||
|
if (data.profile_id) {
|
||||||
|
mainNavigation.navigate("Profile", { profileid: data.profile_id });
|
||||||
|
}
|
||||||
|
if (data.post_id) {
|
||||||
|
mainNavigation.navigate("SinglePost", { postid: data.post_id });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert("Error: " + error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//alert("Notification clicked but no data found.");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
@@ -291,6 +307,10 @@ export default function App() {
|
|||||||
name="Profile"
|
name="Profile"
|
||||||
component={Profile}
|
component={Profile}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="Tags"
|
||||||
|
component={Tags}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="NewPost"
|
name="NewPost"
|
||||||
component={NewPostView}
|
component={NewPostView}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { useSnapshot } from 'valtio';
|
|||||||
import GlobalState from '../contexts/GlobalState.js';
|
import GlobalState from '../contexts/GlobalState.js';
|
||||||
import ProfileCardHorizontal from "../components/ProfileCardHorizontal.js";
|
import ProfileCardHorizontal from "../components/ProfileCardHorizontal.js";
|
||||||
import { reloadAppAsync } from "expo";
|
import { reloadAppAsync } from "expo";
|
||||||
|
import * as Updates from 'expo-updates';
|
||||||
|
|
||||||
|
|
||||||
let MenuView = ({ navigation }) => {
|
let MenuView = ({ navigation }) => {
|
||||||
@@ -93,6 +94,10 @@ let MenuView = ({ navigation }) => {
|
|||||||
<RadioButton.Item value="fr" label="French" />
|
<RadioButton.Item value="fr" label="French" />
|
||||||
</RadioButton.Group>
|
</RadioButton.Group>
|
||||||
</View>
|
</View>
|
||||||
|
<View style={{ padding: 10, alignContent: "center", flex: 1 }}>
|
||||||
|
<Text>Version: {Updates.runtimeVersion}</Text>
|
||||||
|
<Text>Channel: {Updates.Channel}</Text>
|
||||||
|
</View>
|
||||||
</ImageBackground>
|
</ImageBackground>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ let NotificationsView = ({ navigation, route }) => {
|
|||||||
<Text style={{ fontWeight: 'normal', fontSize: 12 }}>
|
<Text style={{ fontWeight: 'normal', fontSize: 12 }}>
|
||||||
{" " + Moment(item.ts).fromNow()}
|
{" " + Moment(item.ts).fromNow()}
|
||||||
</Text>
|
</Text>
|
||||||
<SinglePost postId={item.postid} />
|
<SinglePost postId={item.postid} hideComments={true} />
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
|||||||
75
Views/Tags.js
Normal file
75
Views/Tags.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { View, Text, StyleSheet, SafeAreaView, FlatList } from 'react-native';
|
||||||
|
import API from './../API.js';
|
||||||
|
import Post from './../components/Post.js';
|
||||||
|
|
||||||
|
let Tags = ({ navigation, route }) => {
|
||||||
|
let [Posts, setPosts] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let subscribed = true;
|
||||||
|
const getData = async () => {
|
||||||
|
setPosts([]);
|
||||||
|
console.log("Posts by tag", route.params.tag);
|
||||||
|
API.getPostsByTag(route.params.tag).then((data) => {
|
||||||
|
if(!subscribed) return 0;
|
||||||
|
console.log("Posts by tag", data);
|
||||||
|
setPosts(data);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getData();
|
||||||
|
return ()=>{
|
||||||
|
subscribed = false;
|
||||||
|
}
|
||||||
|
}, [route.params?.tag]);
|
||||||
|
|
||||||
|
const renderPost = (({ item }) => {
|
||||||
|
if (item.nonOrganicType)
|
||||||
|
return (<></>);
|
||||||
|
return (<Post post={item} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
const header = (
|
||||||
|
<View>
|
||||||
|
<Text style={{ fontSize: 20, fontWeight: 'bold', padding: 10, alignContent: 'center', textAlign: 'center' }}>
|
||||||
|
#{route.params.tag}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<View>
|
||||||
|
{!loading ?
|
||||||
|
<FlatList
|
||||||
|
data={Posts}
|
||||||
|
renderItem={renderPost}
|
||||||
|
keyExtractor={item => item.lastUpdated || item._id || item.ceatedAt}
|
||||||
|
ListHeaderComponent={header}
|
||||||
|
refreshing={loading}
|
||||||
|
initialNumToRender={3}
|
||||||
|
maxToRenderPerBatch={3}
|
||||||
|
removeClippedSubviews={true}
|
||||||
|
onRefresh={() => {
|
||||||
|
API.getPostsByTag(route.params.tag).then(setPosts);
|
||||||
|
}}
|
||||||
|
/> :
|
||||||
|
<></> //TODO: Add empty profile card here
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
<StatusBar style="auto" />
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Tags;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#edf2f7",
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -10,6 +10,7 @@ import Moment from 'moment';
|
|||||||
import i18n from "../i18nMessages.js";
|
import i18n from "../i18nMessages.js";
|
||||||
import 'moment/min/locales';
|
import 'moment/min/locales';
|
||||||
Moment.locale(i18n.locale);
|
Moment.locale(i18n.locale);
|
||||||
|
import ProfilePhotoCircle from './ProfilePhotoCircle.js';
|
||||||
|
|
||||||
let Comment = ({ comment, postid }) => {
|
let Comment = ({ comment, postid }) => {
|
||||||
const gState = useSnapshot(GlobalState);
|
const gState = useSnapshot(GlobalState);
|
||||||
@@ -19,7 +20,7 @@ let Comment = ({ comment, postid }) => {
|
|||||||
const newCommentReaction = () => {
|
const newCommentReaction = () => {
|
||||||
if (!comment.reactions[viewer._id]) {
|
if (!comment.reactions[viewer._id]) {
|
||||||
comment.reactions[viewer._id] = { type: "like" };
|
comment.reactions[viewer._id] = { type: "like" };
|
||||||
changeLikes(likes+1);
|
changeLikes(likes + 1);
|
||||||
API.newCommentReaction(postid, comment.createdAt);
|
API.newCommentReaction(postid, comment.createdAt);
|
||||||
} else {
|
} else {
|
||||||
//API.removePostReaction(viewer._id).then(() => {
|
//API.removePostReaction(viewer._id).then(() => {
|
||||||
@@ -31,14 +32,20 @@ let Comment = ({ comment, postid }) => {
|
|||||||
return (
|
return (
|
||||||
<Card style={styles.comment}>
|
<Card style={styles.comment}>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<View style={{flexDirection: "row", alignItems: "center", justifyContent: "center"}}>
|
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "center" }}>
|
||||||
<View style={{flex:8}}>
|
<View style={{ flex: 8, marginBottom: 8 }}>
|
||||||
<Text style={styles.userName}>
|
<ProfilePhotoCircle profileid={comment.profileid} />
|
||||||
<UserName profileid={comment.profileid} key={comment.profileid} />
|
<Text style={
|
||||||
<Text style={{fontSize: 12, fontWeight: "normal"}}> {Moment(comment.createdAt).fromNow()}</Text>
|
{
|
||||||
</Text>
|
fontSize: 12,
|
||||||
|
fontWeight: "normal",
|
||||||
|
position: "absolute",
|
||||||
|
top: 20,
|
||||||
|
left: 37,
|
||||||
|
}
|
||||||
|
}> {Moment(comment.createdAt).fromNow()}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={{flex:2}}>
|
<View style={{ flex: 2 }}>
|
||||||
<Button
|
<Button
|
||||||
icon={comment.reactions[viewer._id] ? "favorite" : "favorite-border"}
|
icon={comment.reactions[viewer._id] ? "favorite" : "favorite-border"}
|
||||||
dense={true}
|
dense={true}
|
||||||
@@ -46,8 +53,8 @@ let Comment = ({ comment, postid }) => {
|
|||||||
>{likes ? likes : ''}</Button>
|
>{likes ? likes : ''}</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Text style={{fontSize: 14}}>{cleanContent}</Text>
|
<Text style={{ fontSize: 14 }}>{cleanContent}</Text>
|
||||||
<Media content={comment.content} postId={postid} skiptVideo={true}/>
|
<Media content={comment.content} postId={postid} skiptVideo={true} />
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
@@ -60,12 +67,6 @@ const styles = StyleSheet.create({
|
|||||||
margin: 8,
|
margin: 8,
|
||||||
marginTop: 0,
|
marginTop: 0,
|
||||||
},
|
},
|
||||||
userName: {
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
marginBottom: 5,
|
|
||||||
fontSize: 16
|
|
||||||
},
|
|
||||||
likeComment: {
|
likeComment: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
margin: 16,
|
margin: 16,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Text, ScrollView, FlatList, StyleSheet, View, Share } from 'react-native';
|
import { Text, Pressable, FlatList, StyleSheet, View, Share, Alert, Linking } from 'react-native';
|
||||||
import Hyperlink from 'react-native-hyperlink'
|
import Hyperlink from 'react-native-hyperlink'
|
||||||
import { Button, Card, Chip } from 'react-native-paper';
|
import { Button, Card, Chip } from 'react-native-paper';
|
||||||
import API from './../API.js';
|
import API from './../API.js';
|
||||||
@@ -13,18 +13,21 @@ import GlobalState from '../contexts/GlobalState.js';
|
|||||||
import i18n from "../i18nMessages.js";
|
import i18n from "../i18nMessages.js";
|
||||||
import ProfilePhotoCircle from './ProfilePhotoCircle.js';
|
import ProfilePhotoCircle from './ProfilePhotoCircle.js';
|
||||||
import { posthog } from './../PostHog.js';
|
import { posthog } from './../PostHog.js';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import ParsedText from 'react-native-parsed-text';
|
||||||
|
|
||||||
|
|
||||||
let Post = (props) => {
|
let Post = (props) => {
|
||||||
const gState = useSnapshot(GlobalState);
|
const gState = useSnapshot(GlobalState);
|
||||||
const viewer = gState.me;
|
const viewer = gState.me;
|
||||||
let [showCommentsB, changeshowCommentsB] = useState(false);
|
let [showCommentsB, changeshowCommentsB] = useState(props.showComments || false);
|
||||||
let [post, changePost] = useState(props.post);
|
let [post, changePost] = useState(props.post);
|
||||||
let [likes, changeLikes] = useState(Object.keys(post.reactions).length);
|
let [likes, changeLikes] = useState(Object.keys(post.reactions).length);
|
||||||
let [bookmarked, changeBookmarked] = useState(post.bookmarks && post.bookmarks.includes(viewer._id));
|
let [bookmarked, changeBookmarked] = useState(post.bookmarks && post.bookmarks.includes(viewer._id));
|
||||||
let toProfileText = post.toProfile && post.toProfile !== post.profileid ?
|
let toProfileText = post.toProfile && post.toProfile !== post.profileid ?
|
||||||
<ProfilePhotoCircle profileid={post.toProfile} small={true} /> : undefined;
|
<ProfilePhotoCircle profileid={post.toProfile} small={true} /> : undefined;
|
||||||
let cleanContent = post.content.replace(/@[A-z]+:.+\w/g, '').trim();
|
let cleanContent = post.content.replace(/@[A-z]+:.+\w/g, '').trim();
|
||||||
|
const navigation = useNavigation();
|
||||||
//cleanContent = convertLinks(cleanContent);
|
//cleanContent = convertLinks(cleanContent);
|
||||||
const newComentAdded = (commentData) => {
|
const newComentAdded = (commentData) => {
|
||||||
let newPostObj = { ...post };
|
let newPostObj = { ...post };
|
||||||
@@ -65,6 +68,29 @@ let Post = (props) => {
|
|||||||
const renderComment = ({ item }) => (
|
const renderComment = ({ item }) => (
|
||||||
<Comment comment={item} postid={post._id} />
|
<Comment comment={item} postid={post._id} />
|
||||||
);
|
);
|
||||||
|
const handleTagPress = (tag) => {
|
||||||
|
// Alert.alert("tag pressed", `You pressed the tag: ${tag}`);
|
||||||
|
// You can navigate to another screen or perform any other action here
|
||||||
|
//remove hastag from tag
|
||||||
|
tag = tag.replace("#", "");
|
||||||
|
navigation.navigate("Tags", { tag: tag });
|
||||||
|
};
|
||||||
|
const handleLinkPress = (url) => {
|
||||||
|
Linking.canOpenURL(url)
|
||||||
|
.then((supported) => {
|
||||||
|
if (supported) {
|
||||||
|
Linking.openURL(url);
|
||||||
|
} else {
|
||||||
|
Alert.alert('Error', 'Unable to open the link.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => console.error('An error occurred', err));
|
||||||
|
};
|
||||||
|
const handleLinkLongPress = (url) => {
|
||||||
|
Share.share({
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Card style={styles.card}>
|
<Card style={styles.card}>
|
||||||
<Card.Content style={{
|
<Card.Content style={{
|
||||||
@@ -72,27 +98,40 @@ let Post = (props) => {
|
|||||||
margin: 0,
|
margin: 0,
|
||||||
marginBottom: 0
|
marginBottom: 0
|
||||||
}}>
|
}}>
|
||||||
<Hyperlink linkDefault={true} linkStyle={{ color: '#2980b9' }}>
|
|
||||||
{!post.nonOrganicType ?
|
|
||||||
<View>
|
|
||||||
<ProfilePhotoCircle profileid={post.profileid} />
|
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', margin: 0, marginLeft: 37, marginTop: -14, paddingBottom: 2 }}>
|
|
||||||
{toProfileText}
|
|
||||||
|
|
||||||
</View>
|
{!post.nonOrganicType ?
|
||||||
|
<View>
|
||||||
|
<ProfilePhotoCircle profileid={post.profileid} />
|
||||||
|
<View style={{ flexDirection: 'row', alignItems: 'center', margin: 0, marginLeft: 37, marginTop: -14, paddingBottom: 2 }}>
|
||||||
|
{toProfileText}
|
||||||
|
|
||||||
<Text style={{ fontSize: 16, padding: 3, paddingLeft: 40 }}>{
|
|
||||||
cleanContent
|
|
||||||
}</Text>
|
|
||||||
<Media content={post.content} postId={post._id} post={post} style={{ paddingTop: 2 }} />
|
|
||||||
</View> :
|
|
||||||
<View>
|
|
||||||
<Chip icon="new-releases" style={{ width: 100 }} >{i18n.t("message.news")}</Chip>
|
|
||||||
<Text style={{ fontSize: 18 }}>{cleanContent}</Text>
|
|
||||||
<Media content={post.content} />
|
|
||||||
</View>
|
</View>
|
||||||
}
|
<Pressable onLongPress={() => {
|
||||||
</Hyperlink>
|
if (cleanContent.length > 10) {
|
||||||
|
Share.share({
|
||||||
|
message: cleanContent
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<ParsedText
|
||||||
|
style={styles.text}
|
||||||
|
parse={[
|
||||||
|
{ pattern: /#(\w+)/, style: styles.tag, onPress: handleTagPress },
|
||||||
|
{ pattern: /(https?:\/\/[^\s]+)/, style: styles.link, onPress: handleLinkPress, onLongPress: handleLinkLongPress },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{cleanContent}
|
||||||
|
</ParsedText>
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
|
<Media content={post.content} postId={post._id} post={post} style={{ paddingTop: 2 }} />
|
||||||
|
</View> :
|
||||||
|
<View>
|
||||||
|
<Chip icon="new-releases" style={{ width: 100 }} >{i18n.t("message.news")}</Chip>
|
||||||
|
<Text style={{ fontSize: 18 }}>{cleanContent}</Text>
|
||||||
|
<Media content={post.content} />
|
||||||
|
</View>
|
||||||
|
}
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
<Card.Actions style={{ flexDirection: "row", flow: 4, fontSize: 16, marginLeft: 36, marginTop: -10 }}>
|
<Card.Actions style={{ flexDirection: "row", flow: 4, fontSize: 16, marginLeft: 36, marginTop: -10 }}>
|
||||||
<Button
|
<Button
|
||||||
@@ -105,7 +144,13 @@ let Post = (props) => {
|
|||||||
{likes}
|
{likes}
|
||||||
</Button>
|
</Button>
|
||||||
<Button icon="forum" labelStyle={{ fontSize: 17 }} style={{ flow: 1 }}
|
<Button icon="forum" labelStyle={{ fontSize: 17 }} style={{ flow: 1 }}
|
||||||
onPress={() => { changeshowCommentsB(!showCommentsB) }}
|
onPress={() => {
|
||||||
|
// changeshowCommentsB(!showCommentsB) // Show comments
|
||||||
|
// Change view to single post
|
||||||
|
navigation.navigate("SinglePost", {
|
||||||
|
postid: post._id,
|
||||||
|
});
|
||||||
|
}}
|
||||||
color="#555"
|
color="#555"
|
||||||
>
|
>
|
||||||
{post.comments.length}
|
{post.comments.length}
|
||||||
@@ -133,12 +178,11 @@ let Post = (props) => {
|
|||||||
{showCommentsB && <NewComment postid={post._id} newComentAdded={newComentAdded} />}
|
{showCommentsB && <NewComment postid={post._id} newComentAdded={newComentAdded} />}
|
||||||
{
|
{
|
||||||
showCommentsB &&
|
showCommentsB &&
|
||||||
<ScrollView style={{ maxHeight: 300 }}>
|
<FlatList
|
||||||
<FlatList
|
data={post.comments}
|
||||||
data={post.comments}
|
renderItem={renderComment}
|
||||||
renderItem={renderComment}
|
keyExtractor={item => item.createdAt}
|
||||||
keyExtractor={item => item.createdAt}
|
/>
|
||||||
/></ScrollView>
|
|
||||||
}
|
}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
@@ -164,5 +208,18 @@ const styles = StyleSheet.create({
|
|||||||
margin: 8,
|
margin: 8,
|
||||||
marginTop: 0,
|
marginTop: 0,
|
||||||
padding: 8
|
padding: 8
|
||||||
}
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: 16,
|
||||||
|
padding: 3,
|
||||||
|
paddingLeft: 40
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
color: '#77B5FE',
|
||||||
|
textDecorationLine: 'underline',
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
color: '#77B5FE',
|
||||||
|
textDecorationLine: 'underline',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Avatar } from 'react-native-paper';
|
import { Avatar } from 'react-native-paper';
|
||||||
import { View, StyleSheet, Text } from 'react-native';
|
import { View, StyleSheet, Text, TouchableOpacity } from 'react-native';
|
||||||
import API from './../API.js';
|
import API from './../API.js';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { Image } from 'expo-image'; // Import Image from expo-image
|
import { Image } from 'expo-image'; // Import Image from expo-image
|
||||||
@@ -29,13 +29,17 @@ const ProfileHeader = ({ profileid, withName = false, small = false }) => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
|
<TouchableOpacity onPress={onPress}>
|
||||||
<Image source={{ uri: photoUrl }} key={photoUrl}
|
<Image source={{ uri: photoUrl }} key={photoUrl}
|
||||||
style={{
|
style={{
|
||||||
width: small ? 25 : 35,
|
width: small ? 25 : 35,
|
||||||
height: small ? 25 : 35,
|
height: small ? 25 : 35,
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
}} cachePolicy="memory-disk"/>
|
}} cachePolicy="memory-disk"
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
<View style={styles.textContainer}>
|
<View style={styles.textContainer}>
|
||||||
<Text style={small ? styles.smallProfileName : styles.profileName} onPress={onPress}>{fullName}</Text>
|
<Text style={small ? styles.smallProfileName : styles.profileName} onPress={onPress}>{fullName}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { ScrollView } from 'react-native';
|
import { FlatList } from 'react-native';
|
||||||
import API from './../API.js';
|
import API from './../API.js';
|
||||||
import Post from './Post.js';
|
import Post from './Post.js';
|
||||||
|
|
||||||
let SinglePostComponent = ({ postId }) => {
|
let SinglePostComponent = ({ postId, hideComments }) => {
|
||||||
let [post, setPost] = useState({});
|
let [post, setPost] = useState({});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let subscribed = true;
|
let subscribed = true;
|
||||||
@@ -19,9 +19,12 @@ let SinglePostComponent = ({ postId }) => {
|
|||||||
}
|
}
|
||||||
}, [postId]);
|
}, [postId]);
|
||||||
return (post._id ? (
|
return (post._id ? (
|
||||||
<ScrollView>
|
<FlatList
|
||||||
<Post post={post}/>
|
data={[post]}
|
||||||
</ScrollView>
|
renderItem={({ item }) => <Post post={item} showComments={hideComments ? false : true}/>}
|
||||||
|
keyExtractor={item => item._id}
|
||||||
|
/>
|
||||||
|
|
||||||
) : null);
|
) : null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -40,6 +40,7 @@
|
|||||||
"react-native-autoheight-webview": "^1.6.1",
|
"react-native-autoheight-webview": "^1.6.1",
|
||||||
"react-native-hyperlink": "0.0.19",
|
"react-native-hyperlink": "0.0.19",
|
||||||
"react-native-paper": "^4.11.2",
|
"react-native-paper": "^4.11.2",
|
||||||
|
"react-native-parsed-text": "^0.0.22",
|
||||||
"react-native-safe-area-context": "4.10.5",
|
"react-native-safe-area-context": "4.10.5",
|
||||||
"react-native-screens": "3.31.1",
|
"react-native-screens": "3.31.1",
|
||||||
"react-native-vector-icons": "^9.1.0",
|
"react-native-vector-icons": "^9.1.0",
|
||||||
@@ -13160,6 +13161,19 @@
|
|||||||
"color-string": "^1.6.0"
|
"color-string": "^1.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-parsed-text": {
|
||||||
|
"version": "0.0.22",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-parsed-text/-/react-native-parsed-text-0.0.22.tgz",
|
||||||
|
"integrity": "sha512-hfD83RDXZf9Fvth3DowR7j65fMnlqM9PpxZBGWkzVcUTFtqe6/yPcIoIAgrJbKn6YmtzkivmhWE2MCE4JKBXrQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.7.x"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native-safe-area-context": {
|
"node_modules/react-native-safe-area-context": {
|
||||||
"version": "4.10.5",
|
"version": "4.10.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.5.tgz",
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
"react-native-autoheight-webview": "^1.6.1",
|
"react-native-autoheight-webview": "^1.6.1",
|
||||||
"react-native-hyperlink": "0.0.19",
|
"react-native-hyperlink": "0.0.19",
|
||||||
"react-native-paper": "^4.11.2",
|
"react-native-paper": "^4.11.2",
|
||||||
|
"react-native-parsed-text": "^0.0.22",
|
||||||
"react-native-safe-area-context": "4.10.5",
|
"react-native-safe-area-context": "4.10.5",
|
||||||
"react-native-screens": "3.31.1",
|
"react-native-screens": "3.31.1",
|
||||||
"react-native-vector-icons": "^9.1.0",
|
"react-native-vector-icons": "^9.1.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user