-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #509 from cofacts/profile-comment
Comments in Profile page
- Loading branch information
Showing
6 changed files
with
295 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
import gql from 'graphql-tag'; | ||
import { useQuery } from '@apollo/react-hooks'; | ||
import { useRouter } from 'next/router'; | ||
import Link from 'next/link'; | ||
import { t, ngettext, msgid } from 'ttag'; | ||
import { makeStyles } from '@material-ui/core/styles'; | ||
import { Tools, TimeRange, LoadMore } from 'components/ListPageControls'; | ||
import { CardHeader, CardContent } from 'components/Card'; | ||
import Infos from 'components/Infos'; | ||
import TimeInfo from 'components/Infos/TimeInfo'; | ||
import ExpandableText from 'components/ExpandableText'; | ||
import ReplyRequestReason from 'components/ReplyRequestReason'; | ||
import Thumbnail from 'components/Thumbnail'; | ||
|
||
const LOAD_USER_COMMENTS = gql` | ||
query LoadUserComments( | ||
$filter: ListReplyRequestFilter! | ||
$orderBy: [ListReplyRequestOrderBy] | ||
$after: String | ||
) { | ||
ListReplyRequests( | ||
filter: $filter | ||
orderBy: $orderBy | ||
after: $after | ||
first: 15 | ||
) { | ||
edges { | ||
node { | ||
id | ||
...ReplyRequestInfo | ||
article { | ||
id | ||
replyRequestCount | ||
replyCount | ||
createdAt | ||
text | ||
...ThumbnailArticleData | ||
} | ||
} | ||
...LoadMoreEdge | ||
} | ||
} | ||
} | ||
${ReplyRequestReason.fragments.ReplyRequestInfo} | ||
${LoadMore.fragments.LoadMoreEdge} | ||
${Thumbnail.fragments.ThumbnailArticleData} | ||
`; | ||
|
||
const LOAD_USER_COMMENTS_STAT = gql` | ||
query LoadUserCommentsStat( | ||
$filter: ListReplyRequestFilter! | ||
$orderBy: [ListReplyRequestOrderBy] | ||
) { | ||
ListReplyRequests(filter: $filter, orderBy: $orderBy) { | ||
totalCount | ||
...LoadMoreConnectionForStats | ||
} | ||
} | ||
${LoadMore.fragments.LoadMoreConnectionForStats} | ||
`; | ||
|
||
const useStyles = makeStyles(theme => ({ | ||
tools: { | ||
[theme.breakpoints.up('sm')]: { | ||
marginLeft: 'var(--card-px)', | ||
marginRight: 'var(--card-px)', | ||
}, | ||
}, | ||
divider: { | ||
border: 0, | ||
margin: '16px 0 0', | ||
borderBottom: `1px dashed ${theme.palette.secondary[100]}`, | ||
}, | ||
infos: { | ||
marginBottom: 4, | ||
[theme.breakpoints.up('md')]: { | ||
marginBottom: 12, | ||
}, | ||
}, | ||
})); | ||
|
||
/** | ||
* @param {object} urlQuery - URL query object and urserId | ||
* @param {string} userId - The author ID of article reply to look for | ||
* @returns {object} ListArticleFilter | ||
*/ | ||
function urlQuery2Filter(query = {}, userId) { | ||
const filterObj = { userId }; | ||
|
||
const [start, end] = TimeRange.getValues(query); | ||
|
||
if (start) { | ||
filterObj.createdAt = { | ||
...filterObj.createdAt, | ||
GTE: start, | ||
}; | ||
} | ||
if (end) { | ||
filterObj.createdAt = { | ||
...filterObj.createdAt, | ||
LTE: end, | ||
}; | ||
} | ||
|
||
return filterObj; | ||
} | ||
|
||
function CommentTab({ userId }) { | ||
const classes = useStyles(); | ||
const { query } = useRouter(); | ||
|
||
const listQueryVars = { | ||
filter: urlQuery2Filter(query, userId), | ||
orderBy: [{ createdAt: 'DESC' }], | ||
}; | ||
|
||
const { | ||
loading, | ||
fetchMore, | ||
data: listCommentsData, | ||
error: listCommentsError, | ||
} = useQuery(LOAD_USER_COMMENTS, { | ||
skip: !userId, | ||
variables: listQueryVars, | ||
notifyOnNetworkStatusChange: true, // Make loading true on `fetchMore` | ||
}); | ||
|
||
// Separate these stats query so that it will be cached by apollo-client and sends no network request | ||
// on page change, but still works when filter options are updated. | ||
// | ||
const { data: listStatData } = useQuery(LOAD_USER_COMMENTS_STAT, { | ||
skip: !userId, | ||
variables: listQueryVars, | ||
}); | ||
|
||
// List data | ||
const commentEdges = listCommentsData?.ListReplyRequests?.edges || []; | ||
const statsData = listStatData?.ListReplyRequests || {}; | ||
const totalCount = statsData?.totalCount; | ||
|
||
if (!userId) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<> | ||
<Tools className={classes.tools}> | ||
<TimeRange /> | ||
</Tools> | ||
{loading && !totalCount ? ( | ||
<CardContent>{t`Loading...`}</CardContent> | ||
) : listCommentsError ? ( | ||
<CardContent>{listCommentsError.toString()}</CardContent> | ||
) : totalCount === 0 ? ( | ||
<CardContent>{t`This user does not provide comments to any message in the specified date range.`}</CardContent> | ||
) : ( | ||
<> | ||
<CardHeader> | ||
{ngettext( | ||
msgid`${totalCount} comment matching criteria`, | ||
`${totalCount} comments matching criteria`, | ||
totalCount | ||
)} | ||
</CardHeader> | ||
{commentEdges.map(({ node: { article, ...comment } }) => ( | ||
<CardContent key={comment.id} style={{ paddingBottom: 0 }}> | ||
<Infos className={classes.infos}> | ||
<> | ||
{ngettext( | ||
msgid`${article.replyRequestCount} occurrence`, | ||
`${article.replyRequestCount} occurrences`, | ||
article.replyRequestCount | ||
)} | ||
</> | ||
<TimeInfo time={article.createdAt}> | ||
{timeAgo => ( | ||
<Link href="/article/[id]" as={`/article/${article.id}`}> | ||
{t`First reported ${timeAgo}`} | ||
</Link> | ||
)} | ||
</TimeInfo> | ||
{article.replyCount > 0 && ( | ||
<> | ||
{ngettext( | ||
msgid`${article.replyCount} reply`, | ||
`${article.replyCount} replies`, | ||
article.replyCount | ||
)} | ||
</> | ||
)} | ||
</Infos> | ||
<Thumbnail article={article} /> | ||
{article.text && ( | ||
<ExpandableText lineClamp={3}>{article.text}</ExpandableText> | ||
)} | ||
|
||
<hr className={classes.divider} /> | ||
|
||
<ReplyRequestReason | ||
articleId={article.id} | ||
replyRequest={comment} | ||
/> | ||
</CardContent> | ||
))} | ||
|
||
<LoadMore | ||
edges={commentEdges} | ||
pageInfo={statsData?.pageInfo} | ||
loading={loading} | ||
onMoreRequest={args => | ||
fetchMore({ | ||
variables: args, | ||
updateQuery(prev, { fetchMoreResult }) { | ||
if (!fetchMoreResult) return prev; | ||
const newCommentData = fetchMoreResult?.ListReplyRequests; | ||
return { | ||
...prev, | ||
ListArticles: { | ||
...newCommentData, | ||
edges: [...commentEdges, ...newCommentData.edges], | ||
}, | ||
}; | ||
}, | ||
}) | ||
} | ||
/> | ||
</> | ||
)} | ||
</> | ||
); | ||
} | ||
|
||
export default CommentTab; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.