Add tag functionality to posts and implement Tags screen
This commit is contained in:
3
API.js
3
API.js
@@ -143,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);
|
||||||
|
|||||||
5
App.js
5
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';
|
||||||
@@ -306,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}
|
||||||
|
|||||||
77
Views/Tags.js
Normal file
77
Views/Tags.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { View, ActivityIndicator, StyleSheet, SafeAreaView, FlatList } from 'react-native';
|
||||||
|
import { Button, IconButton } from 'react-native-paper';
|
||||||
|
import API from './../API.js';
|
||||||
|
import Post from './../components/Post.js';
|
||||||
|
import NewPost from "./../components/NewPost.js";
|
||||||
|
import ProfileHeader from '../components/ProfileHeader.js';
|
||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
</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",
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Text, Pressable, 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';
|
||||||
@@ -14,6 +14,7 @@ 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 { useNavigation } from '@react-navigation/native';
|
||||||
|
import ParsedText from 'react-native-parsed-text';
|
||||||
|
|
||||||
|
|
||||||
let Post = (props) => {
|
let Post = (props) => {
|
||||||
@@ -67,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={{
|
||||||
@@ -74,34 +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 ?
|
||||||
<Pressable onLongPress={() => {
|
<View>
|
||||||
if(cleanContent.length > 10){
|
<ProfilePhotoCircle profileid={post.profileid} />
|
||||||
Share.share({
|
<View style={{ flexDirection: 'row', alignItems: 'center', margin: 0, marginLeft: 37, marginTop: -14, paddingBottom: 2 }}>
|
||||||
message: cleanContent
|
{toProfileText}
|
||||||
});
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<Text style={{ fontSize: 16, padding: 3, paddingLeft: 40 }}>{
|
|
||||||
cleanContent
|
|
||||||
}</Text>
|
|
||||||
</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>
|
</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
|
||||||
@@ -148,11 +178,11 @@ let Post = (props) => {
|
|||||||
{showCommentsB && <NewComment postid={post._id} newComentAdded={newComentAdded} />}
|
{showCommentsB && <NewComment postid={post._id} newComentAdded={newComentAdded} />}
|
||||||
{
|
{
|
||||||
showCommentsB &&
|
showCommentsB &&
|
||||||
<FlatList
|
<FlatList
|
||||||
data={post.comments}
|
data={post.comments}
|
||||||
renderItem={renderComment}
|
renderItem={renderComment}
|
||||||
keyExtractor={item => item.createdAt}
|
keyExtractor={item => item.createdAt}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
@@ -178,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',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
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