mirror of
https://github.com/wassname/talk.git
synced 2026-07-07 15:13:35 +08:00
Merge branch 'master' into workspaces
This commit is contained in:
+2
-1
@@ -31,4 +31,5 @@ public
|
||||
!plugins/talk-plugin-sort-oldest
|
||||
!plugins/talk-plugin-subscriber
|
||||
!plugins/talk-plugin-toxic-comments
|
||||
!plugins/talk-plugin-viewing-options
|
||||
!plugins/talk-plugin-viewing-options
|
||||
!plugins/talk-plugin-profile-settings
|
||||
|
||||
@@ -52,6 +52,7 @@ plugins/*
|
||||
!plugins/talk-plugin-subscriber
|
||||
!plugins/talk-plugin-flag-details
|
||||
!plugins/talk-plugin-slack-notifications
|
||||
!plugins/talk-plugin-profile-settings
|
||||
|
||||
**/node_modules/*
|
||||
yarn-error.log
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import * as actions from '../constants/profile';
|
||||
|
||||
export const setActiveTab = tab => ({ type: actions.SET_ACTIVE_TAB, tab });
|
||||
@@ -9,16 +9,12 @@ import AutomaticAssetClosure from '../containers/AutomaticAssetClosure';
|
||||
|
||||
import ExtendableTabPanel from '../containers/ExtendableTabPanel';
|
||||
import { Tab, TabPane } from 'coral-ui';
|
||||
import ProfileContainer from '../tabs/profile/containers/ProfileContainer';
|
||||
import Profile from '../tabs/profile/containers/Profile';
|
||||
import Popup from 'coral-framework/components/Popup';
|
||||
import IfSlotIsNotEmpty from 'coral-framework/components/IfSlotIsNotEmpty';
|
||||
import cn from 'classnames';
|
||||
|
||||
export default class Embed extends React.Component {
|
||||
changeTab = tab => {
|
||||
this.props.setActiveTab(tab);
|
||||
};
|
||||
|
||||
getTabs() {
|
||||
const tabs = [
|
||||
<Tab
|
||||
@@ -53,6 +49,7 @@ export default class Embed extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
commentId,
|
||||
root,
|
||||
root: { asset },
|
||||
@@ -65,6 +62,7 @@ export default class Embed extends React.Component {
|
||||
parentUrl,
|
||||
} = this.props;
|
||||
const hasHighlightedComment = !!commentId;
|
||||
const popupUrl = `login?parentUrl=${encodeURIComponent(parentUrl)}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -75,7 +73,7 @@ export default class Embed extends React.Component {
|
||||
<AutomaticAssetClosure asset={asset} />
|
||||
<IfSlotIsNotEmpty slot="login">
|
||||
<Popup
|
||||
href={`login?parentUrl=${encodeURIComponent(parentUrl)}`}
|
||||
href={popupUrl}
|
||||
title="Login"
|
||||
features="menubar=0,resizable=0,width=500,height=550,top=200,left=500"
|
||||
open={showSignInDialog}
|
||||
@@ -91,7 +89,7 @@ export default class Embed extends React.Component {
|
||||
<ExtendableTabPanel
|
||||
className="talk-embed-stream-tab-bar"
|
||||
activeTab={activeTab}
|
||||
setActiveTab={this.changeTab}
|
||||
setActiveTab={setActiveTab}
|
||||
fallbackTab="stream"
|
||||
tabSlot="embedStreamTabs"
|
||||
tabSlotPrepend="embedStreamTabsPrepend"
|
||||
@@ -112,7 +110,7 @@ export default class Embed extends React.Component {
|
||||
tabId="profile"
|
||||
className="talk-embed-stream-profile-tab-pane"
|
||||
>
|
||||
<ProfileContainer />
|
||||
<Profile />
|
||||
</TabPane>,
|
||||
<TabPane
|
||||
key="config"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
const prefix = 'TALK_EMBED_STREAM';
|
||||
export const SET_ACTIVE_TAB = `${prefix}_SET_ACTIVE_TAB`;
|
||||
@@ -3,6 +3,7 @@ import asset from './asset';
|
||||
import embed from './embed';
|
||||
import configure from './configure';
|
||||
import stream from './stream';
|
||||
import profile from './profile';
|
||||
|
||||
export default {
|
||||
login,
|
||||
@@ -10,4 +11,5 @@ export default {
|
||||
embed,
|
||||
configure,
|
||||
stream,
|
||||
profile,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import * as actions from '../constants/profile';
|
||||
|
||||
const initialState = {
|
||||
activeTab: 'comments',
|
||||
previousTab: '',
|
||||
};
|
||||
|
||||
export default function stream(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case actions.SET_ACTIVE_TAB:
|
||||
return {
|
||||
...state,
|
||||
activeTab: action.tab,
|
||||
previousTab: state.activeTab,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,18 @@ import { getTotalReactionsCount } from 'coral-framework/utils';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
class Comment extends React.Component {
|
||||
goToStory = () => {
|
||||
this.props.navigate(this.props.comment.asset.url);
|
||||
};
|
||||
|
||||
goToConversation = () => {
|
||||
this.props.navigate(
|
||||
`${this.props.comment.asset.url}?commentId=${this.props.comment.id}`
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { comment, link, data, root } = this.props;
|
||||
const { comment, data, root } = this.props;
|
||||
const reactionCount = getTotalReactionsCount(comment.action_summaries);
|
||||
const queryData = { root, comment, asset: comment.asset };
|
||||
|
||||
@@ -67,7 +77,7 @@ class Comment extends React.Component {
|
||||
<a
|
||||
className={cn(styles.assetURL, 'my-comment-anchor')}
|
||||
href="#"
|
||||
onClick={link(`${comment.asset.url}`)}
|
||||
onClick={this.goToStory}
|
||||
>
|
||||
{t('common.story')}:{' '}
|
||||
{comment.asset.title ? comment.asset.title : comment.asset.url}
|
||||
@@ -77,10 +87,7 @@ class Comment extends React.Component {
|
||||
<div className={styles.sidebar}>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
onClick={link(`${comment.asset.url}?commentId=${comment.id}`)}
|
||||
className={styles.viewLink}
|
||||
>
|
||||
<a onClick={this.goToConversation} className={styles.viewLink}>
|
||||
<Icon name="open_in_new" className={styles.iconView} />
|
||||
{t('view_conversation')}
|
||||
</a>
|
||||
@@ -105,10 +112,10 @@ class Comment extends React.Component {
|
||||
}
|
||||
|
||||
Comment.propTypes = {
|
||||
comment: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
body: PropTypes.string,
|
||||
}).isRequired,
|
||||
comment: PropTypes.object.isRequired,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
root: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default Comment;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Comment from './Comment';
|
||||
import Comment from '../containers/Comment';
|
||||
import LoadMore from './LoadMore';
|
||||
|
||||
class CommentHistory extends React.Component {
|
||||
@@ -21,9 +21,9 @@ class CommentHistory extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { link, comments, data, root } = this.props;
|
||||
const { navigate, comments, data, root } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<div className="talk-my-profile-comment-history">
|
||||
<div className="commentHistory__list">
|
||||
{comments.nodes.map((comment, i) => {
|
||||
return (
|
||||
@@ -32,7 +32,7 @@ class CommentHistory extends React.Component {
|
||||
data={data}
|
||||
root={root}
|
||||
comment={comment}
|
||||
link={link}
|
||||
navigate={navigate}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -51,7 +51,7 @@ class CommentHistory extends React.Component {
|
||||
CommentHistory.propTypes = {
|
||||
comments: PropTypes.object.isRequired,
|
||||
loadMore: PropTypes.func,
|
||||
link: PropTypes.func,
|
||||
navigate: PropTypes.func,
|
||||
data: PropTypes.object,
|
||||
root: PropTypes.object,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.userInfo {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.email {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.username {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Slot from 'coral-framework/components/Slot';
|
||||
import CommentHistory from '../containers/CommentHistory';
|
||||
import ExtendableTabPanel from '../../../containers/ExtendableTabPanel';
|
||||
import { Tab, TabPane } from 'coral-ui';
|
||||
import styles from './Profile.css';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const Profile = ({
|
||||
username,
|
||||
emailAddress,
|
||||
data,
|
||||
root,
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
}) => (
|
||||
<div className="talk-my-profile talk-profile-container">
|
||||
<div className={styles.userInfo}>
|
||||
<h2 className={styles.username}>{username}</h2>
|
||||
{emailAddress ? <p className={styles.email}>{emailAddress}</p> : null}
|
||||
</div>
|
||||
<Slot fill="profileSections" data={data} queryData={{ root }} />
|
||||
<ExtendableTabPanel
|
||||
activeTab={activeTab}
|
||||
setActiveTab={setActiveTab}
|
||||
fallbackTab="comments"
|
||||
tabSlot="profileTabs"
|
||||
tabSlotPrepend="profileTabsPrepend"
|
||||
tabPaneSlot="profileTabPanes"
|
||||
slotProps={{ data }}
|
||||
queryData={{ root }}
|
||||
tabs={[
|
||||
<Tab key="comments" tabId="comments">
|
||||
{t('framework.my_comments')}
|
||||
</Tab>,
|
||||
]}
|
||||
tabPanes={[
|
||||
<TabPane key="comments" tabId="comments">
|
||||
<CommentHistory data={data} root={root} />
|
||||
</TabPane>,
|
||||
]}
|
||||
sub
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Profile.propTypes = {
|
||||
username: PropTypes.string,
|
||||
emailAddress: PropTypes.string,
|
||||
data: PropTypes.object,
|
||||
root: PropTypes.object,
|
||||
activeTab: PropTypes.string.isRequired,
|
||||
setActiveTab: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Profile;
|
||||
@@ -0,0 +1,32 @@
|
||||
import { gql, compose } from 'react-apollo';
|
||||
import Comment from '../components/Comment';
|
||||
import { withFragments } from 'coral-framework/hocs';
|
||||
import { getSlotFragmentSpreads } from 'coral-framework/utils';
|
||||
|
||||
const slots = ['commentContent', 'historyCommentTimestamp'];
|
||||
|
||||
const withCommentFragments = withFragments({
|
||||
comment: gql`
|
||||
fragment TalkEmbedStream_ProfileComment_comment on Comment {
|
||||
id
|
||||
body
|
||||
replyCount
|
||||
action_summaries {
|
||||
count
|
||||
__typename
|
||||
}
|
||||
asset {
|
||||
id
|
||||
title
|
||||
url
|
||||
${getSlotFragmentSpreads(slots, 'asset')}
|
||||
}
|
||||
created_at
|
||||
${getSlotFragmentSpreads(slots, 'comment')}
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
const enhance = compose(withCommentFragments);
|
||||
|
||||
export default enhance(Comment);
|
||||
@@ -0,0 +1,103 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { compose, gql } from 'react-apollo';
|
||||
import CommentHistory from '../components/CommentHistory';
|
||||
import Comment from './Comment';
|
||||
import { withFragments } from 'coral-framework/hocs';
|
||||
|
||||
import { appendNewNodes } from 'plugin-api/beta/client/utils';
|
||||
import update from 'immutability-helper';
|
||||
import { getDefinitionName } from 'coral-framework/utils';
|
||||
|
||||
class CommentHistoryContainer extends Component {
|
||||
navigate = url => {
|
||||
this.context.pym.sendMessage('navigate', url);
|
||||
};
|
||||
|
||||
loadMore = () => {
|
||||
return this.props.data.fetchMore({
|
||||
query: LOAD_MORE_QUERY,
|
||||
variables: {
|
||||
limit: 5,
|
||||
cursor: this.props.root.me.comments.endCursor,
|
||||
},
|
||||
updateQuery: (previous, { fetchMoreResult: { me: { comments } } }) => {
|
||||
const updated = update(previous, {
|
||||
me: {
|
||||
comments: {
|
||||
nodes: {
|
||||
$apply: nodes => appendNewNodes(nodes, comments.nodes),
|
||||
},
|
||||
hasNextPage: { $set: comments.hasNextPage },
|
||||
endCursor: { $set: comments.endCursor },
|
||||
},
|
||||
},
|
||||
});
|
||||
return updated;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<CommentHistory
|
||||
comments={this.props.root.me.comments}
|
||||
data={this.props.data}
|
||||
root={this.props.root}
|
||||
loadMore={this.loadMore}
|
||||
navigate={this.navigate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CommentHistoryContainer.contextTypes = {
|
||||
pym: PropTypes.object,
|
||||
};
|
||||
|
||||
CommentHistoryContainer.propTypes = {
|
||||
data: PropTypes.object,
|
||||
root: PropTypes.object,
|
||||
};
|
||||
|
||||
const LOAD_MORE_QUERY = gql`
|
||||
query TalkEmbedStream_CommentHistory_LoadMoreComments($limit: Int, $cursor: Cursor) {
|
||||
me {
|
||||
comments(query: { limit: $limit, cursor: $cursor }) {
|
||||
nodes {
|
||||
...${getDefinitionName(Comment.fragments.comment)}
|
||||
}
|
||||
endCursor
|
||||
hasNextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
${Comment.fragments.comment}
|
||||
`;
|
||||
|
||||
const withCommentHistoryFragments = withFragments({
|
||||
root: gql`
|
||||
fragment TalkEmbedStream_CommentHistory on RootQuery {
|
||||
me {
|
||||
comments(query: {limit: 10}) {
|
||||
nodes {
|
||||
...${getDefinitionName(Comment.fragments.comment)}
|
||||
}
|
||||
endCursor
|
||||
hasNextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
${Comment.fragments.comment}
|
||||
`,
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
currentUser: state.auth.user,
|
||||
});
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, null),
|
||||
withCommentHistoryFragments
|
||||
)(CommentHistoryContainer);
|
||||
@@ -0,0 +1,104 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { compose, gql } from 'react-apollo';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { withQuery } from 'coral-framework/hocs';
|
||||
import NotLoggedIn from '../components/NotLoggedIn';
|
||||
import { Spinner } from 'coral-ui';
|
||||
import Profile from '../components/Profile';
|
||||
import CommentHistory from './CommentHistory';
|
||||
import { getDefinitionName } from 'coral-framework/utils';
|
||||
|
||||
import { showSignInDialog } from 'coral-embed-stream/src/actions/login';
|
||||
import { setActiveTab } from '../../../actions/profile';
|
||||
import { getSlotFragmentSpreads } from 'coral-framework/utils';
|
||||
|
||||
class ProfileContainer extends Component {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!this.props.currentUser && nextProps.currentUser) {
|
||||
// Refetch after login.
|
||||
this.props.data.refetch();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { currentUser, showSignInDialog, root, data } = this.props;
|
||||
const { me } = this.props.root;
|
||||
const loading = this.props.data.loading;
|
||||
|
||||
if (this.props.data.error) {
|
||||
return <div>{this.props.data.error.message}</div>;
|
||||
}
|
||||
|
||||
if (!currentUser) {
|
||||
return <NotLoggedIn showSignInDialog={showSignInDialog} />;
|
||||
}
|
||||
|
||||
if (loading || !me) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
const localProfile = currentUser.profiles.find(p => p.provider === 'local');
|
||||
const emailAddress = localProfile && localProfile.id;
|
||||
|
||||
return (
|
||||
<Profile
|
||||
username={me.username}
|
||||
emailAddress={emailAddress}
|
||||
data={data}
|
||||
root={root}
|
||||
activeTab={this.props.activeTab}
|
||||
setActiveTab={this.props.setActiveTab}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProfileContainer.propTypes = {
|
||||
data: PropTypes.object,
|
||||
root: PropTypes.object,
|
||||
currentUser: PropTypes.object,
|
||||
showSignInDialog: PropTypes.func,
|
||||
activeTab: PropTypes.string.isRequired,
|
||||
setActiveTab: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const slots = [
|
||||
'profileSections',
|
||||
'profileTabs',
|
||||
'profileTabsPrepend',
|
||||
'profileTabPanes',
|
||||
];
|
||||
|
||||
const withProfileQuery = withQuery(
|
||||
gql`
|
||||
query CoralEmbedStream_Profile {
|
||||
me {
|
||||
id
|
||||
username
|
||||
}
|
||||
...${getDefinitionName(CommentHistory.fragments.root)}
|
||||
${getSlotFragmentSpreads(slots, 'root')}
|
||||
}
|
||||
${CommentHistory.fragments.root}
|
||||
`,
|
||||
{
|
||||
options: {
|
||||
fetchPolicy: 'network-only',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
currentUser: state.auth.user,
|
||||
activeTab: state.profile.activeTab,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ showSignInDialog, setActiveTab }, dispatch);
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
withProfileQuery
|
||||
)(ProfileContainer);
|
||||
@@ -1,178 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { compose, gql } from 'react-apollo';
|
||||
import React, { Component } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { withQuery } from 'coral-framework/hocs';
|
||||
import Slot from 'coral-framework/components/Slot';
|
||||
import cn from 'classnames';
|
||||
import { link } from 'coral-framework/services/pym';
|
||||
import NotLoggedIn from '../components/NotLoggedIn';
|
||||
import { Spinner } from 'coral-ui';
|
||||
import CommentHistory from '../components/CommentHistory';
|
||||
|
||||
import { showSignInDialog } from 'coral-embed-stream/src/actions/login';
|
||||
import { appendNewNodes } from 'plugin-api/beta/client/utils';
|
||||
import update from 'immutability-helper';
|
||||
import { getSlotFragmentSpreads } from 'coral-framework/utils';
|
||||
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
class ProfileContainer extends Component {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!this.props.currentUser && nextProps.currentUser) {
|
||||
// Refetch after login.
|
||||
this.props.data.refetch();
|
||||
}
|
||||
}
|
||||
|
||||
loadMore = () => {
|
||||
return this.props.data.fetchMore({
|
||||
query: LOAD_MORE_QUERY,
|
||||
variables: {
|
||||
limit: 5,
|
||||
cursor: this.props.root.me.comments.endCursor,
|
||||
},
|
||||
updateQuery: (previous, { fetchMoreResult: { me: { comments } } }) => {
|
||||
const updated = update(previous, {
|
||||
me: {
|
||||
comments: {
|
||||
nodes: {
|
||||
$apply: nodes => appendNewNodes(nodes, comments.nodes),
|
||||
},
|
||||
hasNextPage: { $set: comments.hasNextPage },
|
||||
endCursor: { $set: comments.endCursor },
|
||||
},
|
||||
},
|
||||
});
|
||||
return updated;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { currentUser, showSignInDialog, root, data } = this.props;
|
||||
const { me } = this.props.root;
|
||||
const loading = this.props.data.loading;
|
||||
|
||||
if (this.props.data.error) {
|
||||
return <div>{this.props.data.error.message}</div>;
|
||||
}
|
||||
|
||||
if (!currentUser) {
|
||||
return <NotLoggedIn showSignInDialog={showSignInDialog} />;
|
||||
}
|
||||
|
||||
if (loading || !me) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
const localProfile = currentUser.profiles.find(p => p.provider === 'local');
|
||||
const emailAddress = localProfile && localProfile.id;
|
||||
|
||||
return (
|
||||
<div className="talk-my-profile talk-embed-stream-profile-container">
|
||||
<h2>{me.username}</h2>
|
||||
{emailAddress ? <p>{emailAddress}</p> : null}
|
||||
<Slot fill="profileSections" data={data} queryData={{ root }} />
|
||||
<hr />
|
||||
<h3>{t('framework.my_comments')}</h3>
|
||||
<div
|
||||
className={cn('talk-my-profile-comment-history', {
|
||||
'talk-my-profile-comment-history-no-comments': !me.comments.nodes
|
||||
.length,
|
||||
})}
|
||||
>
|
||||
{me.comments.nodes.length ? (
|
||||
<CommentHistory
|
||||
data={data}
|
||||
root={root}
|
||||
comments={me.comments}
|
||||
link={link}
|
||||
loadMore={this.loadMore}
|
||||
/>
|
||||
) : (
|
||||
<p className="talk-my-profile-comment-history-no-comments-cta">
|
||||
{t('user_no_comment')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const slots = [
|
||||
'profileSections',
|
||||
|
||||
// TODO: These Slots should be included in `talk-plugin-history` instead.
|
||||
'commentContent',
|
||||
'historyCommentTimestamp',
|
||||
];
|
||||
|
||||
const CommentFragment = gql`
|
||||
fragment TalkSettings_CommentConnectionFragment on CommentConnection {
|
||||
nodes {
|
||||
id
|
||||
body
|
||||
replyCount
|
||||
action_summaries {
|
||||
count
|
||||
__typename
|
||||
}
|
||||
asset {
|
||||
id
|
||||
title
|
||||
url
|
||||
${getSlotFragmentSpreads(slots, 'asset')}
|
||||
}
|
||||
created_at
|
||||
${getSlotFragmentSpreads(slots, 'comment')}
|
||||
}
|
||||
endCursor
|
||||
hasNextPage
|
||||
}
|
||||
`;
|
||||
|
||||
const LOAD_MORE_QUERY = gql`
|
||||
query TalkSettings_LoadMoreComments($limit: Int, $cursor: Cursor) {
|
||||
me {
|
||||
comments(query: { limit: $limit, cursor: $cursor }) {
|
||||
...TalkSettings_CommentConnectionFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${CommentFragment}
|
||||
`;
|
||||
|
||||
const withProfileQuery = withQuery(
|
||||
gql`
|
||||
query CoralEmbedStream_Profile {
|
||||
me {
|
||||
id
|
||||
username
|
||||
comments(query: {limit: 10}) {
|
||||
...TalkSettings_CommentConnectionFragment
|
||||
}
|
||||
}
|
||||
${getSlotFragmentSpreads(slots, 'root')}
|
||||
}
|
||||
${CommentFragment}
|
||||
`,
|
||||
{
|
||||
options: {
|
||||
fetchPolicy: 'network-only',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
currentUser: state.auth.user,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ showSignInDialog }, dispatch);
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
withProfileQuery
|
||||
)(ProfileContainer);
|
||||
@@ -1,9 +1,5 @@
|
||||
import Pym from 'pym.js';
|
||||
|
||||
const pym = new Pym.Child({ polling: 100 });
|
||||
export default pym;
|
||||
|
||||
export const link = url => e => {
|
||||
e.preventDefault();
|
||||
pym.sendMessage('navigate', url);
|
||||
};
|
||||
export default pym;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"talk-plugin-sort-most-respected",
|
||||
"talk-plugin-sort-newest",
|
||||
"talk-plugin-sort-oldest",
|
||||
"talk-plugin-viewing-options"
|
||||
"talk-plugin-viewing-options",
|
||||
"talk-plugin-profile-settings"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export default {
|
||||
slots: {
|
||||
authorMenuActions: [IgnoreUserAction],
|
||||
ignoreUserConfirmation: [IgnoreUserConfirmation],
|
||||
profileSections: [IgnoredUserSection],
|
||||
profileSettings: [IgnoredUserSection],
|
||||
},
|
||||
translations,
|
||||
mutations: {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "@coralproject/eslint-config-talk"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "@coralproject/eslint-config-talk/client"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import { t } from 'plugin-api/beta/client/services';
|
||||
|
||||
const Tab = () => {
|
||||
return <span>{t('talk-plugin-profile-settings.tab')}</span>;
|
||||
};
|
||||
|
||||
export default Tab;
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Slot } from 'plugin-api/beta/client/components';
|
||||
|
||||
class TabPane extends React.Component {
|
||||
render() {
|
||||
const { data, root } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Slot fill="profileSettings" data={data} queryData={{ root }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TabPane.propTypes = {
|
||||
data: PropTypes.object,
|
||||
root: PropTypes.object,
|
||||
};
|
||||
|
||||
export default TabPane;
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { compose, gql } from 'react-apollo';
|
||||
import TabPane from '../components/TabPane';
|
||||
import { withFragments } from 'plugin-api/beta/client/hocs';
|
||||
import { getSlotFragmentSpreads } from 'plugin-api/beta/client/utils';
|
||||
|
||||
const slots = ['profileSettings'];
|
||||
|
||||
class TabPaneContainer extends React.Component {
|
||||
render() {
|
||||
return <TabPane {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhance = compose(
|
||||
withFragments({
|
||||
root: gql`
|
||||
fragment TalkProfileSettings_TabPane_root on RootQuery {
|
||||
__typename
|
||||
${getSlotFragmentSpreads(slots, 'root')}
|
||||
}
|
||||
`,
|
||||
})
|
||||
);
|
||||
|
||||
export default enhance(TabPaneContainer);
|
||||
@@ -0,0 +1,11 @@
|
||||
import Tab from './components/Tab';
|
||||
import TabPane from './containers/TabPane';
|
||||
import translations from './translations.yml';
|
||||
|
||||
export default {
|
||||
slots: {
|
||||
profileTabs: [Tab],
|
||||
profileTabPanes: [TabPane],
|
||||
},
|
||||
translations,
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
en:
|
||||
talk-plugin-profile-settings:
|
||||
tab: Settings
|
||||
de:
|
||||
talk-plugin-profile-settings:
|
||||
tab: Einstellungen
|
||||
es:
|
||||
talk-plugin-profile-settings:
|
||||
tab: Configuración
|
||||
fr:
|
||||
talk-plugin-profile-settings:
|
||||
tab: Paramètres
|
||||
nl_NL:
|
||||
talk-plugin-profile-settings:
|
||||
tab: Instellingen
|
||||
da:
|
||||
talk-plugin-profile-settings:
|
||||
tab: Indstillinger
|
||||
pt_PR:
|
||||
talk-plugin-profile-settings:
|
||||
tab: Configurações
|
||||
zh_TW:
|
||||
talk-plugin-profile-settings:
|
||||
tab: 設置
|
||||
zh_CN:
|
||||
talk-plugin-profile-settings:
|
||||
tab: 设置
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = {};
|
||||
Reference in New Issue
Block a user