diff --git a/components/ProfilePage/CommentTab.js b/components/ProfilePage/CommentTab.js
new file mode 100644
index 00000000..02601ed2
--- /dev/null
+++ b/components/ProfilePage/CommentTab.js
@@ -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 (
+ <>
+
+
+
+ {loading && !totalCount ? (
+ {t`Loading...`}
+ ) : listCommentsError ? (
+ {listCommentsError.toString()}
+ ) : totalCount === 0 ? (
+ {t`This user does not provide comments to any message in the specified date range.`}
+ ) : (
+ <>
+
+ {ngettext(
+ msgid`${totalCount} comment matching criteria`,
+ `${totalCount} comments matching criteria`,
+ totalCount
+ )}
+
+ {commentEdges.map(({ node: { article, ...comment } }) => (
+
+
+ <>
+ {ngettext(
+ msgid`${article.replyRequestCount} occurrence`,
+ `${article.replyRequestCount} occurrences`,
+ article.replyRequestCount
+ )}
+ >
+
+ {timeAgo => (
+
+ {t`First reported ${timeAgo}`}
+
+ )}
+
+ {article.replyCount > 0 && (
+ <>
+ {ngettext(
+ msgid`${article.replyCount} reply`,
+ `${article.replyCount} replies`,
+ article.replyCount
+ )}
+ >
+ )}
+
+
+ {article.text && (
+ {article.text}
+ )}
+
+
+
+
+
+ ))}
+
+
+ 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;
diff --git a/components/ProfilePage/ProfilePage.js b/components/ProfilePage/ProfilePage.js
index e1a3917b..4f1c4b52 100644
--- a/components/ProfilePage/ProfilePage.js
+++ b/components/ProfilePage/ProfilePage.js
@@ -14,6 +14,7 @@ import AppLayout from 'components/AppLayout';
import { Card } from 'components/Card';
import UserPageHeader from './UserPageHeader';
import RepliedArticleTab from './RepliedArticleTab';
+import CommentTab from './CommentTab';
import ContributionChart from 'components/ContributionChart';
import { startOfWeek, subDays, format } from 'date-fns';
@@ -63,6 +64,9 @@ const LOAD_CONTRIBUTION = gql`
commentedReplies: ListArticleReplyFeedbacks(filter: { userId: $id }) {
totalCount
}
+ comments: ListReplyRequests(filter: { userId: $id }) {
+ totalCount
+ }
}
`;
@@ -75,6 +79,7 @@ function ProfilePage({ id, slug }) {
const { data: contributionData } = useQuery(LOAD_CONTRIBUTION, {
variables: { id: data?.GetUser?.id },
skip: !data?.GetUser?.id,
+ ssr: false, // Speed up SSR
});
const isSelf = currentUser && data?.GetUser?.id === currentUser.id;
@@ -123,8 +128,11 @@ function ProfilePage({ id, slug }) {
let contentElem = null;
switch (tab) {
case 'replies':
- default:
contentElem = ;
+ break;
+ case 'comments':
+ contentElem = ;
+ break;
}
const today = format(new Date(), 'yyyy-MM-dd');
const aYearAgo = format(
@@ -144,6 +152,7 @@ function ProfilePage({ id, slug }) {
stats={{
repliedArticles: contributionData?.repliedArticles?.totalCount,
commentedReplies: contributionData?.commentedReplies?.totalCount,
+ comments: contributionData?.comments?.totalCount,
}}
/>
{
- router.push({ query: { tab } });
+ router.push({ query: { tab, id, slug } });
}}
>
+
{contentElem}
diff --git a/components/ProfilePage/RepliedArticleTab.js b/components/ProfilePage/RepliedArticleTab.js
index 6fddebac..ef9ce286 100644
--- a/components/ProfilePage/RepliedArticleTab.js
+++ b/components/ProfilePage/RepliedArticleTab.js
@@ -1,6 +1,7 @@
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 { Box } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
@@ -272,7 +273,11 @@ function RepliedArticleTab({ userId }) {
)}
>
- {timeAgo => t`First reported ${timeAgo}`}
+ {timeAgo => (
+
+ {t`First reported ${timeAgo}`}
+
+ )}
diff --git a/components/ProfilePage/Stats.js b/components/ProfilePage/Stats.js
index c00e4ba1..8e3e7826 100644
--- a/components/ProfilePage/Stats.js
+++ b/components/ProfilePage/Stats.js
@@ -58,6 +58,7 @@ function Stats({ stats }) {
return (
);
diff --git a/components/ProfilePage/UserPageHeader.js b/components/ProfilePage/UserPageHeader.js
index a31f70b2..6b6fe4c4 100644
--- a/components/ProfilePage/UserPageHeader.js
+++ b/components/ProfilePage/UserPageHeader.js
@@ -179,7 +179,7 @@ const useStyles = makeStyles(theme => ({
*
* @param {object} props.user
* @param {boolean} props.isSelf - If the current user is the one in `user` prop
- * @param {{repliedArticles: number, commentedReplies: number}} props.stats
+ * @param {{repliedArticles: number, commentedReplies: number, comments: number}} props.stats
*/
function UserPageHeader({ user, isSelf, stats }) {
const classes = useStyles();
diff --git a/i18n/zh_TW.po b/i18n/zh_TW.po
index 8e82aacc..252e27cf 100644
--- a/i18n/zh_TW.po
+++ b/i18n/zh_TW.po
@@ -51,19 +51,19 @@ msgstr[0] "${ replyCount } 份回應"
msgid "Sort by"
msgstr "排序方式"
-#: components/ProfilePage/RepliedArticleTab.js:35
+#: components/ProfilePage/RepliedArticleTab.js:36
#: pages/articles.js:166
#: pages/replies.js:143
msgid "Most recently asked"
msgstr "最近被查詢"
-#: components/ProfilePage/RepliedArticleTab.js:32
+#: components/ProfilePage/RepliedArticleTab.js:33
#: pages/articles.js:168
#: pages/replies.js:142
msgid "Most recently replied"
msgstr "最近被回應"
-#: components/ProfilePage/RepliedArticleTab.js:36
+#: components/ProfilePage/RepliedArticleTab.js:37
#: pages/articles.js:167
#: pages/replies.js:144
msgid "Most asked"
@@ -234,7 +234,7 @@ msgid ""
"fighting mis/disinformation in Taiwan."
msgstr "一起來幫網路上的可疑訊息寫查核回應、共同教導聊天機器人回話,將多元的闢謠資訊傳遞給在 LINE 上收到謠言的人。"
-#: components/ProfilePage/ProfilePage.js:98
+#: components/ProfilePage/ProfilePage.js:103
#: pages/article/[id].js:300
#: pages/article/[id].js:304
#: pages/reply/[id].js:167
@@ -444,7 +444,8 @@ msgid "Why do you think it is not useful?"
msgstr "您認為沒有幫助的理由是什麼呢?"
#: components/LandingPage/SectionArticles.js:153
-#: components/ProfilePage/RepliedArticleTab.js:250
+#: components/ProfilePage/CommentTab.js:151
+#: components/ProfilePage/RepliedArticleTab.js:251
#: pages/articles.js:181
#: pages/hoax-for-you.js:133
#: pages/replies.js:246
@@ -701,7 +702,8 @@ msgid "Total Visit: ${ totalVisits }"
msgstr "${ totalVisits } 次瀏覽"
#: components/ListPageDisplays/ReplySearchItem.js:103
-#: components/ProfilePage/RepliedArticleTab.js:268
+#: components/ProfilePage/CommentTab.js:169
+#: components/ProfilePage/RepliedArticleTab.js:269
#: pages/replies.js:256
#, javascript-format
msgid "${ article.replyRequestCount } occurrence"
@@ -924,19 +926,19 @@ msgstr "你的個人頁面網址會變成 ${ profileURL }"
msgid "Bio"
msgstr "自我介紹"
-#: components/ProfilePage/ProfilePage.js:111
+#: components/ProfilePage/ProfilePage.js:116
msgid "User not found"
msgstr "找不到使用者"
-#: components/ProfilePage/ProfilePage.js:114
+#: components/ProfilePage/ProfilePage.js:119
msgid "The user does not exist"
msgstr "此使用者不存在"
-#: components/ProfilePage/ProfilePage.js:165
+#: components/ProfilePage/ProfilePage.js:174
msgid "Replied messages"
msgstr "查核回應"
-#: components/ProfilePage/RepliedArticleTab.js:254
+#: components/ProfilePage/RepliedArticleTab.js:255
msgid "No replied messages."
msgstr "無查核回應"
@@ -944,7 +946,7 @@ msgstr "無查核回應"
msgid "replied messages"
msgstr "查核回應"
-#: components/ProfilePage/Stats.js:61
+#: components/ProfilePage/Stats.js:62
msgid "voted replies"
msgstr "評價回應"
@@ -2295,7 +2297,8 @@ msgstr "目前已經存有4萬5千筆以上查證過的資訊,透過投入資
#: components/ListPageDisplays/ArticleCard.js:111
#: components/ListPageDisplays/ReplySearchItem.js:112
-#: components/ProfilePage/RepliedArticleTab.js:275
+#: components/ProfilePage/CommentTab.js:178
+#: components/ProfilePage/RepliedArticleTab.js:278
#: pages/article/[id].js:370
#: pages/replies.js:263
msgid "First reported ${ timeAgo }"
@@ -2385,11 +2388,11 @@ msgstr "注意:這裏不是討論區!請針對上面的「可疑訊息」撰
msgid "Comments from people reporting this message"
msgstr "網友回報補充"
-#: components/ProfilePage/RepliedArticleTab.js:34
+#: components/ProfilePage/RepliedArticleTab.js:35
msgid "Most recently replied by any user"
msgstr "最近有人回應"
-#: components/ProfilePage/RepliedArticleTab.js:258
+#: components/ProfilePage/RepliedArticleTab.js:259
#, javascript-format
msgid "${ totalCount } message matching criteria"
msgid_plural "${ totalCount } messages matching criteria"
@@ -2440,6 +2443,31 @@ msgstr "語音訊息"
msgid "Format"
msgstr "格式"
+#: components/ProfilePage/CommentTab.js:155
+msgid ""
+"This user does not provide comments to any message in the specified date "
+"range."
+msgstr "這名使用者在指定日期範圍內沒有提供任何回報補充"
+
+#: components/ProfilePage/CommentTab.js:159
+#, javascript-format
+msgid "${ totalCount } comment matching criteria"
+msgid_plural "${ totalCount } comments matching criteria"
+msgstr[0] "${ totalCount } 份符合篩選條件的回報補充"
+
+#: components/ProfilePage/ProfilePage.js:175
+msgid "Comments"
+msgstr "回報補充"
+
+#: components/ProfilePage/Stats.js:61
+msgid "comments"
+msgstr "回報補充"
+
+#: components/ProfilePage/CommentTab.js:184
+msgid "${ article.replyCount } reply"
+msgid_plural "${ article.replyCount } replies"
+msgstr[0] "${ article.replyCount } 則回應"
+
#: pages/index.js:29
msgctxt "site title"
msgid "Cofacts"