From eb67d86163fc54bc015fbb049ef6cc25a50cfdd0 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 29 Aug 2017 18:57:20 +0700 Subject: [PATCH 01/18] Implement UserConnection on the graph --- graph/loaders/users.js | 42 ++++++++++++++++++++++++++++++++--------- graph/resolvers/user.js | 5 +++-- graph/typeDefs.graphql | 18 +++++++++++++++++- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/graph/loaders/users.js b/graph/loaders/users.js index 999ad1938..3d48bafd7 100644 --- a/graph/loaders/users.js +++ b/graph/loaders/users.js @@ -26,12 +26,12 @@ const genUserByIDs = async (context, ids) => { * @param {Object} context graph context * @param {Object} query query terms to apply to the users query */ -const getUsersByQuery = ({user}, {ids, limit, cursor, statuses = null, sortOrder}) => { +const getUsersByQuery = async ({user}, {ids, limit, cursor, statuses = null, sortOrder}) => { - let users = UserModel.find(); + let query = UserModel.find(); if (ids) { - users = users.find({ + query = query.find({ id: { $in: ids } @@ -39,7 +39,7 @@ const getUsersByQuery = ({user}, {ids, limit, cursor, statuses = null, sortOrder } if (statuses != null) { - users = users.where({ + query = query.where({ status: { $in: statuses } @@ -48,13 +48,13 @@ const getUsersByQuery = ({user}, {ids, limit, cursor, statuses = null, sortOrder if (cursor) { if (sortOrder === 'DESC') { - users = users.where({ + query = query.where({ created_at: { $lt: cursor } }); } else { - users = users.where({ + query = query.where({ created_at: { $gt: cursor } @@ -62,9 +62,33 @@ const getUsersByQuery = ({user}, {ids, limit, cursor, statuses = null, sortOrder } } - return users - .sort({created_at: sortOrder === 'DESC' ? -1 : 1}) - .limit(limit); + // Apply the limit. + if (limit) { + query = query.limit(limit + 1); + } + + const nodes = await query.exec(); + + // The hasNextPage is always handled the same (ask for one more than we need, + // if there is one more, than there is more). + let hasNextPage = false; + if (limit && nodes.length > limit) { + + // There was one more than we expected! Set hasNextPage = true and remove + // the last item from the array that we requested. + hasNextPage = true; + nodes.splice(limit, 1); + } + + const startCursor = nodes.length ? nodes[0].created_at : null; + const endCursor = nodes.length ? nodes[nodes.length - 1].created_at : null; + + return { + startCursor, + endCursor, + hasNextPage, + nodes, + }; }; /** diff --git a/graph/resolvers/user.js b/graph/resolvers/user.js index 57601c269..e5c5746ca 100644 --- a/graph/resolvers/user.js +++ b/graph/resolvers/user.js @@ -51,7 +51,7 @@ const User = { return tokens; }, - ignoredUsers({id}, args, {user, loaders: {Users}}) { + async ignoredUsers({id}, args, {user, loaders: {Users}}) { // Only allow a logged in user that is either the current user or is a staff // member to access the ignoredUsers of a given user. @@ -64,7 +64,8 @@ const User = { return []; } - return Users.getByQuery({ids: user.ignoresUsers}); + const connection = await Users.getByQuery({ids: user.ignoresUsers}); + return connection.nodes; }, roles({id, roles}, _, {user}) { diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 4dff623fd..5bebb7e76 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -121,6 +121,22 @@ type User { suspension: SuspensionInfo } +# UserConnection represents a paginable subset of a user list. +type UserConnection { + + # Indicates that there are more users after this subset. + hasNextPage: Boolean! + + # Cursor of first user in subset. + startCursor: Cursor + + # Cursor of last user in subset. + endCursor: Cursor + + # Subset of users. + nodes: [User!]! +} + # UsersQuery allows the ability to query users by a specific fields. input UsersQuery { action_type: ACTION_TYPE @@ -731,7 +747,7 @@ type RootQuery { me: User # Users returned based on a query. - users(query: UsersQuery): [User] + users(query: UsersQuery!): UserConnection # a single User by id user(id: ID!): User From 87e90e956d57486b02439fc8b21fc008f336efe6 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 29 Aug 2017 19:06:58 +0700 Subject: [PATCH 02/18] Port current frontend to connections --- .../routes/Community/components/Community.js | 2 +- .../routes/Community/containers/Community.js | 38 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/Community.js b/client/coral-admin/src/routes/Community/components/Community.js index b2451cd9a..f7e91c590 100644 --- a/client/coral-admin/src/routes/Community/components/Community.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -77,7 +77,7 @@ export default class Community extends Component { return (
Date: Tue, 29 Aug 2017 22:23:35 +0700 Subject: [PATCH 03/18] Refactor and implement pagination --- .../src/containers/SuspendUserDialog.js | 3 +- .../routes/Community/components/Community.js | 15 +-- .../Community/components/FlaggedAccounts.js | 39 ++++-- .../components/{User.js => FlaggedUser.js} | 2 +- .../routes/Community/containers/Community.js | 75 +---------- .../Community/containers/FlaggedAccounts.js | 120 ++++++++++++++++++ .../Community/containers/FlaggedUser.js | 42 ++++++ 7 files changed, 197 insertions(+), 99 deletions(-) rename client/coral-admin/src/routes/Community/components/{User.js => FlaggedUser.js} (98%) create mode 100644 client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js create mode 100644 client/coral-admin/src/routes/Community/containers/FlaggedUser.js diff --git a/client/coral-admin/src/containers/SuspendUserDialog.js b/client/coral-admin/src/containers/SuspendUserDialog.js index f60014832..1616acfcf 100644 --- a/client/coral-admin/src/containers/SuspendUserDialog.js +++ b/client/coral-admin/src/containers/SuspendUserDialog.js @@ -54,8 +54,9 @@ class SuspendUserDialogContainer extends Component { const withOrganizationName = withQuery(gql` query CoralAdmin_SuspendUserDialog { + __typename settings { - organizationName + organizationName } } `); diff --git a/client/coral-admin/src/routes/Community/components/Community.js b/client/coral-admin/src/routes/Community/components/Community.js index f7e91c590..e59f9b6ca 100644 --- a/client/coral-admin/src/routes/Community/components/Community.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import CommunityMenu from './CommunityMenu'; import People from './People'; -import FlaggedAccounts from './FlaggedAccounts'; +import FlaggedAccounts from '../containers/FlaggedAccounts'; import RejectUsernameDialog from './RejectUsernameDialog'; export default class Community extends Component { @@ -54,7 +54,7 @@ export default class Community extends Component { } getTabContent(searchValue, props) { - const {community, root: {users}, viewUserDetail} = props; + const {community} = props; const activeTab = props.route.path === ':id' ? 'flagged' : props.route.path; if (activeTab === 'people') { @@ -76,16 +76,7 @@ export default class Community extends Component { return (
- + { - const {commenters} = props; - const hasResults = commenters && !!commenters.length; + const { + users, + loadMore, + showBanUserDialog, + showSuspendUserDialog, + showRejectUsernameDialog, + approveUser, + me, + viewUserDetail, + } = props; + + const hasResults = users.nodes && !!users.nodes.length; return (
{ hasResults - ? commenters.map((commenter, index) => { - return { + return ; }) : {t('community.no_flagged_accounts')} } +
); diff --git a/client/coral-admin/src/routes/Community/components/User.js b/client/coral-admin/src/routes/Community/components/FlaggedUser.js similarity index 98% rename from client/coral-admin/src/routes/Community/components/User.js rename to client/coral-admin/src/routes/Community/components/FlaggedUser.js index 2d921a1c7..736e3a992 100644 --- a/client/coral-admin/src/routes/Community/components/User.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.js @@ -39,7 +39,7 @@ const User = (props) => {
- {props.currentUser.id !== user.id && + {props.me.id !== user.id && { - return this.props.setUserStatus({userId, status: 'APPROVED'}); - } - - banUser = ({userId}) => { - return this.props.setUserStatus({userId, status: 'BANNED'}); - } - render() { - if (this.props.data.error) { - return
{this.props.data.error.message}
; - } - - if (!('users' in this.props.root)) { - return
; - } return ( - + ); } } - -export const withCommunityQuery = withQuery(gql` - query CoralAdmin_Community($action_type: ACTION_TYPE) { - users(query:{action_type: $action_type}){ - hasNextPage - endCursor - nodes { - id - username - status - roles - actions{ - id - created_at - ... on FlagAction { - reason - message - user { - id - username - } - } - } - action_summaries { - count - ... on FlagActionSummary { - reason - } - } - } - } - } -`, { - options: ({params: {action_type = 'FLAG'}}) => { - return { - variables: { - action_type: action_type - } - }; - } -}); - const mapStateToProps = (state) => ({ community: state.community, - currentUser: state.auth.user, }); const mapDispatchToProps = (dispatch) => bindActionCreators({ fetchAccounts, - showBanUserDialog, - showSuspendUserDialog, - showRejectUsernameDialog, hideRejectUsernameDialog, updateSorting, newPage, - viewUserDetail, }, dispatch); export default compose( connect(mapStateToProps, mapDispatchToProps), - withCommunityQuery, withSetUserStatus, withRejectUsername, )(CommunityContainer); diff --git a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js new file mode 100644 index 000000000..29439e362 --- /dev/null +++ b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js @@ -0,0 +1,120 @@ +import React, {Component} from 'react'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {compose, gql} from 'react-apollo'; +import withQuery from 'coral-framework/hocs/withQuery'; +import {Spinner} from 'coral-ui'; + +import {withSetUserStatus} from 'coral-framework/graphql/mutations'; +import {showBanUserDialog} from 'actions/banUserDialog'; +import {showSuspendUserDialog} from 'actions/suspendUserDialog'; +import {showRejectUsernameDialog} from '../../../actions/community'; +import {viewUserDetail} from '../../../actions/userDetail'; +import {getDefinitionName} from 'coral-framework/utils'; +import {appendNewNodes} from 'plugin-api/beta/client/utils'; +import update from 'immutability-helper'; + +import FlaggedAccounts from '../components/FlaggedAccounts'; +import FlaggedUser from '../containers/FlaggedUser'; + +class FlaggedAccountsContainer extends Component { + + approveUser = ({userId}) => { + return this.props.setUserStatus({userId, status: 'APPROVED'}); + } + + loadMore = () => { + return this.props.data.fetchMore({ + query: LOAD_MORE_QUERY, + variables: { + limit: 5, + cursor: this.props.root.users.endCursor, + }, + updateQuery: (previous, {fetchMoreResult:{users}}) => { + const updated = update(previous, { + users: { + nodes: { + $apply: (nodes) => appendNewNodes(nodes, users.nodes), + }, + hasNextPage: {$set: users.hasNextPage}, + endCursor: {$set: users.endCursor}, + }, + }); + return updated; + }, + }); + }; + + render() { + if (this.props.data.error) { + return
{this.props.data.error.message}
; + } + + if (this.props.data.loading) { + return
; + } + return ( + + ); + } +} + +const LOAD_MORE_QUERY = gql` + query TalkAdmin_LoadMoreFlaggedAccounts($limit: Int, $cursor: Cursor) { + users(query:{action_type: FLAG, limit: $limit, cursor: $cursor}){ + hasNextPage + endCursor + nodes { + __typename + ...${getDefinitionName(FlaggedUser.fragments.user)} + } + } + } + ${FlaggedUser.fragments.user} +`; + +export const withFlaggedAccountsyQuery = withQuery(gql` + query TalkAdmin_FlaggedAccounts { + ...${getDefinitionName(FlaggedUser.fragments.root)} + users(query:{action_type: FLAG, limit: 10}){ + hasNextPage + endCursor + nodes { + __typename + ...${getDefinitionName(FlaggedUser.fragments.user)} + } + } + me { + __typename + ...${getDefinitionName(FlaggedUser.fragments.me)} + } + } + ${FlaggedUser.fragments.root} + ${FlaggedUser.fragments.user} + ${FlaggedUser.fragments.me} +`); + +const mapDispatchToProps = (dispatch) => + bindActionCreators({ + showBanUserDialog, + showSuspendUserDialog, + showRejectUsernameDialog, + viewUserDetail, + }, dispatch); + +export default compose( + connect(null, mapDispatchToProps), + withFlaggedAccountsyQuery, + withSetUserStatus, +)(FlaggedAccountsContainer); diff --git a/client/coral-admin/src/routes/Community/containers/FlaggedUser.js b/client/coral-admin/src/routes/Community/containers/FlaggedUser.js new file mode 100644 index 000000000..f92c93c1c --- /dev/null +++ b/client/coral-admin/src/routes/Community/containers/FlaggedUser.js @@ -0,0 +1,42 @@ +import {gql} from 'react-apollo'; +import FlaggedUser from '../components/FlaggedUser'; +import {withFragments} from 'plugin-api/beta/client/hocs'; + +export default withFragments({ + root: gql` + fragment TalkAdminCommunity_FlaggedUser_root on RootQuery { + __typename + } + `, + me: gql` + fragment TalkAdminCommunity_FlaggedUser_me on User { + id + } + `, + user: gql` + fragment TalkAdminCommunity_FlaggedUser_user on User { + id + username + status + roles + actions{ + id + created_at + ... on FlagAction { + reason + message + user { + id + username + } + } + } + action_summaries { + count + ... on FlagActionSummary { + reason + } + } + } + ` +})(FlaggedUser); From ddced5b8c91171701da78f4ec77fce5997aeff81 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 29 Aug 2017 23:43:58 +0700 Subject: [PATCH 04/18] Refactor styling --- .../Community/components/ActionButton.css | 4 + .../Community/components/ActionButton.js | 2 +- .../routes/Community/components/Community.css | 299 ------------------ .../Community/components/CommunityMenu.css | 21 ++ .../Community/components/CommunityMenu.js | 2 +- .../Community/components/FlaggedAccounts.css | 11 + .../Community/components/FlaggedAccounts.js | 2 +- .../Community/components/FlaggedUser.css | 17 + .../Community/components/FlaggedUser.js | 2 +- .../src/routes/Community/components/People.js | 2 +- .../routes/Community/components/styles.css | 226 +++++-------- 11 files changed, 143 insertions(+), 445 deletions(-) create mode 100644 client/coral-admin/src/routes/Community/components/ActionButton.css delete mode 100644 client/coral-admin/src/routes/Community/components/Community.css create mode 100644 client/coral-admin/src/routes/Community/components/CommunityMenu.css create mode 100644 client/coral-admin/src/routes/Community/components/FlaggedUser.css diff --git a/client/coral-admin/src/routes/Community/components/ActionButton.css b/client/coral-admin/src/routes/Community/components/ActionButton.css new file mode 100644 index 000000000..8f328e8b8 --- /dev/null +++ b/client/coral-admin/src/routes/Community/components/ActionButton.css @@ -0,0 +1,4 @@ +.actionButton { + transform: scale(.8); + margin: 0; +} diff --git a/client/coral-admin/src/routes/Community/components/ActionButton.js b/client/coral-admin/src/routes/Community/components/ActionButton.js index 09af420f6..400b59ab3 100644 --- a/client/coral-admin/src/routes/Community/components/ActionButton.js +++ b/client/coral-admin/src/routes/Community/components/ActionButton.js @@ -1,5 +1,5 @@ import React from 'react'; -import styles from './Community.css'; +import styles from './ActionButton.css'; import {Button} from 'coral-ui'; import {menuActionsMap} from '../../../utils/moderationQueueActionsMap'; diff --git a/client/coral-admin/src/routes/Community/components/Community.css b/client/coral-admin/src/routes/Community/components/Community.css deleted file mode 100644 index 629045ab8..000000000 --- a/client/coral-admin/src/routes/Community/components/Community.css +++ /dev/null @@ -1,299 +0,0 @@ -@custom-media --big-viewport (min-width: 780px); - -.container { - padding: 10px; - display: flex; - padding-bottom: 200px; -} - -.leftColumn { - padding: 42px 56px; - width: 234px; -} - -.mainContent { - width: calc(100% - 300px); - padding: 34px 14px; - box-sizing: border-box; -} - -.mainFlaggedContent { - width: 100%; - padding: 34px 14px; - box-sizing: border-box; -} - -.roleButton { - display: block; -} - -.searchBox { - width: 100%; - padding: 9px; - border: 1px solid #ccc; - border-radius: 2px; - display: flex; - background: white; - box-sizing: border-box; - height: 40px; - - i { - color: #A1A1A1 - } - - input { - display: block; - width: 100%; - height: 100%; - border: none; - font-size: 16px; - padding: 0 2px 0 15px; - box-sizing: border-box; - } -} - -.list { - padding: 8px 0; - list-style: none; - display: block; - - &.singleView .listItem { - display: none; - } - - &.singleView .listItem.activeItem { - display: block; - height: 100%; - font-size: 1.5em; - line-height: 1.5em; - border: none; - - .actions { - position: fixed; - bottom: 60px; - left: 25%; - margin: 0 auto; - display: flex; - justify-content: space-around; - width: 50%; - margin: 0; - } - - .actionButton { - transform: scale(1.4); - } - } -} - -.listItem { - border-bottom: 1px solid #e0e0e0; - font-size: 18px; - width: 100%; - max-width: 660px; - min-width: 400px; - margin: 0 auto; - position: relative; - transition: all 200ms; - padding: 10px 0 0; - min-height: 220px; - - .container { - display: block; - padding: 0 14px; - min-height: 180px; - } - - &:hover { - box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); - } - - &:last-child { - border-bottom: none; - } - - .context { - a { - color: #f36451; - text-decoration: underline; - float: right; - } - } - - .sideActions { - position: absolute; - right: 0; - height: 100%; - top: 0; - padding: 50px 18px; - box-sizing: border-box; - } - - .itemHeader { - display: flex; - align-items: center; - justify-content: space-between; - - .author { - font-size: 16px; - font-weight: bold; - min-width: 230px; - display: flex; - align-items: center; - } - } - - .itemBody { - display: flex; - justify-content: space-between; - } - - .avatar { - margin-right: 16px; - height: 40px; - width: 40px; - border-radius: 50%; - background-color: #757575; - font-size: 40px; - color: #fff; - } - - .created { - color: #666; - font-size: 13px; - margin-left: 40px; - } - - .actionButton { - transform: scale(.8); - margin: 0; - } - - .body { - margin-top: 0px; - flex: 1; - color: black; - max-width: 500px; - word-wrap: break-word; - } - - .flagged { - color: rgba(255, 0, 0, .5); - padding-top: 15px; - padding-left: 10px; - } - - .flagCount{ - font-size: 12px; - color: #d32f2f; - } - -} - -.empty { - color: #444; - margin-top: 50px; - text-align: center; -} - - -@media (--big-viewport) { - .listItem { - border: 1px solid #e0e0e0; - margin-bottom: 30px; - - &:last-child { - border-bottom: 1px solid #e0e0e0; - } - - &.activeItem { - border: 2px solid #333; - } - } - -} - -.hasLinks { - color: #f00; - text-align: right; - display: flex; - align-items: center; - - i { - margin-right: 5px; - } -} - -.banned { - color: #f00; - text-align: left; - display: flex; - align-items: center; - - i { - margin-right: 5px; - } -} - -.ban { - display: block; - text-align: center; - margin-top: 5px; -} - -.banButton { - width: 114px; - letter-spacing: 1px; - - i { - vertical-align: middle; - margin-right: 10px; - font-size: 14px; - } -} - -.actionButton { - transform: scale(.8); - margin: 0; -} - -.flaggedByCount { - display: block; - text-align: left; - margin-top: 5px; -} - -.flaggedByCount i { - font-size: 14px; - margin-right: 10px; -} - -.flaggedBy { - display: inline; - padding: 3px; - font-size: 14px; - margin-left: 5px; -} - -.flaggedByLabel { - font-weight: 600; - font-size: 14px; -} - -.flaggedReasons { - margin-left: 24px; - margin-top: 10px; -} - -p.flaggedByReason { - font-size: 1tpx; - margin: 0px; - line-height: 1.4; -} - -.button { - composes: buttonReset from 'coral-framework/styles/reset.css'; - vertical-align: text-bottom; - &:hover { - background-color: #E0E0E0; - } -} diff --git a/client/coral-admin/src/routes/Community/components/CommunityMenu.css b/client/coral-admin/src/routes/Community/components/CommunityMenu.css new file mode 100644 index 000000000..c6313828a --- /dev/null +++ b/client/coral-admin/src/routes/Community/components/CommunityMenu.css @@ -0,0 +1,21 @@ +.tab { + flex: 1; + color: white; + text-transform: capitalize; + font-weight: 500; + font-size: 15px; + letter-spacing: 1px; + transition: border-bottom 200ms; +} + +.tabBar { + background-color: rgba(44, 44, 44, 0.89); + z-index: 5; +} + +.active { + color: white; + box-sizing: border-box; + border-bottom: solid 5px #F36451; +} + diff --git a/client/coral-admin/src/routes/Community/components/CommunityMenu.js b/client/coral-admin/src/routes/Community/components/CommunityMenu.js index b89e9350a..1e226313d 100644 --- a/client/coral-admin/src/routes/Community/components/CommunityMenu.js +++ b/client/coral-admin/src/routes/Community/components/CommunityMenu.js @@ -1,6 +1,6 @@ import React from 'react'; -import styles from './styles.css'; +import styles from './CommunityMenu.css'; import t from 'coral-framework/services/i18n'; import {Link} from 'react-router'; diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css index e69de29bb..6c7173c3d 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css @@ -0,0 +1,11 @@ +.container { + padding: 10px; + display: flex; + padding-bottom: 200px; +} + +.mainFlaggedContent { + width: 100%; + padding: 34px 14px; + box-sizing: border-box; +} diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js index b455dfff1..da3d03e61 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js @@ -1,7 +1,7 @@ import React from 'react'; import t from 'coral-framework/services/i18n'; -import styles from './Community.css'; +import styles from './FlaggedAccounts.css'; import EmptyCard from 'coral-admin/src/components/EmptyCard'; import LoadMore from '../../../components/LoadMore'; import FlaggedUser from '../containers/FlaggedUser'; diff --git a/client/coral-admin/src/routes/Community/components/FlaggedUser.css b/client/coral-admin/src/routes/Community/components/FlaggedUser.css new file mode 100644 index 000000000..d5648fa61 --- /dev/null +++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.css @@ -0,0 +1,17 @@ +.listItem { + border-bottom: 1px solid #e0e0e0; + font-size: 18px; + width: 100%; + max-width: 660px; + min-width: 400px; + margin: 0 auto; + position: relative; + transition: all 200ms; + padding: 10px 0 0; + min-height: 220px; + + &:hover { + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); + } +} + diff --git a/client/coral-admin/src/routes/Community/components/FlaggedUser.js b/client/coral-admin/src/routes/Community/components/FlaggedUser.js index 736e3a992..5f0136367 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedUser.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.js @@ -1,5 +1,5 @@ import React from 'react'; -import styles from './Community.css'; +import styles from './styles.css'; import ActionButton from './ActionButton'; import {username} from 'talk-plugin-flags/helpers/flagReasons'; diff --git a/client/coral-admin/src/routes/Community/components/People.js b/client/coral-admin/src/routes/Community/components/People.js index 464f3bc5b..64c1aaf6f 100644 --- a/client/coral-admin/src/routes/Community/components/People.js +++ b/client/coral-admin/src/routes/Community/components/People.js @@ -1,6 +1,6 @@ import React from 'react'; -import styles from './Community.css'; +import styles from './styles.css'; import Table from '../containers/Table'; import {Pager, Icon} from 'coral-ui'; import EmptyCard from '../../../components/EmptyCard'; diff --git a/client/coral-admin/src/routes/Community/components/styles.css b/client/coral-admin/src/routes/Community/components/styles.css index 8581c4c7d..f647da86b 100644 --- a/client/coral-admin/src/routes/Community/components/styles.css +++ b/client/coral-admin/src/routes/Community/components/styles.css @@ -1,119 +1,51 @@ @custom-media --big-viewport (min-width: 780px); -.listContainer { - max-width: 860px; - margin: 0 auto; -} - -.tabBar { - background-color: rgba(44, 44, 44, 0.89); - z-index: 5; -} - -.tab { - flex: 1; - color: white; - text-transform: capitalize; - font-weight: 500; - font-size: 15px; - letter-spacing: 1px; - transition: border-bottom 200ms; -} - -.active { - color: white; - box-sizing: border-box; - border-bottom: solid 5px #F36451; -} - -.active > span { - color: white; -} - -.active:after { - background: transparent !important; -} - -.showShortcuts { - position: absolute; - right: 130px; +.container { + padding: 10px; display: flex; - align-items: center; - font-size: 13px; - -span { - margin-left: 7px; -} + padding-bottom: 200px; } -@media (--big-viewport) { - .tab { - flex: none; - } +.leftColumn { + padding: 42px 56px; + width: 234px; } -.approve { - margin-top: 10px; +.mainContent { + width: calc(100% - 300px); + padding: 34px 14px; + box-sizing: border-box; } -.notFound { - position: relative; - margin: 20px auto; - text-align: center; - padding: 68px 45px; - vertical-align: middle; - min-width: 500px; - - a { - color: rgb(244, 126, 107); - font-weight: 500; - - &.goToStreams { - position: absolute; - right: 10px; - bottom: 10px; - } - } +.roleButton { + display: block; } -.header { - background-color: #2c2c2c; - color: white; - margin-bottom: -1px; +.searchBox { + width: 100%; + padding: 9px; + border: 1px solid #ccc; + border-radius: 2px; + display: flex; + background: white; + box-sizing: border-box; + height: 40px; - .settingsButton { - i { - vertical-align: middle; - margin-left: 10px; - margin-top: -4px; - } + i { + color: #A1A1A1 } - .moderateAsset { - a { - text-align: center; - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; - color: white; - text-transform: capitalize; - font-weight: 500; - font-size: 15px; - letter-spacing: 1px; - transition: opacity 200ms; - opacity: 1; - - &:hover { - opacity: .8; - cursor: pointer; - } - } + input { + display: block; + width: 100%; + height: 100%; + border: none; + font-size: 16px; + padding: 0 2px 0 15px; + box-sizing: border-box; } } - -@custom-media --big-viewport (min-width: 780px); - .list { padding: 8px 0; list-style: none; @@ -134,15 +66,12 @@ span { position: fixed; bottom: 60px; left: 25%; + margin: 0 auto; display: flex; justify-content: space-around; width: 50%; margin: 0; } - - .actionButton { - transform: scale(1.4); - } } } @@ -159,6 +88,7 @@ span { min-height: 220px; .container { + display: block; padding: 0 14px; min-height: 180px; } @@ -184,9 +114,9 @@ span { right: 0; height: 100%; top: 0; - padding: 40px 18px; + padding: 50px 18px; box-sizing: border-box; - } + } .itemHeader { display: flex; @@ -194,6 +124,8 @@ span { justify-content: space-between; .author { + font-size: 16px; + font-weight: bold; min-width: 230px; display: flex; align-items: center; @@ -221,18 +153,12 @@ span { margin-left: 40px; } - .actionButton { - transform: scale(.8); - margin: 0; - } - .body { margin-top: 0px; flex: 1; color: black; max-width: 500px; word-wrap: break-word; - font-weight: 300; } .flagged { @@ -245,11 +171,8 @@ span { font-size: 12px; color: #d32f2f; } - } - - .empty { color: #444; margin-top: 50px; @@ -301,34 +224,55 @@ span { margin-top: 5px; } -.Comment { - .moderateArticle { - font-size: 12px; - a { - display: inline-block; - color: #679af3; - text-decoration: none; - font-size: 1em; - font-weight: 400; - letter-spacing: .5px; - font-size: 12px; - margin-left: 10px; +.banButton { + width: 114px; + letter-spacing: 1px; - &:hover { - text-decoration: underline; - opacity: .9; - cursor: pointer; - } - } - } -} - -.flagBox { - max-width: 480px; - border-top: 1px solid rgba(66, 66, 66, 0.12); - h3 { + i { + vertical-align: middle; + margin-right: 10px; font-size: 14px; - margin: 0; - font-weight: 500; + } +} + +.flaggedByCount { + display: block; + text-align: left; + margin-top: 5px; +} + +.flaggedByCount i { + font-size: 14px; + margin-right: 10px; +} + +.flaggedBy { + display: inline; + padding: 3px; + font-size: 14px; + margin-left: 5px; +} + +.flaggedByLabel { + font-weight: 600; + font-size: 14px; +} + +.flaggedReasons { + margin-left: 24px; + margin-top: 10px; +} + +p.flaggedByReason { + font-size: 1tpx; + margin: 0px; + line-height: 1.4; +} + +.button { + composes: buttonReset from 'coral-framework/styles/reset.css'; + vertical-align: text-bottom; + &:hover { + background-color: #E0E0E0; } } From ffab9ca8d59b0b1440f551e80ff3833ea78a5025 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 18:48:20 +0700 Subject: [PATCH 05/18] Refactor users query --- .../src/routes/Community/components/FlaggedUser.js | 8 +++----- .../src/routes/Community/containers/FlaggedAccounts.js | 2 +- graph/loaders/users.js | 10 ++++++++-- graph/resolvers/root_query.js | 8 +------- graph/typeDefs.graphql | 3 +++ 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/FlaggedUser.js b/client/coral-admin/src/routes/Community/components/FlaggedUser.js index 5f0136367..3526bcc37 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedUser.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.js @@ -19,7 +19,6 @@ const shortReasons = { // Render a single user for the list const User = (props) => { const {user, modActionButtons} = props; - let userStatus = user.status; const showSuspenUserDialog = () => props.showSuspendUserDialog({ userId: user.id, @@ -31,9 +30,7 @@ const User = (props) => { username: user.username, }); - // Do not display unless the user status is 'pending' or 'banned'. - // This means that they have already been reviewed and approved. - return (userStatus === 'PENDING' || userStatus === 'BANNED') && + return (
  • @@ -111,7 +108,8 @@ const User = (props) => {
  • - ; + + ); }; export default User; diff --git a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js index 29439e362..ad3339bd5 100644 --- a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js @@ -87,7 +87,7 @@ const LOAD_MORE_QUERY = gql` export const withFlaggedAccountsyQuery = withQuery(gql` query TalkAdmin_FlaggedAccounts { ...${getDefinitionName(FlaggedUser.fragments.root)} - users(query:{action_type: FLAG, limit: 10}){ + users(query:{action_type: FLAG, statuses: [PENDING], limit: 10}){ hasNextPage endCursor nodes { diff --git a/graph/loaders/users.js b/graph/loaders/users.js index 3d48bafd7..8ef8bd6cf 100644 --- a/graph/loaders/users.js +++ b/graph/loaders/users.js @@ -1,6 +1,7 @@ const DataLoader = require('dataloader'); const util = require('./util'); +const union = require('lodash/union'); const UsersService = require('../../services/users'); const UserModel = require('../../models/user'); @@ -26,10 +27,15 @@ const genUserByIDs = async (context, ids) => { * @param {Object} context graph context * @param {Object} query query terms to apply to the users query */ -const getUsersByQuery = async ({user}, {ids, limit, cursor, statuses = null, sortOrder}) => { +const getUsersByQuery = async ({user, loaders: {Actions}}, {ids, limit, cursor, statuses, action_type, sortOrder}) => { let query = UserModel.find(); + if (action_type) { + const userIds = await Actions.getByTypes({action_type, item_type: 'USERS'}); + ids = ids ? union(ids, userIds) : userIds; + } + if (ids) { query = query.find({ id: { @@ -38,7 +44,7 @@ const getUsersByQuery = async ({user}, {ids, limit, cursor, statuses = null, sor }); } - if (statuses != null) { + if (statuses) { query = query.where({ status: { $in: statuses diff --git a/graph/resolvers/root_query.js b/graph/resolvers/root_query.js index 1e34e8ecd..239b052c0 100644 --- a/graph/resolvers/root_query.js +++ b/graph/resolvers/root_query.js @@ -92,17 +92,11 @@ const RootQuery = { // This endpoint is used for loading the user moderation queues (users whose username has been flagged), // so hide it in the event that we aren't an admin. - async users(_, {query}, {user, loaders: {Users, Actions}}) { + async users(_, {query}, {user, loaders: {Users}}) { if (user == null || !user.can(SEARCH_OTHER_USERS)) { return null; } - const {action_type} = query; - if (action_type) { - query.ids = await Actions.getByTypes({action_type, item_type: 'USERS'}); - query.statuses = ['PENDING']; - } - return Users.getByQuery(query); } }; diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 5bebb7e76..79b0f59c0 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -141,6 +141,9 @@ type UserConnection { input UsersQuery { action_type: ACTION_TYPE + # Current status of a user. Requires the `ADMIN` role. + statuses: [USER_STATUS!] + # Limit the number of results to be returned. limit: Int = 10 From 0df7df9dd6b99871a5836e439c4e658dbf485c74 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 19:03:55 +0700 Subject: [PATCH 06/18] Wrap ul before li --- .../Community/components/FlaggedAccounts.js | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js index da3d03e61..1d74fd4ae 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js @@ -25,20 +25,26 @@ const FlaggedAccounts = (props) => {
    { hasResults - ? users.nodes.map((user, index) => { - return ; - }) + ?
      + { + users.nodes.map((user, index) => { + return ( + + ); + }) + } +
    : {t('community.no_flagged_accounts')} } Date: Wed, 30 Aug 2017 19:04:30 +0700 Subject: [PATCH 07/18] Refactor into a ComponentClass --- .../Community/components/FlaggedUser.js | 174 ++++++++++-------- 1 file changed, 95 insertions(+), 79 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/FlaggedUser.js b/client/coral-admin/src/routes/Community/components/FlaggedUser.js index 3526bcc37..f77b95b90 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedUser.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.js @@ -17,99 +17,115 @@ const shortReasons = { }; // Render a single user for the list -const User = (props) => { - const {user, modActionButtons} = props; +class User extends React.Component { - const showSuspenUserDialog = () => props.showSuspendUserDialog({ - userId: user.id, - username: user.username, + showSuspenUserDialog = () => this.props.showSuspendUserDialog({ + userId: this.props.user.id, + username: this.props.user.username, }); - const showBanUserDialog = () => props.showBanUserDialog({ - userId: user.id, - username: user.username, + showBanUserDialog = () => this.props.showBanUserDialog({ + userId: this.props.user.id, + username: this.props.user.username, }); - return ( -
  • -
    -
    -
    - - {props.me.id !== user.id && - - - Suspend User - - Ban User - - - } + viewAuthorDetail = () => this.props.viewUserDetail(this.props.user.id); + + render() { + const { + user, + modActionButtons, + viewUserDetail, + index, + selected, + isActive, + hideActive, + approveUser, + showRejectUsernameDialog, + me, + } = this.props; + + return ( +
  • +
    +
    +
    + + {me.id !== user.id && + + + Suspend User + + Ban User + + + } +
    -
    -
    -
    -
    - flag{t('community.flags')}({ user.actions.length }): +
    +
    +
    + flag{t('community.flags')}({ user.actions.length }): + { user.action_summaries.map( + (action, i) => { + return + {shortReasons[action.reason]} ({action.count}) + ; + } + )} +
    +
    { user.action_summaries.map( - (action, i) => { - return - {shortReasons[action.reason]} ({action.count}) - ; - } - )} -
    -
    - { user.action_summaries.map( - (action_sum, i) => { - return
    - - {shortReasons[action_sum.reason]} ({action_sum.count}) - - {user.actions.map( + (action_sum, i) => { + return
    + + {shortReasons[action_sum.reason]} ({action_sum.count}) + + {user.actions.map( - // find the action by action_sum.reason - (action, j) => { - if (action.reason === action_sum.reason) { - return

    - {action.user && - - } - : {action.message ? action.message : 'n/a'} -

    ; + // find the action by action_sum.reason + (action, j) => { + if (action.reason === action_sum.reason) { + return

    + {action.user && + + } + : {action.message ? action.message : 'n/a'} +

    ; + } + return null; } - return null; - } - )} -
    ; - } - )} + )} +
    ; + } + )} +
    -
    -
    -
    - {modActionButtons.map((action, i) => - - )} +
    +
    + {modActionButtons.map((action, i) => + + )} +
    -
    -
  • - ); -}; + + ); + } +} export default User; From 67b0624c901c8e65d6274c42ca4e55c114ba8dc6 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 19:48:54 +0700 Subject: [PATCH 08/18] Refactor styles --- .../Community/components/FlaggedUser.css | 89 ++++++++++++++++++- .../Community/components/FlaggedUser.js | 32 ++++--- .../routes/Community/components/styles.css | 46 ++-------- .../routes/Moderation/components/Comment.js | 2 +- 4 files changed, 115 insertions(+), 54 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/FlaggedUser.css b/client/coral-admin/src/routes/Community/components/FlaggedUser.css index d5648fa61..615b00e21 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedUser.css +++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.css @@ -1,4 +1,7 @@ -.listItem { +@custom-media --big-viewport (min-width: 780px); + +.root { + composes: mdl-shadow--2dp mdl-card from global; border-bottom: 1px solid #e0e0e0; font-size: 18px; width: 100%; @@ -15,3 +18,87 @@ } } +.rootSelected { + composes: mdl-shadow--16dp from global; +} + +.container { + display: block; + padding: 0 14px; + min-height: 180px; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; +} + +.author { + display: flex; + justify-content: flex-start; +} + +.body { + display: flex; + align-items: center; + justify-content: space-between; +} + +.flaggedByCount { + display: block; + text-align: left; + margin-top: 5px; +} + +.flagIcon { + font-size: 14px; + margin-right: 10px; +} + +.flaggedBy { + display: inline; + padding: 3px; + font-size: 14px; + margin-left: 5px; +} + +.flaggedByLabel { + font-weight: 600; + font-size: 14px; +} + +.flaggedReasons { + margin-left: 24px; + margin-top: 10px; +} + +.flaggedByReason { + font-size: 1tpx; + margin: 0px; + line-height: 1.4; +} + + +@media (--big-viewport) { + .root { + border: 1px solid #e0e0e0; + margin-bottom: 30px; + + &:last-child { + border-bottom: 1px solid #e0e0e0; + } + } + + &.rootSelected { + border: 2px solid #333; + } +} + +.button { + composes: buttonReset from 'coral-framework/styles/reset.css'; + vertical-align: text-bottom; + &:hover { + background-color: #E0E0E0; + } +} diff --git a/client/coral-admin/src/routes/Community/components/FlaggedUser.js b/client/coral-admin/src/routes/Community/components/FlaggedUser.js index f77b95b90..1e36c45a2 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedUser.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.js @@ -1,11 +1,12 @@ import React from 'react'; -import styles from './styles.css'; +import styles from './FlaggedUser.css'; import ActionButton from './ActionButton'; import {username} from 'talk-plugin-flags/helpers/flagReasons'; import ActionsMenu from 'coral-admin/src/components/ActionsMenu'; import ActionsMenuItem from 'coral-admin/src/components/ActionsMenuItem'; +import cn from 'classnames'; import t from 'coral-framework/services/i18n'; const shortReasons = { @@ -36,27 +37,33 @@ class User extends React.Component { user, modActionButtons, viewUserDetail, - index, selected, - isActive, - hideActive, approveUser, showRejectUsernameDialog, me, + className, } = this.props; return ( -
  • +
  • -
    +
    - + {me.id !== user.id && - Suspend User + Suspend User + @@ -67,10 +74,13 @@ class User extends React.Component {
    -
    -
    +
    +
    - flag{t('community.flags')}({ user.actions.length }): + flag + + {t('community.flags')}({ user.actions.length }) + : { user.action_summaries.map( (action, i) => { return diff --git a/client/coral-admin/src/routes/Community/components/styles.css b/client/coral-admin/src/routes/Community/components/styles.css index f647da86b..fc94717e0 100644 --- a/client/coral-admin/src/routes/Community/components/styles.css +++ b/client/coral-admin/src/routes/Community/components/styles.css @@ -1,3 +1,8 @@ +/** + * deprecated as this file contains styles from multiple components. Please remove this file + * when styles have been refactored. + */ + @custom-media --big-viewport (min-width: 780px); .container { @@ -235,44 +240,3 @@ } } -.flaggedByCount { - display: block; - text-align: left; - margin-top: 5px; -} - -.flaggedByCount i { - font-size: 14px; - margin-right: 10px; -} - -.flaggedBy { - display: inline; - padding: 3px; - font-size: 14px; - margin-left: 5px; -} - -.flaggedByLabel { - font-weight: 600; - font-size: 14px; -} - -.flaggedReasons { - margin-left: 24px; - margin-top: 10px; -} - -p.flaggedByReason { - font-size: 1tpx; - margin: 0px; - line-height: 1.4; -} - -.button { - composes: buttonReset from 'coral-framework/styles/reset.css'; - vertical-align: text-bottom; - &:hover { - background-color: #E0E0E0; - } -} diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index c73d15eaa..acee1d02e 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -67,7 +67,7 @@ class Comment extends React.Component { const flagActions = comment.actions && comment.actions.filter((a) => a.__typename === 'FlagAction'); const commentType = getCommentType(comment); - let selectionStateCSS = selected ? 'mdl-shadow--16dp' : 'mdl-shadow--2dp'; + const selectionStateCSS = selected ? 'mdl-shadow--16dp' : 'mdl-shadow--2dp'; const queryData = {root, comment, asset: comment.asset}; From 4ce6206516dcc849f2b52f3f7064273829ba115a Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 19:56:24 +0700 Subject: [PATCH 09/18] Add deprecation and refactor notices to legacy styles --- .../src/routes/Configure/components/Configure.css | 4 ++++ .../src/routes/Dashboard/components/Dashboard.css | 4 ++++ .../src/routes/Dashboard/components/Widget.css | 4 ++++ .../Install/components/{style.css => Install.css} | 12 ++++++------ .../src/routes/Install/components/Install.js | 6 +++--- .../src/routes/Install/components/Steps/style.css | 5 +++++ .../src/routes/Moderation/components/styles.css | 5 +++++ client/coral-embed-stream/style/default.css | 5 +++++ 8 files changed, 36 insertions(+), 9 deletions(-) rename client/coral-admin/src/routes/Install/components/{style.css => Install.css} (50%) diff --git a/client/coral-admin/src/routes/Configure/components/Configure.css b/client/coral-admin/src/routes/Configure/components/Configure.css index 5b02b963a..0b7319132 100644 --- a/client/coral-admin/src/routes/Configure/components/Configure.css +++ b/client/coral-admin/src/routes/Configure/components/Configure.css @@ -1,3 +1,7 @@ +/** + * @TODO: deprecated as this file contains styles from multiple components. Please refactor. + */ + .container { display: flex; diff --git a/client/coral-admin/src/routes/Dashboard/components/Dashboard.css b/client/coral-admin/src/routes/Dashboard/components/Dashboard.css index cdad990ba..586ff6c08 100644 --- a/client/coral-admin/src/routes/Dashboard/components/Dashboard.css +++ b/client/coral-admin/src/routes/Dashboard/components/Dashboard.css @@ -1,3 +1,7 @@ +/** + * @TODO: deprecated as this file contains styles from multiple components. Please refactor. + */ + .Dashboard { display: flex; } diff --git a/client/coral-admin/src/routes/Dashboard/components/Widget.css b/client/coral-admin/src/routes/Dashboard/components/Widget.css index cf83fcbdc..e94d2313f 100644 --- a/client/coral-admin/src/routes/Dashboard/components/Widget.css +++ b/client/coral-admin/src/routes/Dashboard/components/Widget.css @@ -1,3 +1,7 @@ +/** + * @TODO: deprecated as this file contains styles from multiple components. Please refactor. + */ + :root { --row-height: 60px; } diff --git a/client/coral-admin/src/routes/Install/components/style.css b/client/coral-admin/src/routes/Install/components/Install.css similarity index 50% rename from client/coral-admin/src/routes/Install/components/style.css rename to client/coral-admin/src/routes/Install/components/Install.css index 62f7ffe04..8e6f7504d 100644 --- a/client/coral-admin/src/routes/Install/components/style.css +++ b/client/coral-admin/src/routes/Install/components/Install.css @@ -1,12 +1,12 @@ -.Install { +.install { max-width: 900px; margin: 0 auto; text-align: center; padding: 50px 0; +} - h2 { - font-size: 2em; - font-weight: 500; - margin: 0; - } +.header { + font-size: 2em; + font-weight: 500; + margin: 0; } diff --git a/client/coral-admin/src/routes/Install/components/Install.js b/client/coral-admin/src/routes/Install/components/Install.js index f7069d67c..0d206d8f0 100644 --- a/client/coral-admin/src/routes/Install/components/Install.js +++ b/client/coral-admin/src/routes/Install/components/Install.js @@ -1,5 +1,5 @@ import React, {Component} from 'react'; -import styles from './style.css'; +import styles from './Install.css'; import {Wizard, WizardNav} from 'coral-ui'; import Layout from 'coral-admin/src/components/ui/Layout'; @@ -37,11 +37,11 @@ export default class Install extends Component { return ( -
    +
    { !install.alreadyInstalled ? (
    -

    Welcome to the Coral Project

    +

    Welcome to the Coral Project

    { install.step !== 0 ? : null } Date: Wed, 30 Aug 2017 20:41:18 +0700 Subject: [PATCH 10/18] Fix center styling --- .../src/routes/Community/components/FlaggedAccounts.css | 5 +++++ .../src/routes/Community/components/FlaggedAccounts.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css index 6c7173c3d..fef9aabd8 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css @@ -9,3 +9,8 @@ padding: 34px 14px; box-sizing: border-box; } + +.list { + margin: 0; + padding: 0; +} diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js index 1d74fd4ae..8687226db 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js @@ -25,7 +25,7 @@ const FlaggedAccounts = (props) => {
    { hasResults - ?
      + ?
        { users.nodes.map((user, index) => { return ( From 02ec1f69ee5fd6cf84a756433a835892d077b3f7 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 20:42:13 +0700 Subject: [PATCH 11/18] More graphql fixes --- client/coral-admin/src/graphql/index.js | 4 ++-- .../src/routes/Community/containers/FlaggedAccounts.js | 2 +- graph/loaders/users.js | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/client/coral-admin/src/graphql/index.js b/client/coral-admin/src/graphql/index.js index dea68c5e8..be99f1c64 100644 --- a/client/coral-admin/src/graphql/index.js +++ b/client/coral-admin/src/graphql/index.js @@ -1,10 +1,10 @@ export default { mutations: { SetUserStatus: () => ({ - refetchQueries: ['CoralAdmin_Community'], + refetchQueries: ['TalkAdmin_FlaggedAccounts'], }), RejectUsername: () => ({ - refetchQueries: ['CoralAdmin_Community'], + refetchQueries: ['TalkAdmin_FlaggedAccounts'], }), }, }; diff --git a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js index ad3339bd5..d4dca76f6 100644 --- a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js @@ -72,7 +72,7 @@ class FlaggedAccountsContainer extends Component { const LOAD_MORE_QUERY = gql` query TalkAdmin_LoadMoreFlaggedAccounts($limit: Int, $cursor: Cursor) { - users(query:{action_type: FLAG, limit: $limit, cursor: $cursor}){ + users(query:{action_type: FLAG, statuses: [PENDING], limit: $limit, cursor: $cursor}){ hasNextPage endCursor nodes { diff --git a/graph/loaders/users.js b/graph/loaders/users.js index 8ef8bd6cf..ac3493479 100644 --- a/graph/loaders/users.js +++ b/graph/loaders/users.js @@ -73,6 +73,9 @@ const getUsersByQuery = async ({user, loaders: {Actions}}, {ids, limit, cursor, query = query.limit(limit + 1); } + // Sort by created_at. + query.sort({created_at: sortOrder === 'DESC' ? -1 : 1}); + const nodes = await query.exec(); // The hasNextPage is always handled the same (ask for one more than we need, From 797f0fd0e3360f1133233be2d047f3054ffae29e Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 21:05:03 +0700 Subject: [PATCH 12/18] Docs in schema --- graph/typeDefs.graphql | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 79b0f59c0..9d8e25f12 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -139,9 +139,11 @@ type UserConnection { # UsersQuery allows the ability to query users by a specific fields. input UsersQuery { + + # Users returned will only be ones which have at least one action of this. action_type: ACTION_TYPE - # Current status of a user. Requires the `ADMIN` role. + # Current status of a user.. statuses: [USER_STATUS!] # Limit the number of results to be returned. @@ -749,7 +751,7 @@ type RootQuery { # role. me: User - # Users returned based on a query. + # Users returned based on a query. Requires the `ADMIN` role. users(query: UsersQuery!): UserConnection # a single User by id From c66fb053e7f0fe13a4d50e63314a4e91708df1aa Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 21:40:28 +0700 Subject: [PATCH 13/18] Implement mutation updators --- client/coral-admin/src/graphql/index.js | 31 +++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/client/coral-admin/src/graphql/index.js b/client/coral-admin/src/graphql/index.js index be99f1c64..8fedf6ebe 100644 --- a/client/coral-admin/src/graphql/index.js +++ b/client/coral-admin/src/graphql/index.js @@ -1,10 +1,33 @@ +import update from 'immutability-helper'; + export default { mutations: { - SetUserStatus: () => ({ - refetchQueries: ['TalkAdmin_FlaggedAccounts'], + SetUserStatus: ({variables: {status, userId}}) => ({ + updateQueries: { + TalkAdmin_FlaggedAccounts: (prev) => { + if (status !== 'APPROVED') { + return prev; + } + const updated = update(prev, { + users: { + nodes: {$apply: (nodes) => nodes.filter((node) => node.id !== userId)}, + }, + }); + return updated; + } + } }), - RejectUsername: () => ({ - refetchQueries: ['TalkAdmin_FlaggedAccounts'], + RejectUsername: ({variables: {input: {id: userId}}}) => ({ + updateQueries: { + TalkAdmin_FlaggedAccounts: (prev) => { + const updated = update(prev, { + users: { + nodes: {$apply: (nodes) => nodes.filter((node) => node.id !== userId)}, + }, + }); + return updated; + } + } }), }, }; From f582cc2534198ce5a75bbea68cda14505a42c94a Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 22:25:03 +0700 Subject: [PATCH 14/18] Add transition effect --- .../Community/components/FlaggedAccounts.css | 18 +++ .../Community/components/FlaggedAccounts.js | 109 ++++++++++-------- .../Community/components/FlaggedUser.css | 2 +- .../routes/Moderation/components/styles.css | 4 +- 4 files changed, 83 insertions(+), 50 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css index fef9aabd8..4e9600cbd 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css @@ -14,3 +14,21 @@ margin: 0; padding: 0; } + +.userLeave { + opacity: 1.0; +} + +.userLeaveActive { + opacity: 0; + transition: opacity 800ms; +} + +.userEnter { + opacity: 0; +} + +.userEnterActive { + opacity: 1.0; + transition: opacity 800ms; +} diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js index 8687226db..328e1789d 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js @@ -1,59 +1,74 @@ import React from 'react'; import t from 'coral-framework/services/i18n'; -import styles from './FlaggedAccounts.css'; import EmptyCard from 'coral-admin/src/components/EmptyCard'; import LoadMore from '../../../components/LoadMore'; import FlaggedUser from '../containers/FlaggedUser'; +import {CSSTransitionGroup} from 'react-transition-group'; +import styles from './FlaggedAccounts.css'; -const FlaggedAccounts = (props) => { - const { - users, - loadMore, - showBanUserDialog, - showSuspendUserDialog, - showRejectUsernameDialog, - approveUser, - me, - viewUserDetail, - } = props; +class FlaggedAccounts extends React.Component { + render() { + const { + users, + loadMore, + showBanUserDialog, + showSuspendUserDialog, + showRejectUsernameDialog, + approveUser, + me, + viewUserDetail, + } = this.props; - const hasResults = users.nodes && !!users.nodes.length; + const hasResults = users.nodes && !!users.nodes.length; - return ( -
        -
        - { - hasResults - ?
          - { - users.nodes.map((user, index) => { - return ( - - ); - }) - } -
        - : {t('community.no_flagged_accounts')} - } - + return ( +
        +
        + { + hasResults + ? + { + users.nodes.map((user) => { + return ( + + ); + }) + } + + : {t('community.no_flagged_accounts')} + } + +
        -
        - ); -}; + ); + } +} export default FlaggedAccounts; diff --git a/client/coral-admin/src/routes/Community/components/FlaggedUser.css b/client/coral-admin/src/routes/Community/components/FlaggedUser.css index 615b00e21..aaaaee3a3 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedUser.css +++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.css @@ -9,7 +9,7 @@ min-width: 400px; margin: 0 auto; position: relative; - transition: all 200ms; + transition: box-shadow 200ms, margin-bottom 200ms; padding: 10px 0 0; min-height: 220px; diff --git a/client/coral-admin/src/routes/Moderation/components/styles.css b/client/coral-admin/src/routes/Moderation/components/styles.css index fe9c66580..6f27e0b94 100644 --- a/client/coral-admin/src/routes/Moderation/components/styles.css +++ b/client/coral-admin/src/routes/Moderation/components/styles.css @@ -449,20 +449,20 @@ span { .commentLeave { opacity: 1.0; - transition: opacity 800ms; } .commentLeaveActive { opacity: 0; + transition: opacity 800ms; } .commentEnter { opacity: 0; - transition: opacity 800ms; } .commentEnterActive { opacity: 1.0; + transition: opacity 800ms; } .editedMarker { From 1f489ff0e5d08fbbd6e0bcf653482a2605d7781f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 22:30:53 +0700 Subject: [PATCH 15/18] Always refetch when switching to FlaggedAccounts --- .../Community/containers/FlaggedAccounts.js | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js index d4dca76f6..cfa66f732 100644 --- a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js @@ -19,6 +19,26 @@ import FlaggedUser from '../containers/FlaggedUser'; class FlaggedAccountsContainer extends Component { + state = {refetching: false}; + + constructor(props) { + super(props); + } + + componentWillMount() { + if (!this.props.data.loading) { + this.setState({refetching: true}); + this.props.data.refetch() + .then(() => { + this.setState({refetching: false}); + }) + .catch((err) => { + this.setState({refetching: false}); + throw err; + }); + } + } + approveUser = ({userId}) => { return this.props.setUserStatus({userId, status: 'APPROVED'}); } @@ -50,7 +70,7 @@ class FlaggedAccountsContainer extends Component { return
        {this.props.data.error.message}
        ; } - if (this.props.data.loading) { + if (this.props.data.loading || this.state.refetching) { return
        ; } return ( From 9b9476d44f974ad0785cccf7a549c74bcb1cc3a9 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 22:51:16 +0700 Subject: [PATCH 16/18] Add todos --- .../src/routes/Community/components/ActionButton.js | 1 + .../src/routes/Community/components/FlaggedUser.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/client/coral-admin/src/routes/Community/components/ActionButton.js b/client/coral-admin/src/routes/Community/components/ActionButton.js index 400b59ab3..0ef9b8a39 100644 --- a/client/coral-admin/src/routes/Community/components/ActionButton.js +++ b/client/coral-admin/src/routes/Community/components/ActionButton.js @@ -5,6 +5,7 @@ import {menuActionsMap} from '../../../utils/moderationQueueActionsMap'; import t from 'coral-framework/services/i18n'; +// TODO: Needs refactoring. const ActionButton = ({type = '', user, ...props}) => { return (
        ); From 5434413308e78a141a34dd9dee742e28603df4bc Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 30 Aug 2017 23:47:48 +0700 Subject: [PATCH 18/18] Use fetchPolicy network-only --- .../Community/containers/FlaggedAccounts.js | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js index cfa66f732..00c6fdf33 100644 --- a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js @@ -19,26 +19,10 @@ import FlaggedUser from '../containers/FlaggedUser'; class FlaggedAccountsContainer extends Component { - state = {refetching: false}; - constructor(props) { super(props); } - componentWillMount() { - if (!this.props.data.loading) { - this.setState({refetching: true}); - this.props.data.refetch() - .then(() => { - this.setState({refetching: false}); - }) - .catch((err) => { - this.setState({refetching: false}); - throw err; - }); - } - } - approveUser = ({userId}) => { return this.props.setUserStatus({userId, status: 'APPROVED'}); } @@ -70,7 +54,7 @@ class FlaggedAccountsContainer extends Component { return
        {this.props.data.error.message}
        ; } - if (this.props.data.loading || this.state.refetching) { + if (this.props.data.loading) { return
        ; } return ( @@ -123,7 +107,11 @@ export const withFlaggedAccountsyQuery = withQuery(gql` ${FlaggedUser.fragments.root} ${FlaggedUser.fragments.user} ${FlaggedUser.fragments.me} -`); +`, { + options: { + fetchPolicy: 'network-only', + }, +}); const mapDispatchToProps = (dispatch) => bindActionCreators({