Enable commenting on posts and replies to posts

Pixelfed seems to only return first-level descendants from its context
endpoint, thus after replies-to-replies are submitted, they are not
rendered. See #23
This commit is contained in:
Nat 2021-05-22 16:37:44 -03:00
parent 0eb4f8599e
commit 3d22ed71c7
3 changed files with 131 additions and 26 deletions

View File

@ -132,6 +132,7 @@ const ViewProfileJsx = ({navigation}) => {
active = { navigation.getParam("originTab") } active = { navigation.getParam("originTab") }
navigation = { navigation }> navigation = { navigation }>
<RawProfileJsx <RawProfileJsx
navigation = { navigation }
onFollow = { _handleFollow } onFollow = { _handleFollow }
profile = { state.profile } profile = { state.profile }
mutuals = { state.mutuals } mutuals = { state.mutuals }

View File

@ -7,6 +7,7 @@ import {
TextInput, TextInput,
Text Text
} from "react-native"; } from "react-native";
import { FontAwesome } from '@expo/vector-icons';
import { ScrollView } from "react-native-gesture-handler"; import { ScrollView } from "react-native-gesture-handler";
import AsyncStorage from "@react-native-async-storage/async-storage"; import AsyncStorage from "@react-native-async-storage/async-storage";
@ -15,7 +16,7 @@ import { activeOrNot } from "src/interface/interactions";
import TimelineViewJsx from "src/components/posts/timeline-view"; import TimelineViewJsx from "src/components/posts/timeline-view";
import BackBarJsx from "src/components/navigation/back-bar"; import BackBarJsx from "src/components/navigation/back-bar";
import { TouchableWithoutFeedback } from "react-native-gesture-handler"; import { TouchableOpacity } from "react-native-gesture-handler";
import * as requests from "src/requests"; import * as requests from "src/requests";
@ -199,18 +200,24 @@ const CommentJsx = (props) => {
} }
</Text> </Text>
</View> </View>
<TouchableWithoutFeedback> <TouchableOpacity
onPress = {
props.onReply(
props.data.account.acct,
props.data.id
)
}>
<View> <View>
<Text style = { [styles.actionText] }> <Text style = { [styles.actionText] }>
Reply Reply
</Text> </Text>
</View> </View>
</TouchableWithoutFeedback> </TouchableOpacity>
<TouchableWithoutFeedback> <TouchableOpacity>
<Image <Image
style = { [styles.heart, styles.action] } style = { [styles.heart, styles.action] }
source = { activeOrNot(props.data.favourited, packs.favourited) } /> source = { activeOrNot(props.data.favourited, packs.favourited) } />
</TouchableWithoutFeedback> </TouchableOpacity>
</View> </View>
</View> </View>
</View> </View>
@ -221,7 +228,7 @@ const ViewCommentsJsx = (props) => {
let [state, setState] = useState({ let [state, setState] = useState({
postData: props.navigation.getParam("postData", null), postData: props.navigation.getParam("postData", null),
loaded: false, loaded: false,
reply: "" reply: "",
}); });
useEffect(() => { useEffect(() => {
@ -245,11 +252,66 @@ const ViewCommentsJsx = (props) => {
profile, profile,
instance, instance,
accessToken, accessToken,
inReplyTo: {
acct: state.postData.account.acct,
id: state.postData.id,
},
loaded: true, loaded: true,
}); });
}); });
}, []); }, []);
const _onReplyFactory = (acct, id) => {
return () => {
setState({...state,
inReplyTo: {
acct,
id,
},
});
}
};
const _handleCancelSubReply = () => {
setState({...state,
inReplyTo: {
acct: state.postData.account.acct,
id: state.postData.id,
},
});
};
const _handleSubmitReply = async () => {
if(state.reply.length > 0) {
await requests.publishStatus(
state.instance,
state.accessToken,
{
status: state.reply,
in_reply_to_id: state.inReplyTo.id,
}
);
// Fetch the updated context to rerender the page
const newContext = await requests.fetchStatusContext(
state.instance,
state.postData.id,
state.accessToken,
);
setState({...state,
descendants: threadify(newContext.descendants),
//Reset the comment form
inReplyTo: {
acct: state.postData.account.acct,
id: state.postData.id,
},
reply: "",
});
}
};
return ( return (
<> <>
{ state.loaded ? { state.loaded ?
@ -261,6 +323,7 @@ const ViewCommentsJsx = (props) => {
? <View> ? <View>
<View style = { styles.parentPost }> <View style = { styles.parentPost }>
<CommentJsx <CommentJsx
onReply = { _onReplyFactory }
data = { state.postData } /> data = { state.postData } />
</View> </View>
<View> <View>
@ -270,7 +333,9 @@ const ViewCommentsJsx = (props) => {
const subs = thread.slice(1); const subs = thread.slice(1);
return ( return (
<View key = { i }> <View key = { i }>
<CommentJsx data = { comment }/> <CommentJsx
onReply = { _onReplyFactory }
data = { comment }/>
{ {
subs.map((sub, j) => { subs.map((sub, j) => {
return ( return (
@ -278,6 +343,7 @@ const ViewCommentsJsx = (props) => {
key = { j } key = { j }
style = { styles.sub }> style = { styles.sub }>
<CommentJsx <CommentJsx
onReply = { _onReplyFactory }
data = { sub }/> data = { sub }/>
</View> </View>
) )
@ -297,6 +363,23 @@ const ViewCommentsJsx = (props) => {
: <></> : <></>
} }
</ScrollView> </ScrollView>
<View style = { styles.form.container }>
<>
{ state.inReplyTo.id != state.postData.id
? <TouchableOpacity onPress = { _handleCancelSubReply }>
<View style = { styles.form.inReplyTo.container }>
<FontAwesome name="close" size={24} color="#666" />
<Text style = { styles.form.inReplyTo.message }>
&nbsp;Replying to&nbsp;
<Text style = { styles.bold }>
{ state.inReplyTo.acct }
</Text>...
</Text>
</View>
</TouchableOpacity>
: <></>
}
</>
<View style = { styles.commentForm }> <View style = { styles.commentForm }>
<Image <Image
style = { styles.avatar } style = { styles.avatar }
@ -307,11 +390,12 @@ const ViewCommentsJsx = (props) => {
multiline = { true } multiline = { true }
onChangeText = { c => setState({...state, reply: c }) }/> onChangeText = { c => setState({...state, reply: c }) }/>
<View style = { styles.submitContainer }> <View style = { styles.submitContainer }>
<TouchableWithoutFeedback> <TouchableOpacity onPress = { _handleSubmitReply }>
<Image <Image
style = { styles.commentSubmit } style = { styles.commentSubmit }
source = { require("assets/eva-icons/paper-plane.png") }/> source = { require("assets/eva-icons/paper-plane.png") }/>
</TouchableWithoutFeedback> </TouchableOpacity>
</View>
</View> </View>
</View> </View>
</SafeAreaView> </SafeAreaView>
@ -366,13 +450,28 @@ const styles = {
height: 15, height: 15,
}, },
commentForm: { form: {
flexDirection: "row", container: {
alignItems: "center",
backgroundColor: "white", backgroundColor: "white",
borderTopWidth: 1, borderTopWidth: 1,
borderTopColor: "#CCC", borderTopColor: "#CCC",
},
inReplyTo: {
container: {
padding: 10,
flexDirection: "row",
alignItems: "center",
},
message: {
color: "#666",
},
},
},
commentForm: {
flexDirection: "row",
alignItems: "center",
paddingTop: 10, paddingTop: 10,
paddingBottom: 10, paddingBottom: 10,

View File

@ -92,6 +92,11 @@ export async function fetchAccountStatuses(domain, id, token) {
return resp.json(); return resp.json();
} }
export async function publishStatus(domain, token, params) {
const resp = await postForm(`https://${domain}/api/v1/statuses`, params, token);
return resp.json();
}
export async function fetchStatusContext(domain, id, token) { export async function fetchStatusContext(domain, id, token) {
const resp = await get(`https://${domain}/api/v1/statuses/${id}/context`, token); const resp = await get(`https://${domain}/api/v1/statuses/${id}/context`, token);
return resp.json(); return resp.json();