mirror of
https://github.com/wassname/talk.git
synced 2026-07-21 12:51:03 +08:00
Merge pull request #512 from coralproject/action-summary-fix
Action summary fix - Part Deux
This commit is contained in:
@@ -10,6 +10,7 @@ import FlagBox from './FlagBox';
|
||||
import CommentType from './CommentType';
|
||||
import ActionButton from 'coral-admin/src/components/ActionButton';
|
||||
import BanUserButton from 'coral-admin/src/components/BanUserButton';
|
||||
import {getActionSummary} from 'coral-framework/utils';
|
||||
|
||||
const linkify = new Linkify();
|
||||
|
||||
@@ -20,8 +21,8 @@ const lang = new I18n(translations);
|
||||
const Comment = ({actions = [], comment, ...props}) => {
|
||||
const links = linkify.getMatches(comment.body);
|
||||
const linkText = links ? links.map(link => link.raw) : [];
|
||||
const actionSummaries = comment.action_summaries.filter(a => a.__typename === 'FlagActionSummary');
|
||||
const flagActions = comment.actions.filter(a => a.__typename === 'FlagAction');
|
||||
const flagActionSummaries = getActionSummary('FlagActionSummary', comment);
|
||||
const flagActions = comment.actions && comment.actions.filter(a => a.__typename === 'FlagAction');
|
||||
|
||||
return (
|
||||
<li tabIndex={props.index} className={`mdl-card ${props.selected ? 'mdl-shadow--8dp' : 'mdl-shadow--2dp'} ${styles.Comment} ${styles.listItem}`}>
|
||||
@@ -71,7 +72,11 @@ const Comment = ({actions = [], comment, ...props}) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{flagActions && <FlagBox actions={flagActions} actionSummaries={actionSummaries} />}
|
||||
{
|
||||
flagActions && flagActions.length
|
||||
? <FlagBox actions={flagActions} actionSummaries={flagActionSummaries} />
|
||||
: null
|
||||
}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -22,11 +22,10 @@ import LoadMore from 'coral-embed-stream/src/LoadMore';
|
||||
import {Slot} from 'coral-framework';
|
||||
import IgnoredCommentTombstone from './IgnoredCommentTombstone';
|
||||
import {TopRightMenu} from './TopRightMenu';
|
||||
import {getActionSummary, getTotalActionCount, iPerformedThisAction} from 'coral-framework/utils';
|
||||
|
||||
import styles from './Comment.css';
|
||||
|
||||
const getActionSummary = (type, comment) => comment.action_summaries
|
||||
.filter((a) => a.__typename === type)[0];
|
||||
const isStaff = (tags) => !tags.every((t) => t.name !== 'STAFF') ;
|
||||
|
||||
// hold actions links (e.g. Like, Reply) along the comment footer
|
||||
@@ -124,9 +123,16 @@ class Comment extends React.Component {
|
||||
commentIsIgnored,
|
||||
} = this.props;
|
||||
|
||||
const like = getActionSummary('LikeActionSummary', comment);
|
||||
const flag = getActionSummary('FlagActionSummary', comment);
|
||||
const dontagree = getActionSummary('DontAgreeActionSummary', comment);
|
||||
const likeSummary = getActionSummary('LikeActionSummary', comment);
|
||||
const flagSummary = getActionSummary('FlagActionSummary', comment);
|
||||
const dontAgreeSummary = getActionSummary('DontAgreeActionSummary', comment);
|
||||
let myFlag = null;
|
||||
if (iPerformedThisAction('FlagActionSummary', comment)) {
|
||||
myFlag = flagSummary.find(s => s.current_user);
|
||||
} else if (iPerformedThisAction('DontAgreeActionSummary', comment)) {
|
||||
myFlag = dontAgreeSummary.find(s => s.current_user);
|
||||
}
|
||||
|
||||
let commentClass = parentId ? `reply ${styles.Reply}` : `comment ${styles.Comment}`;
|
||||
commentClass += comment.id === 'pending' ? ` ${styles.pendingComment}` : '';
|
||||
|
||||
@@ -183,8 +189,10 @@ class Comment extends React.Component {
|
||||
<Content body={comment.body} />
|
||||
<div className="commentActionsLeft comment__action-container">
|
||||
<ActionButton>
|
||||
{/* TODO implmement iPerformedThisAction for the like */}
|
||||
<LikeButton
|
||||
like={like}
|
||||
totalLikes={getTotalActionCount('LikeActionSummary', comment)}
|
||||
like={likeSummary[0]}
|
||||
id={comment.id}
|
||||
postLike={postLike}
|
||||
deleteAction={deleteAction}
|
||||
@@ -217,7 +225,8 @@ class Comment extends React.Component {
|
||||
</ActionButton>
|
||||
<ActionButton>
|
||||
<FlagComment
|
||||
flag={flag && flag.current_user ? flag : dontagree}
|
||||
flaggedByCurrentUser={!!myFlag}
|
||||
flag={myFlag}
|
||||
id={comment.id}
|
||||
author_id={comment.user.id}
|
||||
postFlag={postFlag}
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
/**
|
||||
* getActionSummary
|
||||
* retrieves the action summary based on the type and the comment
|
||||
*/
|
||||
export const getTotalActionCount = (type, comment) => {
|
||||
return comment.action_summaries
|
||||
.filter(s => s.__typename === type)
|
||||
.reduce((total, summary) => {
|
||||
return total + summary.count;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
export const getActionSummary = (type, comment) =>
|
||||
comment.action_summaries.filter(a => a.__typename === type)[0];
|
||||
export const iPerformedThisAction = (type, comment) => {
|
||||
|
||||
// if there is a current_user on any of the ActionSummary(s), the user performed this action
|
||||
return comment.action_summaries
|
||||
.filter(a => a.__typename === type)
|
||||
.some(a => a.current_user);
|
||||
};
|
||||
|
||||
export const getMyActionSummary = (type, comment) => {
|
||||
return comment.action_summaries
|
||||
.filter(a => a.__typename === type)
|
||||
.find(a => a.current_user);
|
||||
};
|
||||
|
||||
/**
|
||||
* getActionSummary
|
||||
* retrieves the action summaries based on the type and the comment
|
||||
* array could be length > 1, as in the case of FlagActionSummary
|
||||
*/
|
||||
|
||||
export const getActionSummary = (type, comment) => {
|
||||
return comment.action_summaries.filter(a => a.__typename === type);
|
||||
};
|
||||
|
||||
@@ -21,15 +21,15 @@ class FlagButton extends Component {
|
||||
|
||||
// When the "report" button is clicked expand the menu
|
||||
onReportClick = () => {
|
||||
const {currentUser, flag, deleteAction} = this.props;
|
||||
const {currentUser, deleteAction, flaggedByCurrentUser, flag} = this.props;
|
||||
const {localPost, localDelete} = this.state;
|
||||
const flagged = (flag && flag.current_user && !localDelete) || localPost;
|
||||
const localFlagged = (flaggedByCurrentUser && !localDelete) || localPost;
|
||||
if (!currentUser) {
|
||||
const offset = document.getElementById(`c_${this.props.id}`).getBoundingClientRect().top - 75;
|
||||
this.props.showSignInDialog(offset);
|
||||
return;
|
||||
}
|
||||
if (flagged) {
|
||||
if (localFlagged) {
|
||||
this.setState((prev) => prev.localPost ? {...prev, localPost: null, step: 0} : {...prev, localDelete: true});
|
||||
deleteAction(localPost || flag.current_user.id);
|
||||
} else if (this.state.showMenu){
|
||||
@@ -130,9 +130,9 @@ class FlagButton extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {flag, getPopupMenu} = this.props;
|
||||
const {getPopupMenu, flaggedByCurrentUser} = this.props;
|
||||
const {localPost, localDelete} = this.state;
|
||||
const flagged = (flag && flag.current_user && !localDelete) || localPost;
|
||||
const flagged = (flaggedByCurrentUser && !localDelete) || localPost;
|
||||
const popupMenu = getPopupMenu[this.state.step](this.state.itemType);
|
||||
|
||||
return <div className={`${name}-container`}>
|
||||
|
||||
@@ -27,9 +27,9 @@ class LikeButton extends Component {
|
||||
|
||||
render() {
|
||||
const {like, id, postLike, deleteAction, showSignInDialog, currentUser} = this.props;
|
||||
let {totalLikes: count} = this.props;
|
||||
const {localPost, localDelete} = this.state;
|
||||
const liked = (like && like.current_user && !localDelete) || localPost;
|
||||
let count = like ? like.count : 0;
|
||||
if (localPost) {count += 1;}
|
||||
if (localDelete) {count -= 1;}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ type User {
|
||||
username: String!
|
||||
|
||||
# Action summaries against the user.
|
||||
action_summaries: [ActionSummary]
|
||||
action_summaries: [ActionSummary]!
|
||||
|
||||
# Actions completed on the parent.
|
||||
actions: [Action]
|
||||
@@ -197,7 +197,7 @@ type Comment {
|
||||
actions: [Action]
|
||||
|
||||
# Action summaries against a comment.
|
||||
action_summaries: [ActionSummary]
|
||||
action_summaries: [ActionSummary]!
|
||||
|
||||
# The asset that a comment was made on.
|
||||
asset: Asset
|
||||
@@ -440,7 +440,7 @@ type Asset {
|
||||
|
||||
# Summary of all Actions against all entities associated with the Asset.
|
||||
# (likes, flags, etc.). Requires the `ADMIN` role.
|
||||
action_summaries: [AssetActionSummary]
|
||||
action_summaries: [AssetActionSummary!]
|
||||
|
||||
# The date that the asset was created.
|
||||
created_at: Date
|
||||
|
||||
@@ -5,6 +5,7 @@ import Icon from './Icon';
|
||||
import {I18n} from 'coral-framework';
|
||||
import cn from 'classnames';
|
||||
import translations from '../translations.json';
|
||||
import {getMyActionSummary, getTotalActionCount} from 'coral-framework/utils';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
@@ -14,8 +15,7 @@ class RespectButton extends Component {
|
||||
const {postRespect, showSignInDialog, deleteAction, commentId} = this.props;
|
||||
const {me, comment} = this.props.data;
|
||||
|
||||
const respect = comment.action_summaries[0];
|
||||
const respected = (respect && respect.current_user);
|
||||
const myRespectActionSummary = getMyActionSummary('RespectActionSummary', comment);
|
||||
|
||||
// If the current user does not exist, trigger sign in dialog.
|
||||
if (!me) {
|
||||
@@ -29,29 +29,33 @@ class RespectButton extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!respected) {
|
||||
if (myRespectActionSummary) {
|
||||
deleteAction(myRespectActionSummary.current_user.id);
|
||||
} else {
|
||||
postRespect({
|
||||
item_id: commentId,
|
||||
item_type: 'COMMENTS'
|
||||
});
|
||||
} else {
|
||||
deleteAction(respect.current_user.id);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {comment} = this.props.data;
|
||||
const respect = comment && comment.action_summaries && comment.action_summaries[0];
|
||||
const respected = respect && respect.current_user;
|
||||
let count = respect ? respect.count : 0;
|
||||
|
||||
if (!comment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const myRespect = getMyActionSummary('RespectActionSummary', comment);
|
||||
let count = getTotalActionCount('RespectActionSummary', comment);
|
||||
|
||||
return (
|
||||
<div className={styles.respect}>
|
||||
<button
|
||||
className={cn(styles.button, {[styles.respected]: respected})}
|
||||
className={cn(styles.button, {[styles.respected]: myRespect})}
|
||||
onClick={this.handleClick} >
|
||||
<span>{lang.t(respected ? 'respected' : 'respect')}</span>
|
||||
<Icon className={cn(styles.icon, {[styles.respected]: respected})} />
|
||||
<span>{lang.t(myRespect ? 'respected' : 'respect')}</span>
|
||||
<Icon className={cn(styles.icon, {[styles.respected]: myRespect})} />
|
||||
{count > 0 && count}
|
||||
</button>
|
||||
</div>
|
||||
@@ -64,4 +68,3 @@ RespectButton.propTypes = {
|
||||
};
|
||||
|
||||
export default RespectButton;
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import RespectButton from '../components/RespectButton';
|
||||
// See https://dev-blog.apollodata.com/apollo-clients-new-imperative-store-api-6cb69318a1e3
|
||||
// and https://github.com/apollographql/apollo-client/issues/1224
|
||||
|
||||
const isRespectAction = (a) => a.__typename === 'RespectActionSummary';
|
||||
|
||||
export const RESPECT_QUERY = gql`
|
||||
query RespectQuery($commentId: ID!) {
|
||||
comment(id: $commentId) {
|
||||
@@ -52,18 +54,21 @@ const withDeleteAction = graphql(gql`
|
||||
},
|
||||
updateQueries: {
|
||||
RespectQuery: (prev) => {
|
||||
if (get(prev, 'comment.action_summaries.0.current_user.id') !== id) {
|
||||
const action_summaries = prev.comment.action_summaries;
|
||||
const idx = action_summaries.findIndex(isRespectAction);
|
||||
if (idx < 0 || get(action_summaries[idx], 'current_user.id') !== id) {
|
||||
return prev;
|
||||
}
|
||||
const next = {
|
||||
...prev,
|
||||
comment: {
|
||||
...prev.comment,
|
||||
action_summaries: [{
|
||||
__typename: 'RespectActionSummary',
|
||||
count: prev.comment.action_summaries[0].count - 1,
|
||||
current_user: null,
|
||||
}],
|
||||
action_summaries: action_summaries.map(
|
||||
(a, i) => i !== idx ? a : ({
|
||||
...a,
|
||||
count: a.count - 1,
|
||||
current_user: null,
|
||||
})),
|
||||
}
|
||||
};
|
||||
return next;
|
||||
@@ -102,21 +107,40 @@ const withPostRespect = graphql(gql`
|
||||
},
|
||||
updateQueries: {
|
||||
RespectQuery: (prev, {mutationResult, queryVariables}) => {
|
||||
if (queryVariables.commentId !== respect.item_id ||
|
||||
get(prev, 'comment.action_summaries.0.current_user')) {
|
||||
if (queryVariables.commentId !== respect.item_id) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
let action_summaries = prev.comment.action_summaries;
|
||||
let idx = action_summaries.findIndex(isRespectAction);
|
||||
|
||||
// Check whether we already respected this comment.
|
||||
if (idx >= 0 && action_summaries[idx].current_user) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
if (idx < 0) {
|
||||
|
||||
// Add initial action when it doesn't exist.
|
||||
action_summaries = action_summaries.concat([{
|
||||
__typename: 'RespectActionSummary',
|
||||
count: 0,
|
||||
current_user: null,
|
||||
}]);
|
||||
idx = action_summaries.length - 1;
|
||||
}
|
||||
|
||||
const respectAction = mutationResult.data.createRespect.respect;
|
||||
const count = prev.comment.action_summaries[0] ? prev.comment.action_summaries[0].count : 0;
|
||||
const next = {
|
||||
...prev,
|
||||
comment: {
|
||||
...prev.comment,
|
||||
action_summaries: [{
|
||||
__typename: 'RespectActionSummary',
|
||||
count: count + 1,
|
||||
current_user: respectAction,
|
||||
}],
|
||||
action_summaries: action_summaries.map(
|
||||
(a, i) => i !== idx ? a : ({
|
||||
...a,
|
||||
count: a.count + 1,
|
||||
current_user: respectAction,
|
||||
})),
|
||||
}
|
||||
};
|
||||
return next;
|
||||
@@ -138,4 +162,3 @@ const enhance = compose(
|
||||
);
|
||||
|
||||
export default enhance(RespectButton);
|
||||
|
||||
|
||||
+8
-2
@@ -48,10 +48,16 @@ module.exports = class ActionsService {
|
||||
* Finds actions in an array of ids.
|
||||
* @param {String} ids array of user identifiers (uuid)
|
||||
*/
|
||||
static findByItemIdArray(item_ids) {
|
||||
return ActionModel.find({
|
||||
static async findByItemIdArray(item_ids) {
|
||||
let actions = await ActionModel.find({
|
||||
'item_id': {$in: item_ids}
|
||||
});
|
||||
|
||||
if (actions === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user