Enable deleting comments
This commit is contained in:
parent
7968aa2ffc
commit
5339715089
|
@ -20,67 +20,15 @@ import { TouchableOpacity } from "react-native-gesture-handler";
|
||||||
|
|
||||||
import * as requests from "src/requests";
|
import * as requests from "src/requests";
|
||||||
|
|
||||||
const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg";
|
import {
|
||||||
|
Menu,
|
||||||
|
MenuOptions,
|
||||||
|
MenuOption,
|
||||||
|
MenuTrigger,
|
||||||
|
renderers
|
||||||
|
} from "react-native-popup-menu";
|
||||||
|
|
||||||
const TEST_CONTEXT = {
|
const { SlideInMenu } = renderers;
|
||||||
ancestors: [],
|
|
||||||
descendants: [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
in_reply_to_id: "0",
|
|
||||||
username: "respondant1",
|
|
||||||
avatar: TEST_IMAGE,
|
|
||||||
content: "This is a comment",
|
|
||||||
favourited: false,
|
|
||||||
created_at: 1596745156000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
in_reply_to_id: "0",
|
|
||||||
username: "respondant2",
|
|
||||||
avatar: TEST_IMAGE,
|
|
||||||
content: "This is a comment",
|
|
||||||
favourited: true,
|
|
||||||
created_at: 1596745156000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
in_reply_to_id: "2",
|
|
||||||
username: "respondant3",
|
|
||||||
avatar: TEST_IMAGE,
|
|
||||||
content: "This is a comment",
|
|
||||||
favourited: false,
|
|
||||||
created_at: 1596745156000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "4",
|
|
||||||
in_reply_to_id: "2",
|
|
||||||
username: "respondant2",
|
|
||||||
avatar: TEST_IMAGE,
|
|
||||||
content: "This is a comment",
|
|
||||||
favourited: false,
|
|
||||||
created_at: 1596745156000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "5",
|
|
||||||
in_reply_to_id: "1",
|
|
||||||
username: "respondant4",
|
|
||||||
avatar: TEST_IMAGE,
|
|
||||||
content: "This is a comment",
|
|
||||||
favourited: false,
|
|
||||||
created_at: 1596745156000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "6",
|
|
||||||
in_reply_to_id: "4",
|
|
||||||
username: "respondant5",
|
|
||||||
avatar: TEST_IMAGE,
|
|
||||||
content: "This is a comment",
|
|
||||||
favourited: false,
|
|
||||||
created_at: 1596745156000
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
function chunkWhile(arr, fun) {
|
function chunkWhile(arr, fun) {
|
||||||
/*
|
/*
|
||||||
|
@ -172,6 +120,22 @@ function threadify(descendants) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CommentJsx = (props) => {
|
const CommentJsx = (props) => {
|
||||||
|
const menuOptionsStyles = {
|
||||||
|
optionWrapper: { // The wrapper around a single option
|
||||||
|
paddingLeft: SCREEN_WIDTH / 15,
|
||||||
|
paddingTop: SCREEN_WIDTH / 30,
|
||||||
|
paddingBottom: SCREEN_WIDTH / 30
|
||||||
|
},
|
||||||
|
optionsWrapper: { // The wrapper around all options
|
||||||
|
marginTop: SCREEN_WIDTH / 20,
|
||||||
|
marginBottom: SCREEN_WIDTH / 20,
|
||||||
|
},
|
||||||
|
optionsContainer: { // The Animated.View
|
||||||
|
borderTopLeftRadius: 10,
|
||||||
|
borderTopRightRadius: 10
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const packs = {
|
const packs = {
|
||||||
favourited: {
|
favourited: {
|
||||||
active: require("assets/eva-icons/post-actions/heart-active.png"),
|
active: require("assets/eva-icons/post-actions/heart-active.png"),
|
||||||
|
@ -219,6 +183,27 @@ const CommentJsx = (props) => {
|
||||||
style = { [styles.heart, styles.action] }
|
style = { [styles.heart, styles.action] }
|
||||||
source = { activeOrNot(props.data.favourited, packs.favourited) } />
|
source = { activeOrNot(props.data.favourited, packs.favourited) } />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
<View style = { { paddingLeft: 10, } }>
|
||||||
|
<Menu renderer = { SlideInMenu }>
|
||||||
|
<MenuTrigger>
|
||||||
|
<FontAwesome name="ellipsis-h" size={18} color="#666" />
|
||||||
|
</MenuTrigger>
|
||||||
|
<MenuOptions customStyles = { menuOptionsStyles }>
|
||||||
|
{ props.profile.acct == props.data.account.acct
|
||||||
|
? <>
|
||||||
|
<MenuOption
|
||||||
|
onSelect = { props.onDelete(props.data) }
|
||||||
|
text = "Delete" />
|
||||||
|
</>
|
||||||
|
: <>
|
||||||
|
<MenuOption text = "Report" />
|
||||||
|
<MenuOption text = "Mute" />
|
||||||
|
<MenuOption text = "Block" />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</MenuOptions>
|
||||||
|
</Menu>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -262,6 +247,17 @@ const ViewCommentsJsx = (props) => {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const _fetchNewThreads = async () => {
|
||||||
|
// Fetch an updated context to rerender the page
|
||||||
|
const { descendants } = await requests.fetchStatusContext(
|
||||||
|
state.instance,
|
||||||
|
state.postData.id,
|
||||||
|
state.accessToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
return threadify(descendants);
|
||||||
|
}
|
||||||
|
|
||||||
const _onReplyFactory = (acct, id) => {
|
const _onReplyFactory = (acct, id) => {
|
||||||
return () => {
|
return () => {
|
||||||
setState({...state,
|
setState({...state,
|
||||||
|
@ -289,19 +285,34 @@ const ViewCommentsJsx = (props) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the updated context to rerender the page
|
|
||||||
const newContext = await requests.fetchStatusContext(
|
|
||||||
state.instance,
|
|
||||||
state.postData.id,
|
|
||||||
state.accessToken,
|
|
||||||
);
|
|
||||||
|
|
||||||
setState({...state,
|
setState({...state,
|
||||||
descendants: threadify(newContext.descendants),
|
descendants: await _fetchNewThreads(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _onDeleteFactory = data => {
|
||||||
|
return async () => {
|
||||||
|
await requests.deleteStatus(
|
||||||
|
state.instance,
|
||||||
|
data.id,
|
||||||
|
state.accessToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
// NOTE: It appears that it takes a moment for the Context of a
|
||||||
|
// post to register that a comment has been deleted, so instead
|
||||||
|
// of waiting for it, it's more efficient to just drop the comment
|
||||||
|
// on the client side.
|
||||||
|
const newThreads = state.descendants.map(thread =>
|
||||||
|
thread.filter(comment => comment.id != data.id)
|
||||||
|
).filter(thread => thread.length > 0);
|
||||||
|
|
||||||
|
setState({...state,
|
||||||
|
descendants: newThreads,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const _handleCancelSubReply = () => {
|
const _handleCancelSubReply = () => {
|
||||||
setState({...state,
|
setState({...state,
|
||||||
inReplyTo: {
|
inReplyTo: {
|
||||||
|
@ -322,26 +333,29 @@ const ViewCommentsJsx = (props) => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fetch the updated context to rerender the page
|
|
||||||
const newContext = await requests.fetchStatusContext(
|
|
||||||
state.instance,
|
|
||||||
state.postData.id,
|
|
||||||
state.accessToken,
|
|
||||||
);
|
|
||||||
|
|
||||||
setState({...state,
|
setState({...state,
|
||||||
descendants: threadify(newContext.descendants),
|
|
||||||
|
|
||||||
// Reset the comment form
|
// Reset the comment form
|
||||||
inReplyTo: {
|
inReplyTo: {
|
||||||
acct: state.postData.account.acct,
|
acct: state.postData.account.acct,
|
||||||
id: state.postData.id,
|
id: state.postData.id,
|
||||||
},
|
},
|
||||||
reply: "",
|
reply: "",
|
||||||
|
|
||||||
|
// Retrieve updated context
|
||||||
|
descendants: await _fetchNewThreads(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PartialComment = (props) => (
|
||||||
|
<CommentJsx
|
||||||
|
{ ...props }
|
||||||
|
profile = { state.profile }
|
||||||
|
onFavourite = { _onFavouriteFactory }
|
||||||
|
onReply = { _onReplyFactory }
|
||||||
|
onDelete = { _onDeleteFactory } />
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ state.loaded ?
|
{ state.loaded ?
|
||||||
|
@ -352,9 +366,7 @@ const ViewCommentsJsx = (props) => {
|
||||||
{ state.loaded
|
{ state.loaded
|
||||||
? <View>
|
? <View>
|
||||||
<View style = { styles.parentPost }>
|
<View style = { styles.parentPost }>
|
||||||
<CommentJsx
|
<PartialComment
|
||||||
onFavourite = { _onFavouriteFactory }
|
|
||||||
onReply = { _onReplyFactory }
|
|
||||||
data = { state.postData } />
|
data = { state.postData } />
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
|
@ -364,9 +376,7 @@ const ViewCommentsJsx = (props) => {
|
||||||
const subs = thread.slice(1);
|
const subs = thread.slice(1);
|
||||||
return (
|
return (
|
||||||
<View key = { i }>
|
<View key = { i }>
|
||||||
<CommentJsx
|
<PartialComment
|
||||||
onFavourite = { _onFavouriteFactory }
|
|
||||||
onReply = { _onReplyFactory }
|
|
||||||
data = { comment }/>
|
data = { comment }/>
|
||||||
{
|
{
|
||||||
subs.map((sub, j) => {
|
subs.map((sub, j) => {
|
||||||
|
@ -374,9 +384,7 @@ const ViewCommentsJsx = (props) => {
|
||||||
<View
|
<View
|
||||||
key = { j }
|
key = { j }
|
||||||
style = { styles.sub }>
|
style = { styles.sub }>
|
||||||
<CommentJsx
|
<PartialComment
|
||||||
onFavourite = { _onFavouriteFactory }
|
|
||||||
onReply = { _onReplyFactory }
|
|
||||||
data = { sub }/>
|
data = { sub }/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -421,6 +429,7 @@ const ViewCommentsJsx = (props) => {
|
||||||
style = { styles.commentInput }
|
style = { styles.commentInput }
|
||||||
placeholder = "Say something..."
|
placeholder = "Say something..."
|
||||||
multiline = { true }
|
multiline = { true }
|
||||||
|
value = { state.reply }
|
||||||
onChangeText = { c => setState({...state, reply: c }) }/>
|
onChangeText = { c => setState({...state, reply: c }) }/>
|
||||||
<View style = { styles.submitContainer }>
|
<View style = { styles.submitContainer }>
|
||||||
<TouchableOpacity onPress = { _handleSubmitReply }>
|
<TouchableOpacity onPress = { _handleSubmitReply }>
|
||||||
|
|
|
@ -82,6 +82,17 @@ export async function get(url, token = false, data = false) {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function _delete(url, token = false) {
|
||||||
|
const resp = await fetch(url, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: token
|
||||||
|
? { "Authorization": `Bearer ${token}`, }
|
||||||
|
: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchProfile(domain, id) {
|
export async function fetchProfile(domain, id) {
|
||||||
const resp = await get(`https://${domain}/api/v1/accounts/${id}`);
|
const resp = await get(`https://${domain}/api/v1/accounts/${id}`);
|
||||||
return resp.json();
|
return resp.json();
|
||||||
|
@ -97,6 +108,11 @@ export async function publishStatus(domain, token, params) {
|
||||||
return resp.json();
|
return resp.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteStatus(domain, id, token) {
|
||||||
|
const resp = await _delete(`https://${domain}/api/v1/statuses/${id}`, 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();
|
||||||
|
|
Loading…
Reference in New Issue