mirror of
https://github.com/wassname/talk.git
synced 2026-07-16 11:22:16 +08:00
add a drawer for the user details
This commit is contained in:
@@ -20,3 +20,4 @@ export const hideShortcutsNote = () => {
|
||||
};
|
||||
|
||||
export const viewUserDetail = userId => ({type: actions.VIEW_USER_DETAIL, userId});
|
||||
export const hideUserDetail = () => ({type: actions.HIDE_USER_DETAIL});
|
||||
|
||||
@@ -16,7 +16,8 @@ import {
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
hideShortcutsNote,
|
||||
viewUserDetail
|
||||
viewUserDetail,
|
||||
hideUserDetail
|
||||
} from 'actions/moderation';
|
||||
|
||||
import {Spinner} from 'coral-ui';
|
||||
@@ -118,7 +119,7 @@ class ModerationContainer extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {data, moderation, settings, assets, onClose, viewUserDetail, ...props} = this.props;
|
||||
const {data, moderation, settings, assets, onClose, viewUserDetail, hideUserDetail, ...props} = this.props;
|
||||
const providedAssetId = this.props.params.id;
|
||||
const activeTab = this.props.route.path === ':id' ? 'premod' : this.props.route.path;
|
||||
|
||||
@@ -189,6 +190,7 @@ class ModerationContainer extends Component {
|
||||
sort={this.state.sort}
|
||||
commentCount={activeTabCount}
|
||||
viewUserDetail={viewUserDetail}
|
||||
hideUserDetail={hideUserDetail}
|
||||
/>
|
||||
<BanUserDialog
|
||||
open={moderation.banDialog}
|
||||
@@ -204,7 +206,7 @@ class ModerationContainer extends Component {
|
||||
shortcutsNoteVisible={moderation.shortcutsNoteVisible}
|
||||
open={moderation.modalOpen}
|
||||
onClose={onClose}/>
|
||||
{moderation.userDetailId && <UserDetail id={moderation.userDetailId} />}
|
||||
{moderation.userDetailId && <UserDetail id={moderation.userDetailId} hideUserDetail={hideUserDetail}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -223,6 +225,7 @@ const mapDispatchToProps = dispatch => ({
|
||||
updateAssets: assets => dispatch(updateAssets(assets)),
|
||||
fetchSettings: () => dispatch(fetchSettings()),
|
||||
viewUserDetail: id => dispatch(viewUserDetail(id)),
|
||||
hideUserDetail: () => dispatch(hideUserDetail()),
|
||||
showBanUserDialog: (user, commentId, showRejectedNote) => dispatch(showBanUserDialog(user, commentId, showRejectedNote)),
|
||||
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
|
||||
hideShortcutsNote: () => dispatch(hideShortcutsNote()),
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.copyButton {
|
||||
float: right;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
.memberSince {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.small {
|
||||
color: #aaa;
|
||||
}
|
||||
@@ -1,16 +1,27 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Button, Drawer} from 'coral-ui';
|
||||
import styles from './UserDetail.js';
|
||||
import styles from './UserDetail.css';
|
||||
import {compose} from 'react-apollo';
|
||||
import {getUserDetail} from 'coral-admin/src/graphql/queries';
|
||||
|
||||
class UserDetail extends React.Component {
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired
|
||||
id: PropTypes.string.isRequired,
|
||||
hideUserDetail: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
copyPermalink () {
|
||||
this.profile.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} catch (e) {
|
||||
|
||||
/* nothing */
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const {data} = this.props;
|
||||
const {data, hideUserDetail} = this.props;
|
||||
|
||||
if (!('user' in data)) {
|
||||
return null;
|
||||
@@ -27,13 +38,16 @@ class UserDetail extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer>
|
||||
<Drawer handleClickOutside={hideUserDetail}>
|
||||
<h3>{user.username}</h3>
|
||||
<p>{profile}</p> <Button>Copy</Button>
|
||||
<strong>Member since</strong> {user.created_at}
|
||||
<Button className={styles.copyButton}>Copy</Button>
|
||||
<p ref={ref => this.profile = ref} contentEditable="true">{profile}</p>
|
||||
<p className={styles.memberSince}><strong>Member since</strong> {new Date(user.created_at).toLocaleString()}</p>
|
||||
<hr/>
|
||||
<strong>Account summary</strong>
|
||||
<br/><span className={styles.small}>Data represents the last six months of activity</span>
|
||||
<p>
|
||||
<strong>Account summary</strong>
|
||||
<br/><small className={styles.small}>Data represents the last six months of activity</small>
|
||||
</p>
|
||||
<div className={styles.stats}>
|
||||
</div>
|
||||
</Drawer>
|
||||
|
||||
@@ -2,5 +2,10 @@ query UserDetail ($id: ID!) {
|
||||
user(id: $id) {
|
||||
id
|
||||
username
|
||||
created_at
|
||||
profiles {
|
||||
id
|
||||
provider
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
.wrapper {
|
||||
transition: opacity 250ms;
|
||||
.drawer {
|
||||
max-width: 700px;
|
||||
padding: 20px;
|
||||
position: fixed;
|
||||
z-index: 10000;
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: white;
|
||||
transition: transform 500ms ease-in-out;
|
||||
box-shadow: -3px 0px 4px 0px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.drawer {
|
||||
transition: transform 500ms ease-in-out;
|
||||
.closeButton {
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
left: -40px;
|
||||
background-color: white;
|
||||
border-radius: 4px 0 0 4px;
|
||||
box-sizing: border-box;
|
||||
font-size: 32px;
|
||||
top: 60px;
|
||||
box-shadow: -1px 3px 4px 0px rgba(0,0,0,0.15);
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import styles from './Drawer.css';
|
||||
import onClickOutside from 'react-onclickoutside';
|
||||
|
||||
const Drawer = (props) => {
|
||||
const Drawer = ({children, handleClickOutside}) => {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.drawer}>{props.children}</div>
|
||||
<div className={styles.closeButton} onClick={props.close}>×</div>
|
||||
<div className={styles.drawer}>
|
||||
<div className={styles.closeButton} onClick={handleClickOutside}>×</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Drawer.propTypes = {
|
||||
active: PropTypes.bool
|
||||
active: PropTypes.bool,
|
||||
handleClickOutside: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Drawer;
|
||||
export default onClickOutside(Drawer);
|
||||
|
||||
@@ -10,6 +10,13 @@ const User = {
|
||||
}
|
||||
|
||||
},
|
||||
created_at({roles, created_at}, _, {user}) {
|
||||
if (user && user.hasRoles('ADMIN')) {
|
||||
return created_at;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
comments({id}, _, {loaders: {Comments}, user}) {
|
||||
|
||||
// If the user is not an admin, only return comment list for the owner of
|
||||
@@ -20,10 +27,10 @@ const User = {
|
||||
|
||||
return null;
|
||||
},
|
||||
profiles({id, profiles}, _, {user}) {
|
||||
profiles({profiles}, _, {user}) {
|
||||
|
||||
// if the user is not an admin, do not return the profiles
|
||||
if (user && (user.hasRoles('ADMIN') || user.id === id)) {
|
||||
if (user && user.hasRoles('ADMIN')) {
|
||||
return profiles;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ type User {
|
||||
# Username of a user.
|
||||
username: String!
|
||||
|
||||
# creation date of user
|
||||
created_at: String!
|
||||
|
||||
# Action summaries against the user.
|
||||
action_summaries: [ActionSummary]!
|
||||
|
||||
|
||||
+2
-4
@@ -98,12 +98,10 @@
|
||||
"react-recaptcha": "^2.2.6",
|
||||
"recompose": "^0.23.1",
|
||||
"redis": "^2.7.1",
|
||||
"uuid": "^3.0.1",
|
||||
"simplemde": "^1.11.2",
|
||||
"subscriptions-transport-ws": "^0.5.5-alpha.0",
|
||||
"resolve": "^1.3.2",
|
||||
"semver": "^5.3.0",
|
||||
"simplemde": "^1.11.2",
|
||||
"subscriptions-transport-ws": "^0.5.5-alpha.0",
|
||||
"uuid": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -174,7 +172,7 @@
|
||||
"react-linkify": "^0.1.3",
|
||||
"react-mdl": "^1.7.2",
|
||||
"react-mdl-selectfield": "^0.2.0",
|
||||
"react-onclickoutside": "^5.7.1",
|
||||
"react-onclickoutside": "^5.11.1",
|
||||
"react-redux": "^4.4.5",
|
||||
"react-router": "^3.0.0",
|
||||
"react-tagsinput": "^3.14.0",
|
||||
|
||||
@@ -6780,7 +6780,7 @@ react-mdl@^1.7.1, react-mdl@^1.7.2:
|
||||
lodash.isequal "^4.4.0"
|
||||
prop-types "^15.5.0"
|
||||
|
||||
react-onclickoutside@^5.7.1:
|
||||
react-onclickoutside@^5.11.1:
|
||||
version "5.11.1"
|
||||
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-5.11.1.tgz#00314e52567cf55faba94cabbacd119619070623"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user